From a3996de73d087e491c74ec9d6e40f4b1424c14ce Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 16 Dec 2013 16:56:35 +0100 Subject: [PATCH 001/134] Updated Renci version to last 2013.4.7 --- Renci.SshNet/AuthenticationMethod.cs | 54 + Renci.SshNet/AuthenticationResult.cs | 26 + Renci.SshNet/BaseClient.cs | 42 +- Renci.SshNet/Channels/Channel.cs | 175 +- .../Channels/ChannelDirectTcpip.NET40.cs | 3 +- Renci.SshNet/Channels/ChannelDirectTcpip.cs | 155 +- .../Channels/ChannelForwardedTcpip.NET40.cs | 4 +- .../Channels/ChannelForwardedTcpip.cs | 18 +- Renci.SshNet/Channels/ChannelSession.cs | 65 +- Renci.SshNet/CipherInfo.cs | 4 +- Renci.SshNet/Common/ASCIIEncoding.cs | 189 ++ Renci.SshNet/Common/AsyncResult.cs | 5 +- .../AuthenticationPasswordChangeEventArgs.cs | 2 +- Renci.SshNet/Common/BigInteger.cs | 20 +- Renci.SshNet/Common/DerData.cs | 2 +- Renci.SshNet/Common/Extensions.NET.cs | 48 + Renci.SshNet/Common/Extensions.NET40.cs | 25 - Renci.SshNet/Common/Extensions.cs | 52 +- Renci.SshNet/Common/HostKeyEventArgs.cs | 66 + .../Common/NetConfServerException.NET40.cs | 28 + Renci.SshNet/Common/NetConfServerException.cs | 41 + Renci.SshNet/Common/PipeStream.cs | 8 +- Renci.SshNet/Common/ProxyException.NET40.cs | 26 + Renci.SshNet/Common/ProxyException.cs | 42 + Renci.SshNet/Common/ScpDownloadEventArgs.cs | 41 + Renci.SshNet/Common/ScpException.NET40.cs | 25 + Renci.SshNet/Common/ScpException.cs | 41 + Renci.SshNet/Common/ScpUploadEventArgs.cs | 41 + Renci.SshNet/Common/ShellDataEventArgs.cs | 41 + Renci.SshNet/Common/SshData.cs | 77 +- Renci.SshNet/Common/TerminalModes.cs | 293 ++ Renci.SshNet/Compression/Adler32.cs | 88 + Renci.SshNet/Compression/CompressionMode.cs | 18 + Renci.SshNet/Compression/Compressor.cs | 119 +- Renci.SshNet/Compression/Deflate.cs | 1640 +++++++++++ Renci.SshNet/Compression/InfBlocks.cs | 618 +++++ Renci.SshNet/Compression/InfCodes.cs | 611 +++++ Renci.SshNet/Compression/InfTree.cs | 523 ++++ Renci.SshNet/Compression/Inflate.cs | 387 +++ Renci.SshNet/Compression/InflateBlocks.cs | 721 +++++ Renci.SshNet/Compression/InflateCodes.cs | 690 +++++ Renci.SshNet/Compression/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/JZlib.cs | 73 + Renci.SshNet/Compression/StaticTree.cs | 152 ++ Renci.SshNet/Compression/Tree.cs | 367 +++ Renci.SshNet/Compression/Version.5/Adler32.cs | 95 + .../Compression/Version.5/CompressionMode.cs | 18 + .../Compression/Version.5/Compressor.cs | 151 ++ Renci.SshNet/Compression/Version.5/Deflate.cs | 2400 ++++++++++++++++ Renci.SshNet/Compression/Version.5/Inflate.cs | 518 ++++ .../Compression/Version.5/InflateBlocks.cs | 721 +++++ .../Compression/Version.5/InflateCodes.cs | 690 +++++ .../Compression/Version.5/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/Version.5/JZlib.cs | 109 + .../Compression/Version.5/StaticTree.cs | 98 + Renci.SshNet/Compression/Version.5/Tree.cs | 334 +++ .../Compression/Version.5/ZInputStream.cs | 205 ++ .../Compression/Version.5/ZOutputStream.cs | 300 ++ Renci.SshNet/Compression/Version.5/ZStream.cs | 76 + Renci.SshNet/Compression/Version.5/Zlib.cs | 30 + .../Compression/Version.5/ZlibOpenSsh.cs | 37 + .../Compression/Version.5/ZlibStream.cs | 51 + Renci.SshNet/Compression/Version.6/Adler32.cs | 95 + .../Compression/Version.6/CompressionMode.cs | 18 + .../Compression/Version.6/Compressor.cs | 151 ++ Renci.SshNet/Compression/Version.6/Deflate.cs | 2401 +++++++++++++++++ Renci.SshNet/Compression/Version.6/Inflate.cs | 517 ++++ .../Compression/Version.6/InflateBlocks.cs | 721 +++++ .../Compression/Version.6/InflateCodes.cs | 690 +++++ .../Compression/Version.6/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/Version.6/JZlib.cs | 109 + .../Compression/Version.6/StaticTree.cs | 98 + Renci.SshNet/Compression/Version.6/Tree.cs | 334 +++ .../Compression/Version.6/ZInputStream.cs | 205 ++ .../Compression/Version.6/ZOutputStream.cs | 300 ++ Renci.SshNet/Compression/Version.6/ZStream.cs | 73 + Renci.SshNet/Compression/Version.6/Zlib.cs | 30 + .../Compression/Version.6/ZlibOpenSsh.cs | 37 + .../Compression/Version.6/ZlibStream.cs | 51 + Renci.SshNet/Compression/Version.7/Adler32.cs | 95 + .../Compression/Version.7/CompressionMode.cs | 18 + .../Compression/Version.7/Compressor.cs | 151 ++ Renci.SshNet/Compression/Version.7/Deflate.cs | 2394 ++++++++++++++++ Renci.SshNet/Compression/Version.7/Inflate.cs | 517 ++++ .../Compression/Version.7/InflateBlocks.cs | 721 +++++ .../Compression/Version.7/InflateCodes.cs | 690 +++++ .../Compression/Version.7/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/Version.7/JZlib.cs | 109 + .../Compression/Version.7/StaticTree.cs | 98 + Renci.SshNet/Compression/Version.7/Tree.cs | 334 +++ .../Compression/Version.7/ZInputStream.cs | 205 ++ .../Compression/Version.7/ZOutputStream.cs | 287 ++ Renci.SshNet/Compression/Version.7/ZStream.cs | 73 + Renci.SshNet/Compression/Version.7/Zlib.cs | 30 + .../Compression/Version.7/ZlibOpenSsh.cs | 37 + .../Compression/Version.7/ZlibStream.cs | 51 + Renci.SshNet/Compression/Version.8/Adler32.cs | 95 + .../Compression/Version.8/CompressionMode.cs | 18 + .../Compression/Version.8/Compressor.cs | 151 ++ Renci.SshNet/Compression/Version.8/Deflate.cs | 2217 +++++++++++++++ Renci.SshNet/Compression/Version.8/Inflate.cs | 516 ++++ .../Compression/Version.8/InflateBlocks.cs | 721 +++++ .../Compression/Version.8/InflateCodes.cs | 690 +++++ .../Compression/Version.8/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/Version.8/JZlib.cs | 109 + .../Compression/Version.8/StaticTree.cs | 98 + Renci.SshNet/Compression/Version.8/Tree.cs | 334 +++ .../Compression/Version.8/ZInputStream.cs | 202 ++ .../Compression/Version.8/ZOutputStream.cs | 256 ++ Renci.SshNet/Compression/Version.8/ZStream.cs | 94 + Renci.SshNet/Compression/Version.8/Zlib.cs | 30 + .../Compression/Version.8/ZlibOpenSsh.cs | 37 + .../Compression/Version.8/ZlibStream.cs | 51 + .../Compression/Working.1/CompressionMode.cs | 18 + .../Compression/Working.1/Compressor.cs | 151 ++ Renci.SshNet/Compression/Working.1/Deflate.cs | 2185 +++++++++++++++ .../Compression/Working.1/InfBlocks.cs | 1375 ++++++++++ .../Compression/Working.1/InfCodes.cs | 696 +++++ Renci.SshNet/Compression/Working.1/InfTree.cs | 603 +++++ Renci.SshNet/Compression/Working.1/Inflate.cs | 476 ++++ Renci.SshNet/Compression/Working.1/JZlib.cs | 111 + .../Compression/Working.1/StaticTree.cs | 98 + Renci.SshNet/Compression/Working.1/Tree.cs | 333 +++ .../Compression/Working.1/ZInputStream.cs | 192 ++ .../Compression/Working.1/ZOutputStream.cs | 201 ++ Renci.SshNet/Compression/Working.1/ZStream.cs | 274 ++ Renci.SshNet/Compression/Working.1/Zlib.cs | 30 + .../Compression/Working.1/ZlibOpenSsh.cs | 37 + .../Compression/Working.1/ZlibStream.cs | 50 + .../Compression/ZDeflaterOutputStream.cs | 151 ++ .../Compression/ZInflaterInputStream.cs | 127 + Renci.SshNet/Compression/ZInputStream.cs | 193 ++ Renci.SshNet/Compression/ZOutputStream.cs | 217 ++ Renci.SshNet/Compression/ZStream.cs | 214 ++ Renci.SshNet/Compression/Zlib.cs | 46 +- Renci.SshNet/Compression/ZlibOpenSsh.cs | 79 +- Renci.SshNet/Compression/ZlibStream.cs | 49 + Renci.SshNet/Compression/v10/Adler32.cs | 95 + .../Compression/v10/CompressionMode.cs | 18 + Renci.SshNet/Compression/v10/Compressor.cs | 151 ++ Renci.SshNet/Compression/v10/Deflate.cs | 2290 ++++++++++++++++ Renci.SshNet/Compression/v10/Inflate.cs | 524 ++++ Renci.SshNet/Compression/v10/InflateBlocks.cs | 721 +++++ Renci.SshNet/Compression/v10/InflateCodes.cs | 690 +++++ Renci.SshNet/Compression/v10/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/v10/JZlib.cs | 109 + Renci.SshNet/Compression/v10/StaticTree.cs | 98 + Renci.SshNet/Compression/v10/Tree.cs | 334 +++ Renci.SshNet/Compression/v10/ZInputStream.cs | 200 ++ Renci.SshNet/Compression/v10/ZOutputStream.cs | 252 ++ Renci.SshNet/Compression/v10/ZStream.cs | 94 + Renci.SshNet/Compression/v10/Zlib.cs | 30 + Renci.SshNet/Compression/v10/ZlibOpenSsh.cs | 37 + Renci.SshNet/Compression/v10/ZlibStream.cs | 51 + Renci.SshNet/Compression/v11/Adler32.cs | 95 + .../Compression/v11/CompressionMode.cs | 18 + Renci.SshNet/Compression/v11/Compressor.cs | 151 ++ Renci.SshNet/Compression/v11/Deflate.cs | 2225 +++++++++++++++ Renci.SshNet/Compression/v11/Inflate.cs | 524 ++++ Renci.SshNet/Compression/v11/InflateBlocks.cs | 721 +++++ Renci.SshNet/Compression/v11/InflateCodes.cs | 690 +++++ Renci.SshNet/Compression/v11/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/v11/JZlib.cs | 109 + Renci.SshNet/Compression/v11/StaticTree.cs | 98 + Renci.SshNet/Compression/v11/Tree.cs | 334 +++ Renci.SshNet/Compression/v11/ZInputStream.cs | 202 ++ Renci.SshNet/Compression/v11/ZOutputStream.cs | 252 ++ Renci.SshNet/Compression/v11/ZStream.cs | 94 + Renci.SshNet/Compression/v11/Zlib.cs | 30 + Renci.SshNet/Compression/v11/ZlibOpenSsh.cs | 37 + Renci.SshNet/Compression/v11/ZlibStream.cs | 51 + Renci.SshNet/Compression/v12/Adler32.cs | 95 + .../Compression/v12/CompressionMode.cs | 18 + Renci.SshNet/Compression/v12/Compressor.cs | 151 ++ Renci.SshNet/Compression/v12/Deflate.cs | 2225 +++++++++++++++ Renci.SshNet/Compression/v12/Inflate.cs | 524 ++++ Renci.SshNet/Compression/v12/InflateBlocks.cs | 721 +++++ Renci.SshNet/Compression/v12/InflateCodes.cs | 690 +++++ Renci.SshNet/Compression/v12/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/v12/JZlib.cs | 109 + Renci.SshNet/Compression/v12/StaticTree.cs | 98 + Renci.SshNet/Compression/v12/Tree.cs | 334 +++ Renci.SshNet/Compression/v12/ZInputStream.cs | 202 ++ Renci.SshNet/Compression/v12/ZOutputStream.cs | 252 ++ Renci.SshNet/Compression/v12/ZStream.cs | 102 + Renci.SshNet/Compression/v12/Zlib.cs | 30 + Renci.SshNet/Compression/v12/ZlibOpenSsh.cs | 37 + Renci.SshNet/Compression/v12/ZlibStream.cs | 51 + Renci.SshNet/Compression/v13/Adler32.cs | 95 + .../Compression/v13/CompressionMode.cs | 18 + Renci.SshNet/Compression/v13/Compressor.cs | 151 ++ Renci.SshNet/Compression/v13/Deflate.cs | 2225 +++++++++++++++ Renci.SshNet/Compression/v13/Inflate.cs | 524 ++++ Renci.SshNet/Compression/v13/InflateBlocks.cs | 717 +++++ Renci.SshNet/Compression/v13/InflateCodes.cs | 685 +++++ Renci.SshNet/Compression/v13/InflateTree.cs | 554 ++++ Renci.SshNet/Compression/v13/JZlib.cs | 109 + Renci.SshNet/Compression/v13/StaticTree.cs | 98 + Renci.SshNet/Compression/v13/Tree.cs | 334 +++ Renci.SshNet/Compression/v13/ZInputStream.cs | 202 ++ Renci.SshNet/Compression/v13/ZOutputStream.cs | 252 ++ Renci.SshNet/Compression/v13/ZStream.cs | 100 + Renci.SshNet/Compression/v13/Zlib.cs | 30 + Renci.SshNet/Compression/v13/ZlibOpenSsh.cs | 37 + Renci.SshNet/Compression/v13/ZlibStream.cs | 51 + Renci.SshNet/Compression/v9/Adler32.cs | 95 + .../Compression/v9/CompressionMode.cs | 18 + Renci.SshNet/Compression/v9/Compressor.cs | 151 ++ Renci.SshNet/Compression/v9/Deflate.cs | 2226 +++++++++++++++ Renci.SshNet/Compression/v9/Inflate.cs | 524 ++++ Renci.SshNet/Compression/v9/InflateBlocks.cs | 721 +++++ Renci.SshNet/Compression/v9/InflateCodes.cs | 690 +++++ Renci.SshNet/Compression/v9/InflateTree.cs | 555 ++++ Renci.SshNet/Compression/v9/JZlib.cs | 109 + Renci.SshNet/Compression/v9/StaticTree.cs | 98 + Renci.SshNet/Compression/v9/Tree.cs | 334 +++ Renci.SshNet/Compression/v9/ZInputStream.cs | 202 ++ Renci.SshNet/Compression/v9/ZOutputStream.cs | 252 ++ Renci.SshNet/Compression/v9/ZStream.cs | 94 + Renci.SshNet/Compression/v9/Zlib.cs | 30 + Renci.SshNet/Compression/v9/ZlibOpenSsh.cs | 37 + Renci.SshNet/Compression/v9/ZlibStream.cs | 51 + ...{B60A61FD-33DF-43A1-B48C-A8E3DB53FA13}.tmp | 690 +++++ Renci.SshNet/ConnectionInfo.cs | 422 +-- Renci.SshNet/Documentation/Content/About.aml | 55 - .../Guide/Guide.Command.Asynchronous.aml | 41 - .../Content/Guide/Guide.Command.Error.aml | 36 - .../Guide/Guide.Command.Multitasking.aml | 38 - .../Guide/Guide.Command.StatusCode.aml | 31 - .../Content/Guide/Guide.Command.Timeout.aml | 32 - .../Content/Guide/Guide.Command.aml | 88 - .../Guide/Guide.Connection.ChangePassword.aml | 34 - .../Guide/Guide.Connection.Interactive.aml | 41 - .../Guide/Guide.Connection.Password.aml | 42 - .../Guide/Guide.Connection.PrivateKey.aml | 37 - .../Content/Guide/Guide.Connection.Prompt.aml | 35 - .../Guide/Guide.Connection.Timeout.aml | 32 - .../Content/Guide/Guide.Connection.aml | 125 - .../Content/Guide/Guide.PortForward.Local.aml | 30 - .../Guide/Guide.PortForward.Remote.aml | 30 - .../Content/Guide/Guide.PortForward.aml | 17 - .../Content/Guide/Guide.SFtp.aml | 85 - .../Documentation/Content/Guide/Guide.aml | 75 - .../Documentation/Content/HowTo.Connect.aml | 44 - .../Documentation/Content/Reference.aml | 16 - .../Documentation/Renci.SshClient.content | 28 - Renci.SshNet/Documentation/SshClient.shfbproj | 140 - Renci.SshNet/ExpectAction.cs | 60 + Renci.SshNet/ExpectAsyncResult.cs | 23 + Renci.SshNet/ForwardedPort.cs | 33 +- Renci.SshNet/ForwardedPortDynamic.NET.cs | 282 ++ Renci.SshNet/ForwardedPortDynamic.NET40.cs | 14 + Renci.SshNet/ForwardedPortDynamic.cs | 126 + Renci.SshNet/ForwardedPortLocal.NET.cs | 119 + Renci.SshNet/ForwardedPortLocal.NET40.cs | 84 +- Renci.SshNet/ForwardedPortLocal.cs | 70 + Renci.SshNet/ForwardedPortRemote.NET.cs | 37 + Renci.SshNet/ForwardedPortRemote.NET40.cs | 3 +- Renci.SshNet/ForwardedPortRemote.cs | 74 +- Renci.SshNet/HashInfo.cs | 39 + ...rdInteractiveAuthenticationMethod.NET40.cs | 21 + ...KeyboardInteractiveAuthenticationMethod.cs | 198 ++ .../KeyboardInteractiveConnectionInfo.cs | 178 +- Renci.SshNet/MessageEventArgs.cs | 4 + .../Messages/Authentication/BannerMessage.cs | 2 +- .../Authentication/PublicKeyMessage.cs | 4 +- .../Messages/Authentication/RequestMessage.cs | 8 +- .../Authentication/RequestMessageHost.cs | 4 +- .../RequestMessageKeyboardInteractive.cs | 2 +- .../Authentication/RequestMessagePassword.cs | 22 +- .../Authentication/RequestMessagePublicKey.cs | 2 +- .../Connection/ChannelExtendedDataMessage.cs | 4 +- .../ChannelOpen/ChannelOpenMessage.cs | 4 +- .../ChannelRequest/BreakRequestInfo.cs | 72 + .../ChannelRequest/ChannelRequestMessage.cs | 4 +- .../ChannelRequest/ExecRequestInfo.cs | 20 +- .../ChannelRequest/ExitSignalRequestInfo.cs | 6 +- .../ChannelRequest/PseudoTerminalInfo.cs | 44 +- .../ChannelRequest/SignalRequestInfo.cs | 4 +- .../ChannelRequest/SubsystemRequestInfo.cs | 4 +- .../X11ForwardingRequestInfo.cs | 4 +- .../Connection/GlobalRequestMessage.cs | 17 +- Renci.SshNet/Messages/Message.cs | 21 +- .../Messages/Transport/DisconnectMessage.cs | 4 +- .../KeyExchangeDhGroupExchangeInit.cs | 2 +- .../KeyExchangeDhGroupExchangeRequest.cs | 2 +- .../Transport/KeyExchangeDhInitMessage.cs | 2 +- .../Transport/KeyExchangeEcdhInitMessage.cs | 50 + .../Transport/KeyExchangeEcdhReplyMessage.cs | 50 + .../Transport/KeyExchangeInitMessage.cs | 2 +- .../Messages/Transport/NewKeysMessage.cs | 2 +- .../Transport/ServiceAcceptMessage.cs | 2 +- .../Transport/ServiceRequestMessage.cs | 4 +- Renci.SshNet/NetConfClient.cs | 221 ++ Renci.SshNet/Netconf/NetConfSession.cs | 212 ++ Renci.SshNet/NoneAuthenticationMethod.cs | 140 + Renci.SshNet/NoneConnectionInfo.cs | 101 +- .../PasswordAuthenticationMethod.NET40.cs | 21 + Renci.SshNet/PasswordAuthenticationMethod.cs | 217 ++ Renci.SshNet/PasswordConnectionInfo.cs | 285 +- .../PrivateKeyAuthenticationMethod.cs | 232 ++ Renci.SshNet/PrivateKeyConnectionInfo.cs | 219 +- Renci.SshNet/PrivateKeyFile.cs | 281 +- Renci.SshNet/Properties/AssemblyInfo.cs | 13 +- Renci.SshNet/ProxyTypes.cs | 22 + Renci.SshNet/Renci.SshNet.csproj | 193 +- Renci.SshNet/ScpClient.NET.cs | 312 +++ Renci.SshNet/ScpClient.cs | 421 +++ .../Security/CertificateHostAlgorithm.cs | 6 +- .../Security/Cryptography/AsymmetricCipher.cs | 4 + .../Security/Cryptography/BlockCipher.cs | 20 +- Renci.SshNet/Security/Cryptography/Cipher.cs | 28 +- .../Cryptography/CipherDigitalSignature.cs | 10 +- .../Cryptography/Ciphers/AesCipher.cs | 147 +- .../Cryptography/Ciphers/Arc4Cipher.cs | 129 +- .../Cryptography/Ciphers/BlowfishCipher.cs | 145 +- .../Cryptography/Ciphers/CastCipher.cs | 89 +- .../Cryptography/Ciphers/CipherPadding.cs | 2 +- .../Cryptography/Ciphers/DesCipher.cs | 12 +- .../Ciphers/Paddings/PKCS5Padding.cs | 35 + .../Ciphers/Paddings/PKCS7Padding.cs | 6 +- .../Cryptography/Ciphers/RsaCipher.cs | 22 +- .../Cryptography/Ciphers/SerpentCipher.cs | 718 ++--- .../Cryptography/Ciphers/TripleDesCipher.cs | 6 +- .../Cryptography/Ciphers/TwofishCipher.cs | 949 +++---- .../Security/Cryptography/DigitalSignature.cs | 4 +- .../Cryptography/DsaDigitalSignature.cs | 34 +- Renci.SshNet/Security/Cryptography/DsaKey.cs | 36 +- Renci.SshNet/Security/Cryptography/HMAC.cs | 97 +- .../Security/Cryptography/Hashes/MD5Hash.cs | 22 +- .../Cryptography/Hashes/RIPEMD160Hash.cs | 522 ++++ .../Security/Cryptography/Hashes/SHA1Hash.cs | 341 ++- .../Cryptography/Hashes/SHA256Hash.cs | 23 +- .../Cryptography/Hashes/SHA2HashBase.cs | 366 +++ .../Cryptography/Hashes/SHA384Hash.cs | 85 + .../Cryptography/Hashes/SHA512Hash.cs | 87 + Renci.SshNet/Security/Cryptography/Key.cs | 14 +- .../Cryptography/RsaDigitalSignature.cs | 6 +- Renci.SshNet/Security/Cryptography/RsaKey.cs | 42 + .../Security/Cryptography/StreamCipher.cs | 2 +- Renci.SshNet/Security/HostAlgorithm.cs | 4 +- Renci.SshNet/Security/KeyExchange.cs | 79 +- .../Security/KeyExchangeDiffieHellman.cs | 21 +- .../KeyExchangeDiffieHellmanGroup14Sha1.cs | 115 +- .../KeyExchangeDiffieHellmanGroup1Sha1.cs | 116 +- ...yExchangeDiffieHellmanGroupExchangeSha1.cs | 4 +- ...xchangeDiffieHellmanGroupExchangeSha256.cs | 6 +- .../KeyExchangeDiffieHellmanGroupSha1.cs | 132 + .../KeyExchangeEllipticCurveDiffieHellman.cs | 284 ++ Renci.SshNet/Security/KeyHostAlgorithm.cs | 26 +- Renci.SshNet/Session.NET.cs | 103 +- Renci.SshNet/Session.NET40.cs | 2 +- Renci.SshNet/Session.cs | 550 +++- Renci.SshNet/Sftp/Flags.cs | 5 +- .../Sftp/OpenSshFilesytemInformation.cs | 97 - .../ExtendedRequests/FStatVfsRequest.cs | 33 + .../ExtendedRequests/HardLinkRequest.cs | 36 + .../ExtendedRequests/PosixRenameRequest.cs | 40 + .../ExtendedRequests/StatVfsRequest.cs | 37 + ...RenameRequest.cs => PosixRenameRequest.cs} | 15 +- .../Sftp/Requests/SftpBlockRequest.cs | 51 + .../Sftp/Requests/SftpCloseRequest.cs | 4 +- .../Sftp/Requests/SftpExtendedRequest.cs | 28 + .../Sftp/Requests/SftpFSetStatRequest.cs | 4 +- .../Sftp/Requests/SftpFStatRequest.cs | 4 +- .../Sftp/Requests/SftpLStatRequest.cs | 11 +- Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs | 55 + .../Sftp/Requests/SftpMkDirRequest.cs | 17 +- .../Sftp/Requests/SftpOpenDirRequest.cs | 11 +- Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs | 14 +- .../Sftp/Requests/SftpReadDirRequest.cs | 4 +- .../Sftp/Requests/SftpReadLinkRequest.cs | 11 +- Renci.SshNet/Sftp/Requests/SftpReadRequest.cs | 4 +- .../Sftp/Requests/SftpRealPathRequest.cs | 9 +- .../Sftp/Requests/SftpRemoveRequest.cs | 11 +- .../Sftp/Requests/SftpRenameRequest.cs | 15 +- Renci.SshNet/Sftp/Requests/SftpRequest.cs | 5 +- .../Sftp/Requests/SftpRmDirRequest.cs | 11 +- .../Sftp/Requests/SftpSetStatRequest.cs | 11 +- Renci.SshNet/Sftp/Requests/SftpStatRequest.cs | 11 +- .../Sftp/Requests/SftpSymLinkRequest.cs | 15 +- .../Sftp/Requests/SftpUnblockRequest.cs | 46 + .../Sftp/Requests/SftpWriteRequest.cs | 4 +- ...SshStatVfsRequest.cs => StatVfsRequest.cs} | 15 +- .../ExtendedReplies/ExtendedReplyInfo.cs | 25 + .../ExtendedReplies/StatVfsReplyInfo.cs | 26 + .../Sftp/Responses/OpenSshStatVfsResponse.cs | 26 - .../Sftp/Responses/SftpAttrsResponse.cs | 5 + .../Sftp/Responses/SftpDataResponse.cs | 5 + .../Responses/SftpExtendedReplyResponse.cs | 13 +- .../Sftp/Responses/SftpHandleResponse.cs | 5 + .../Sftp/Responses/SftpNameResponse.cs | 10 +- Renci.SshNet/Sftp/Responses/SftpResponse.cs | 9 +- .../Sftp/Responses/SftpStatusResponse.cs | 15 +- .../Sftp/Responses/StatVfsResponse.cs | 24 + Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs | 11 + Renci.SshNet/Sftp/SftpFile.cs | 51 + Renci.SshNet/Sftp/SftpFileAttributes.cs | 74 +- Renci.SshNet/Sftp/SftpFileStream.cs | 78 +- .../Sftp/SftpFileSystemInformation.cs | 136 + Renci.SshNet/Sftp/SftpMessage.cs | 82 +- Renci.SshNet/Sftp/SftpMessageTypes.cs | 15 +- Renci.SshNet/Sftp/SftpSession.cs | 763 +++--- .../SftpSynchronizeDirectoriesAsyncResult.cs | 40 + Renci.SshNet/Sftp/SftpUploadAsyncResult.cs | 11 + Renci.SshNet/Sftp/StatusCodes.cs | 94 +- Renci.SshNet/SftpClient.NET.cs | 177 ++ Renci.SshNet/SftpClient.NET40.cs | 8 +- Renci.SshNet/SftpClient.cs | 661 +++-- Renci.SshNet/Shell.NET40.cs | 4 +- Renci.SshNet/Shell.cs | 28 +- Renci.SshNet/ShellStream.NET40.cs | 18 + Renci.SshNet/ShellStream.cs | 759 ++++++ Renci.SshNet/SshClient.cs | 267 +- Renci.SshNet/SshCommand.NET40.cs | 4 +- Renci.SshNet/SshCommand.cs | 165 +- Renci.SshNet/SubsystemSession.cs | 135 +- Sshfs/Sshfs.sln | 17 +- Sshfs/Sshfs/SftpContextStream.cs | 6 +- Sshfs/Sshfs/SftpFilesystem.cs | 15 +- 420 files changed, 84284 insertions(+), 4887 deletions(-) create mode 100644 Renci.SshNet/AuthenticationMethod.cs create mode 100644 Renci.SshNet/AuthenticationResult.cs create mode 100644 Renci.SshNet/Common/ASCIIEncoding.cs create mode 100644 Renci.SshNet/Common/Extensions.NET.cs delete mode 100644 Renci.SshNet/Common/Extensions.NET40.cs create mode 100644 Renci.SshNet/Common/HostKeyEventArgs.cs create mode 100644 Renci.SshNet/Common/NetConfServerException.NET40.cs create mode 100644 Renci.SshNet/Common/NetConfServerException.cs create mode 100644 Renci.SshNet/Common/ProxyException.NET40.cs create mode 100644 Renci.SshNet/Common/ProxyException.cs create mode 100644 Renci.SshNet/Common/ScpDownloadEventArgs.cs create mode 100644 Renci.SshNet/Common/ScpException.NET40.cs create mode 100644 Renci.SshNet/Common/ScpException.cs create mode 100644 Renci.SshNet/Common/ScpUploadEventArgs.cs create mode 100644 Renci.SshNet/Common/ShellDataEventArgs.cs create mode 100644 Renci.SshNet/Common/TerminalModes.cs create mode 100644 Renci.SshNet/Compression/Adler32.cs create mode 100644 Renci.SshNet/Compression/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/Deflate.cs create mode 100644 Renci.SshNet/Compression/InfBlocks.cs create mode 100644 Renci.SshNet/Compression/InfCodes.cs create mode 100644 Renci.SshNet/Compression/InfTree.cs create mode 100644 Renci.SshNet/Compression/Inflate.cs create mode 100644 Renci.SshNet/Compression/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/InflateTree.cs create mode 100644 Renci.SshNet/Compression/JZlib.cs create mode 100644 Renci.SshNet/Compression/StaticTree.cs create mode 100644 Renci.SshNet/Compression/Tree.cs create mode 100644 Renci.SshNet/Compression/Version.5/Adler32.cs create mode 100644 Renci.SshNet/Compression/Version.5/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/Version.5/Compressor.cs create mode 100644 Renci.SshNet/Compression/Version.5/Deflate.cs create mode 100644 Renci.SshNet/Compression/Version.5/Inflate.cs create mode 100644 Renci.SshNet/Compression/Version.5/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/Version.5/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/Version.5/InflateTree.cs create mode 100644 Renci.SshNet/Compression/Version.5/JZlib.cs create mode 100644 Renci.SshNet/Compression/Version.5/StaticTree.cs create mode 100644 Renci.SshNet/Compression/Version.5/Tree.cs create mode 100644 Renci.SshNet/Compression/Version.5/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/Version.5/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/Version.5/ZStream.cs create mode 100644 Renci.SshNet/Compression/Version.5/Zlib.cs create mode 100644 Renci.SshNet/Compression/Version.5/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/Version.5/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/Version.6/Adler32.cs create mode 100644 Renci.SshNet/Compression/Version.6/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/Version.6/Compressor.cs create mode 100644 Renci.SshNet/Compression/Version.6/Deflate.cs create mode 100644 Renci.SshNet/Compression/Version.6/Inflate.cs create mode 100644 Renci.SshNet/Compression/Version.6/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/Version.6/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/Version.6/InflateTree.cs create mode 100644 Renci.SshNet/Compression/Version.6/JZlib.cs create mode 100644 Renci.SshNet/Compression/Version.6/StaticTree.cs create mode 100644 Renci.SshNet/Compression/Version.6/Tree.cs create mode 100644 Renci.SshNet/Compression/Version.6/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/Version.6/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/Version.6/ZStream.cs create mode 100644 Renci.SshNet/Compression/Version.6/Zlib.cs create mode 100644 Renci.SshNet/Compression/Version.6/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/Version.6/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/Version.7/Adler32.cs create mode 100644 Renci.SshNet/Compression/Version.7/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/Version.7/Compressor.cs create mode 100644 Renci.SshNet/Compression/Version.7/Deflate.cs create mode 100644 Renci.SshNet/Compression/Version.7/Inflate.cs create mode 100644 Renci.SshNet/Compression/Version.7/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/Version.7/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/Version.7/InflateTree.cs create mode 100644 Renci.SshNet/Compression/Version.7/JZlib.cs create mode 100644 Renci.SshNet/Compression/Version.7/StaticTree.cs create mode 100644 Renci.SshNet/Compression/Version.7/Tree.cs create mode 100644 Renci.SshNet/Compression/Version.7/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/Version.7/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/Version.7/ZStream.cs create mode 100644 Renci.SshNet/Compression/Version.7/Zlib.cs create mode 100644 Renci.SshNet/Compression/Version.7/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/Version.7/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/Version.8/Adler32.cs create mode 100644 Renci.SshNet/Compression/Version.8/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/Version.8/Compressor.cs create mode 100644 Renci.SshNet/Compression/Version.8/Deflate.cs create mode 100644 Renci.SshNet/Compression/Version.8/Inflate.cs create mode 100644 Renci.SshNet/Compression/Version.8/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/Version.8/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/Version.8/InflateTree.cs create mode 100644 Renci.SshNet/Compression/Version.8/JZlib.cs create mode 100644 Renci.SshNet/Compression/Version.8/StaticTree.cs create mode 100644 Renci.SshNet/Compression/Version.8/Tree.cs create mode 100644 Renci.SshNet/Compression/Version.8/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/Version.8/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/Version.8/ZStream.cs create mode 100644 Renci.SshNet/Compression/Version.8/Zlib.cs create mode 100644 Renci.SshNet/Compression/Version.8/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/Version.8/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/Working.1/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/Working.1/Compressor.cs create mode 100644 Renci.SshNet/Compression/Working.1/Deflate.cs create mode 100644 Renci.SshNet/Compression/Working.1/InfBlocks.cs create mode 100644 Renci.SshNet/Compression/Working.1/InfCodes.cs create mode 100644 Renci.SshNet/Compression/Working.1/InfTree.cs create mode 100644 Renci.SshNet/Compression/Working.1/Inflate.cs create mode 100644 Renci.SshNet/Compression/Working.1/JZlib.cs create mode 100644 Renci.SshNet/Compression/Working.1/StaticTree.cs create mode 100644 Renci.SshNet/Compression/Working.1/Tree.cs create mode 100644 Renci.SshNet/Compression/Working.1/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/Working.1/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/Working.1/ZStream.cs create mode 100644 Renci.SshNet/Compression/Working.1/Zlib.cs create mode 100644 Renci.SshNet/Compression/Working.1/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/Working.1/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/ZDeflaterOutputStream.cs create mode 100644 Renci.SshNet/Compression/ZInflaterInputStream.cs create mode 100644 Renci.SshNet/Compression/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/ZStream.cs create mode 100644 Renci.SshNet/Compression/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/v10/Adler32.cs create mode 100644 Renci.SshNet/Compression/v10/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/v10/Compressor.cs create mode 100644 Renci.SshNet/Compression/v10/Deflate.cs create mode 100644 Renci.SshNet/Compression/v10/Inflate.cs create mode 100644 Renci.SshNet/Compression/v10/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/v10/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/v10/InflateTree.cs create mode 100644 Renci.SshNet/Compression/v10/JZlib.cs create mode 100644 Renci.SshNet/Compression/v10/StaticTree.cs create mode 100644 Renci.SshNet/Compression/v10/Tree.cs create mode 100644 Renci.SshNet/Compression/v10/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/v10/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/v10/ZStream.cs create mode 100644 Renci.SshNet/Compression/v10/Zlib.cs create mode 100644 Renci.SshNet/Compression/v10/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/v10/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/v11/Adler32.cs create mode 100644 Renci.SshNet/Compression/v11/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/v11/Compressor.cs create mode 100644 Renci.SshNet/Compression/v11/Deflate.cs create mode 100644 Renci.SshNet/Compression/v11/Inflate.cs create mode 100644 Renci.SshNet/Compression/v11/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/v11/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/v11/InflateTree.cs create mode 100644 Renci.SshNet/Compression/v11/JZlib.cs create mode 100644 Renci.SshNet/Compression/v11/StaticTree.cs create mode 100644 Renci.SshNet/Compression/v11/Tree.cs create mode 100644 Renci.SshNet/Compression/v11/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/v11/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/v11/ZStream.cs create mode 100644 Renci.SshNet/Compression/v11/Zlib.cs create mode 100644 Renci.SshNet/Compression/v11/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/v11/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/v12/Adler32.cs create mode 100644 Renci.SshNet/Compression/v12/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/v12/Compressor.cs create mode 100644 Renci.SshNet/Compression/v12/Deflate.cs create mode 100644 Renci.SshNet/Compression/v12/Inflate.cs create mode 100644 Renci.SshNet/Compression/v12/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/v12/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/v12/InflateTree.cs create mode 100644 Renci.SshNet/Compression/v12/JZlib.cs create mode 100644 Renci.SshNet/Compression/v12/StaticTree.cs create mode 100644 Renci.SshNet/Compression/v12/Tree.cs create mode 100644 Renci.SshNet/Compression/v12/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/v12/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/v12/ZStream.cs create mode 100644 Renci.SshNet/Compression/v12/Zlib.cs create mode 100644 Renci.SshNet/Compression/v12/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/v12/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/v13/Adler32.cs create mode 100644 Renci.SshNet/Compression/v13/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/v13/Compressor.cs create mode 100644 Renci.SshNet/Compression/v13/Deflate.cs create mode 100644 Renci.SshNet/Compression/v13/Inflate.cs create mode 100644 Renci.SshNet/Compression/v13/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/v13/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/v13/InflateTree.cs create mode 100644 Renci.SshNet/Compression/v13/JZlib.cs create mode 100644 Renci.SshNet/Compression/v13/StaticTree.cs create mode 100644 Renci.SshNet/Compression/v13/Tree.cs create mode 100644 Renci.SshNet/Compression/v13/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/v13/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/v13/ZStream.cs create mode 100644 Renci.SshNet/Compression/v13/Zlib.cs create mode 100644 Renci.SshNet/Compression/v13/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/v13/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/v9/Adler32.cs create mode 100644 Renci.SshNet/Compression/v9/CompressionMode.cs create mode 100644 Renci.SshNet/Compression/v9/Compressor.cs create mode 100644 Renci.SshNet/Compression/v9/Deflate.cs create mode 100644 Renci.SshNet/Compression/v9/Inflate.cs create mode 100644 Renci.SshNet/Compression/v9/InflateBlocks.cs create mode 100644 Renci.SshNet/Compression/v9/InflateCodes.cs create mode 100644 Renci.SshNet/Compression/v9/InflateTree.cs create mode 100644 Renci.SshNet/Compression/v9/JZlib.cs create mode 100644 Renci.SshNet/Compression/v9/StaticTree.cs create mode 100644 Renci.SshNet/Compression/v9/Tree.cs create mode 100644 Renci.SshNet/Compression/v9/ZInputStream.cs create mode 100644 Renci.SshNet/Compression/v9/ZOutputStream.cs create mode 100644 Renci.SshNet/Compression/v9/ZStream.cs create mode 100644 Renci.SshNet/Compression/v9/Zlib.cs create mode 100644 Renci.SshNet/Compression/v9/ZlibOpenSsh.cs create mode 100644 Renci.SshNet/Compression/v9/ZlibStream.cs create mode 100644 Renci.SshNet/Compression/{B60A61FD-33DF-43A1-B48C-A8E3DB53FA13}.tmp delete mode 100644 Renci.SshNet/Documentation/Content/About.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Command.Asynchronous.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Command.Error.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Command.Multitasking.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Command.StatusCode.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Command.Timeout.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Command.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Connection.ChangePassword.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Interactive.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Password.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Connection.PrivateKey.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Prompt.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Timeout.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.Connection.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Local.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Remote.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.SFtp.aml delete mode 100644 Renci.SshNet/Documentation/Content/Guide/Guide.aml delete mode 100644 Renci.SshNet/Documentation/Content/HowTo.Connect.aml delete mode 100644 Renci.SshNet/Documentation/Content/Reference.aml delete mode 100644 Renci.SshNet/Documentation/Renci.SshClient.content delete mode 100644 Renci.SshNet/Documentation/SshClient.shfbproj create mode 100644 Renci.SshNet/ExpectAction.cs create mode 100644 Renci.SshNet/ExpectAsyncResult.cs create mode 100644 Renci.SshNet/ForwardedPortDynamic.NET.cs create mode 100644 Renci.SshNet/ForwardedPortDynamic.NET40.cs create mode 100644 Renci.SshNet/ForwardedPortDynamic.cs create mode 100644 Renci.SshNet/ForwardedPortLocal.NET.cs create mode 100644 Renci.SshNet/ForwardedPortRemote.NET.cs create mode 100644 Renci.SshNet/HashInfo.cs create mode 100644 Renci.SshNet/KeyboardInteractiveAuthenticationMethod.NET40.cs create mode 100644 Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs create mode 100644 Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs create mode 100644 Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs create mode 100644 Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs create mode 100644 Renci.SshNet/NetConfClient.cs create mode 100644 Renci.SshNet/Netconf/NetConfSession.cs create mode 100644 Renci.SshNet/NoneAuthenticationMethod.cs create mode 100644 Renci.SshNet/PasswordAuthenticationMethod.NET40.cs create mode 100644 Renci.SshNet/PasswordAuthenticationMethod.cs create mode 100644 Renci.SshNet/PrivateKeyAuthenticationMethod.cs create mode 100644 Renci.SshNet/ProxyTypes.cs create mode 100644 Renci.SshNet/ScpClient.NET.cs create mode 100644 Renci.SshNet/ScpClient.cs create mode 100644 Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs create mode 100644 Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs create mode 100644 Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs create mode 100644 Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs create mode 100644 Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs create mode 100644 Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs create mode 100644 Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs delete mode 100644 Renci.SshNet/Sftp/OpenSshFilesytemInformation.cs create mode 100644 Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs create mode 100644 Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs create mode 100644 Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs create mode 100644 Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs rename Renci.SshNet/Sftp/Requests/{OpenSshPosixRenameRequest.cs => PosixRenameRequest.cs} (61%) create mode 100644 Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs create mode 100644 Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs create mode 100644 Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs create mode 100644 Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs rename Renci.SshNet/Sftp/Requests/{OpenSshStatVfsRequest.cs => StatVfsRequest.cs} (57%) create mode 100644 Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs create mode 100644 Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs delete mode 100644 Renci.SshNet/Sftp/Responses/OpenSshStatVfsResponse.cs create mode 100644 Renci.SshNet/Sftp/Responses/StatVfsResponse.cs create mode 100644 Renci.SshNet/Sftp/SftpFileSystemInformation.cs create mode 100644 Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs create mode 100644 Renci.SshNet/SftpClient.NET.cs create mode 100644 Renci.SshNet/ShellStream.NET40.cs create mode 100644 Renci.SshNet/ShellStream.cs diff --git a/Renci.SshNet/AuthenticationMethod.cs b/Renci.SshNet/AuthenticationMethod.cs new file mode 100644 index 0000000..3385d98 --- /dev/null +++ b/Renci.SshNet/AuthenticationMethod.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Renci.SshNet.Common; + +namespace Renci.SshNet +{ + /// + /// Base class for all supported authentication methods + /// + public abstract class AuthenticationMethod + { + /// + /// Gets authentication method name + /// + public abstract string Name { get; } + + /// + /// Gets connection username. + /// + public string Username { get; private set; } + + /// + /// Gets the authentication error message. + /// + public string ErrorMessage { get; private set; } + + /// + /// Gets list of allowed authentications. + /// + public IEnumerable AllowedAuthentications { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// The username. + /// is whitespace or null. + protected AuthenticationMethod(string username) + { + if (username.IsNullOrWhiteSpace()) + throw new ArgumentException("username"); + + this.Username = username; + } + + /// + /// Authenticates the specified session. + /// + /// The session to authenticate. + /// Result of authentication process. + public abstract AuthenticationResult Authenticate(Session session); + } +} diff --git a/Renci.SshNet/AuthenticationResult.cs b/Renci.SshNet/AuthenticationResult.cs new file mode 100644 index 0000000..fb6ba37 --- /dev/null +++ b/Renci.SshNet/AuthenticationResult.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet +{ + /// + /// Represents possible authentication methods results + /// + public enum AuthenticationResult + { + /// + /// Authentication was successful. + /// + Success, + /// + /// Authentication completed with partial success. + /// + PartialSuccess, + /// + /// Authentication failed. + /// + Failure + } +} diff --git a/Renci.SshNet/BaseClient.cs b/Renci.SshNet/BaseClient.cs index d77a850..104e864 100644 --- a/Renci.SshNet/BaseClient.cs +++ b/Renci.SshNet/BaseClient.cs @@ -59,7 +59,7 @@ public TimeSpan KeepAliveInterval if (this._keepAliveTimer == null) { - this._keepAliveTimer = new Timer((state) => + this._keepAliveTimer = new Timer((state) => { this.SendKeepAlive(); }); @@ -72,8 +72,19 @@ public TimeSpan KeepAliveInterval /// /// Occurs when an error occurred. /// + /// + /// + /// public event EventHandler ErrorOccurred; + /// + /// Occurs when host key received. + /// + /// + /// + /// + public event EventHandler HostKeyReceived; + /// /// Initializes a new instance of the class. /// @@ -101,8 +112,9 @@ public void Connect() } this.Session = new Session(this.ConnectionInfo); - this.Session.Connect(); + this.Session.HostKeyReceived += Session_HostKeyReceived; this.Session.ErrorOccured += Session_ErrorOccured; + this.Session.Connect(); this.OnConnected(); } @@ -117,7 +129,7 @@ public void Disconnect() this.OnDisconnecting(); - this.Dispose(); + this.Session.Disconnect(); this.OnDisconnected(); } @@ -167,22 +179,22 @@ protected virtual void OnDisconnected() { } - - /// - /// Ensures that client is connected. - /// - /// When client not connected. - protected void EnsureConnection() + + private void Session_ErrorOccured(object sender, ExceptionEventArgs e) { - if (!this.Session.IsConnected) - throw new SshConnectionException("Client not connected."); + var handler = this.ErrorOccurred; + if (handler != null) + { + handler(this, e); + } } - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) + private void Session_HostKeyReceived(object sender, HostKeyEventArgs e) { - if (this.ErrorOccurred != null) + var handler = this.HostKeyReceived; + if (handler != null) { - this.ErrorOccurred(this, e); + handler(this, e); } } @@ -215,12 +227,14 @@ protected virtual void Dispose(bool disposing) { // Dispose managed ResourceMessages. this.Session.ErrorOccured -= Session_ErrorOccured; + this.Session.HostKeyReceived -= Session_HostKeyReceived; if (this.Session != null) { this.Session.Dispose(); this.Session = null; } + if (this._keepAliveTimer != null) { this._keepAliveTimer.Dispose(); diff --git a/Renci.SshNet/Channels/Channel.cs b/Renci.SshNet/Channels/Channel.cs index 09516d9..acf99f0 100644 --- a/Renci.SshNet/Channels/Channel.cs +++ b/Renci.SshNet/Channels/Channel.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading; using Renci.SshNet.Common; using Renci.SshNet.Messages; @@ -12,14 +13,18 @@ namespace Renci.SshNet.Channels /// internal abstract class Channel : IDisposable { - private EventWaitHandle _channelClosedWaitHandle = new AutoResetEvent(false); + private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false); - private EventWaitHandle _channelWindowAdjustWaitHandle = new AutoResetEvent(false); + private EventWaitHandle _channelServerWindowAdjustWaitHandle = new ManualResetEvent(false); private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(false); private EventWaitHandle _disconnectedWaitHandle = new ManualResetEvent(false); + private object _serverWindowSizeLock = new object(); + + private bool _closeMessageSent = false; + private uint _initialWindowSize = 0x100000; private uint _maximumPacketSize = 0x8000; @@ -196,12 +201,26 @@ internal virtual void Initialize(Session session, uint serverChannelNumber, uint this._session.Disconnected += Session_Disconnected; } + /// + /// Sends the SSH_MSG_CHANNEL_EOF message. + /// + internal void SendEof() + { + // Send EOF message first when channel need to be closed + this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber)); + } + + internal void SendData(byte[] buffer) + { + this.SendMessage(new ChannelDataMessage(this.RemoteChannelNumber, buffer)); + } + /// /// Closes the channel. /// public virtual void Close() { - this.Dispose(); + this.Close(true); } #region Channel virtual methods @@ -250,7 +269,11 @@ protected virtual void OnOpenFailure(uint reasonCode, string description, string /// The bytes to add. protected virtual void OnWindowAdjust(uint bytesToAdd) { - this.ServerWindowSize += bytesToAdd; + lock (this._serverWindowSizeLock) + { + this.ServerWindowSize += bytesToAdd; + } + this._channelServerWindowAdjustWaitHandle.Set(); } /// @@ -298,24 +321,8 @@ protected virtual void OnEof() /// protected virtual void OnClose() { - // No more channel messages are allowed after Close message received - this._session.ChannelOpenReceived -= OnChannelOpen; - this._session.ChannelOpenConfirmationReceived -= OnChannelOpenConfirmation; - this._session.ChannelOpenFailureReceived -= OnChannelOpenFailure; - this._session.ChannelWindowAdjustReceived -= OnChannelWindowAdjust; - this._session.ChannelDataReceived -= OnChannelData; - this._session.ChannelExtendedDataReceived -= OnChannelExtendedData; - this._session.ChannelEofReceived -= OnChannelEof; - this._session.ChannelCloseReceived -= OnChannelClose; - this._session.ChannelRequestReceived -= OnChannelRequest; - this._session.ChannelSuccessReceived -= OnChannelSuccess; - this._session.ChannelFailureReceived -= OnChannelFailure; - this._session.ErrorOccured -= Session_ErrorOccured; - this._session.Disconnected -= Session_Disconnected; - - // Send close message to channel to confirm channel closing - this.SendMessage(new ChannelCloseMessage(this.RemoteChannelNumber)); - + this.Close(false); + if (this.Closed != null) { this.Closed(this, new ChannelEventArgs(this.LocalChannelNumber)); @@ -410,20 +417,36 @@ protected void SendMessage(ChannelCloseMessage message) /// Sends channel data message to the servers. /// /// This method takes care of managing the window size. - /// Channel data message. - public void SendMessage(ChannelDataMessage message) + /// Channel data message. + protected void SendMessage(ChannelDataMessage message) { // Send channel messages only while channel is open if (!this.IsOpen) return; - if (this.ServerWindowSize < 1) + var messageLength = message.Data.Length; + do { - // Wait for window to be adjust - this._session.WaitHandle(this._channelWindowAdjustWaitHandle); - } + lock (this._serverWindowSizeLock) + { + var serverWindowSize = this.ServerWindowSize; + if (serverWindowSize < messageLength) + { + // Wait for window to be big enough for this message + this._channelServerWindowAdjustWaitHandle.Reset(); + } + else + { + this.ServerWindowSize -= (uint)messageLength; + break; + } + } + + // Wait for window to change + this.WaitHandle(this._channelServerWindowAdjustWaitHandle); + + } while (true); - this.ServerWindowSize -= (uint)message.Data.Length; this._session.SendMessage(message); } @@ -438,13 +461,29 @@ protected void SendMessage(ChannelExtendedDataMessage message) if (!this.IsOpen) return; - if (this.ServerWindowSize < 1) + var messageLength = message.Data.Length; + do { - // Wait for window to be adjust - this._session.WaitHandle(this._channelWindowAdjustWaitHandle); - } + lock (this._serverWindowSizeLock) + { + var serverWindowSize = this.ServerWindowSize; + if (serverWindowSize < messageLength) + { + // Wait for window to be big enough for this message + this._channelServerWindowAdjustWaitHandle.Reset(); + } + else + { + this.ServerWindowSize -= (uint)messageLength; + break; + } + } + + // Wait for window to change + this.WaitHandle(this._channelServerWindowAdjustWaitHandle); + + } while (true); - this.ServerWindowSize -= (uint)message.Data.Length; this._session.SendMessage(message); } @@ -457,13 +496,56 @@ protected void WaitHandle(WaitHandle waitHandle) this._session.WaitHandle(waitHandle); } + protected virtual void Close(bool wait) + { + // Send message to close the channel on the server + // Ignore sending close message when client not connected + if (!_closeMessageSent && this.IsConnected) + { + lock (this) + { + if (!_closeMessageSent) + { + this.SendMessage(new ChannelCloseMessage(this.RemoteChannelNumber)); + this._closeMessageSent = true; + } + } + } + + // Wait for channel to be closed + if (wait) + { + this._session.WaitHandle(this._channelClosedWaitHandle); + } + } + + protected virtual void OnDisconnected() + { + } + + protected virtual void OnErrorOccured(Exception exp) + { + } + private void Session_Disconnected(object sender, EventArgs e) { + this.OnDisconnected(); + + // If objected is disposed or being disposed don't handle this event + if (this._isDisposed) + return; + this._disconnectedWaitHandle.Set(); } private void Session_ErrorOccured(object sender, ExceptionEventArgs e) { + this.OnErrorOccured(e.Exception); + + // If objected is disposed or being disposed don't handle this event + if (this._isDisposed) + return; + this._errorOccuredWaitHandle.Set(); } @@ -498,8 +580,6 @@ private void OnChannelWindowAdjust(object sender, MessageEventArgs public void Dispose() { - Dispose(true); + this.Dispose(true); GC.SuppressFinalize(this); } @@ -608,7 +688,7 @@ public void Dispose() /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) - { + { // Check to see if Dispose has already been called. if (!this._isDisposed) { @@ -616,14 +696,7 @@ protected virtual void Dispose(bool disposing) // and unmanaged resources. if (disposing) { - // Send EOF message first when channel need to be closed - this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber)); - - // Send message to close the channel on the server - this.SendMessage(new ChannelCloseMessage(this.RemoteChannelNumber)); - - // Wait for channel to be closed - this._session.WaitHandle(this._channelClosedWaitHandle); + this.Close(false); // Dispose managed resources. if (this._channelClosedWaitHandle != null) @@ -631,10 +704,10 @@ protected virtual void Dispose(bool disposing) this._channelClosedWaitHandle.Dispose(); this._channelClosedWaitHandle = null; } - if (this._channelWindowAdjustWaitHandle != null) + if (this._channelServerWindowAdjustWaitHandle != null) { - this._channelWindowAdjustWaitHandle.Dispose(); - this._channelWindowAdjustWaitHandle = null; + this._channelServerWindowAdjustWaitHandle.Dispose(); + this._channelServerWindowAdjustWaitHandle = null; } if (this._errorOccuredWaitHandle != null) { @@ -665,7 +738,7 @@ protected virtual void Dispose(bool disposing) // Note disposing has been done. - _isDisposed = true; + this._isDisposed = true; } } @@ -678,7 +751,7 @@ protected virtual void Dispose(bool disposing) // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. - Dispose(false); + this.Dispose(false); } #endregion diff --git a/Renci.SshNet/Channels/ChannelDirectTcpip.NET40.cs b/Renci.SshNet/Channels/ChannelDirectTcpip.NET40.cs index f7415b4..9bf7e24 100644 --- a/Renci.SshNet/Channels/ChannelDirectTcpip.NET40.cs +++ b/Renci.SshNet/Channels/ChannelDirectTcpip.NET40.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using System; using System.Net.Sockets; +using System.Threading; namespace Renci.SshNet.Channels { @@ -11,7 +12,7 @@ internal partial class ChannelDirectTcpip { partial void ExecuteThread(Action action) { - Task.Factory.StartNew(action, TaskCreationOptions.LongRunning); + ThreadPool.QueueUserWorkItem((o) => { action(); }); } partial void InternalSocketReceive(byte[] buffer, ref int read) diff --git a/Renci.SshNet/Channels/ChannelDirectTcpip.cs b/Renci.SshNet/Channels/ChannelDirectTcpip.cs index 9aeb457..6154119 100644 --- a/Renci.SshNet/Channels/ChannelDirectTcpip.cs +++ b/Renci.SshNet/Channels/ChannelDirectTcpip.cs @@ -41,18 +41,12 @@ public ChannelDirectTcpip() } - /// - /// Binds channel to specified remote host. - /// - /// The remote host. - /// The port. - /// The socket. - public void Bind(string remoteHost, uint port, Socket socket) + public void Open(string remoteHost, uint port, Socket socket) { this._socket = socket; IPEndPoint ep = socket.RemoteEndPoint as IPEndPoint; - + if (!this.IsConnected) { @@ -65,78 +59,93 @@ public void Bind(string remoteHost, uint port, Socket socket) // Wait for channel to open this.WaitHandle(this._channelOpen); + } - // Start reading data from the port and send to channel - EventWaitHandle readerTaskError = new AutoResetEvent(false); + /// + /// Binds channel to remote host. + /// + public void Bind() + { + // Cannot bind if channel is not open + if (!this.IsOpen) + return; - var readerTaskCompleted = new ManualResetEvent(false); + // Start reading data from the port and send to channel Exception exception = null; - this.ExecuteThread(() => + try { - try - { - var buffer = new byte[this.PacketSize - 9]; + var buffer = new byte[this.PacketSize - 9]; - while (this._socket.Connected || this.IsConnected) + while (this._socket != null && this._socket.CanRead()) + { + try { - try + var read = 0; + this.InternalSocketReceive(buffer, ref read); + if (read > 0) { - - var read = 0; - this.InternalSocketReceive(buffer, ref read); - if (read > 0) - { - this.SendMessage(new ChannelDataMessage(this.RemoteChannelNumber, buffer.Take(read).ToArray())); - } - else - { - break; - } + this.SendMessage(new ChannelDataMessage(this.RemoteChannelNumber, buffer.Take(read).ToArray())); } - catch (SocketException exp) + else { - if (exp.SocketErrorCode == SocketError.WouldBlock || - exp.SocketErrorCode == SocketError.IOPending || - exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable) - { - // socket buffer is probably empty, wait and try again - Thread.Sleep(30); - } - else if (exp.SocketErrorCode == SocketError.ConnectionAborted) - { - break; - } - else - throw; // throw any other error + break; } } + catch (SocketException exp) + { + if (exp.SocketErrorCode == SocketError.WouldBlock || + exp.SocketErrorCode == SocketError.IOPending || + exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable) + { + // socket buffer is probably empty, wait and try again + Thread.Sleep(30); + } + else if (exp.SocketErrorCode == SocketError.ConnectionAborted || exp.SocketErrorCode == SocketError.ConnectionReset) + { + break; + } + else + throw; // throw any other error + } } - catch (Exception exp) - { - readerTaskError.Set(); - exception = exp; - } - finally - { - readerTaskCompleted.Set(); - } - }); + } + catch (Exception exp) + { + exception = exp; + } // Channel was open and we MUST receive EOF notification, // data transfer can take longer then connection specified timeout - System.Threading.WaitHandle.WaitAny(new WaitHandle[] { this._channelEof, readerTaskError }); - - this._socket.Dispose(); - this._socket = null; + // If listener thread is finished then socket was closed + System.Threading.WaitHandle.WaitAny(new WaitHandle[] { this._channelEof }); - // Wait for task to finish and will throw any errors if any - readerTaskCompleted.WaitOne(); + // Close socket if still open + if (this._socket != null) + { + this._socket.Dispose(); + this._socket = null; + } if (exception != null) throw exception; } + public override void Close() + { + // Close socket if still open + if (this._socket != null) + { + this._socket.Dispose(); + this._socket = null; + } + + // Send EOF message first when channel need to be closed + this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber)); + + base.Close(); + } + /// /// Called when channel data is received. /// @@ -161,6 +170,13 @@ protected override void OnOpenConfirmation(uint remoteChannelNumber, uint initia this._channelOpen.Set(); } + protected override void OnOpenFailure(uint reasonCode, string description, string language) + { + base.OnOpenFailure(reasonCode, description, language); + + this._channelOpen.Set(); + } + /// /// Called when channel has no more data to receive. /// @@ -171,6 +187,29 @@ protected override void OnEof() this._channelEof.Set(); } + protected override void OnClose() + { + base.OnClose(); + + this._channelEof.Set(); + } + + protected override void OnErrorOccured(Exception exp) + { + base.OnErrorOccured(exp); + + // If error occured, no more data can be received + this._channelEof.Set(); + } + + protected override void OnDisconnected() + { + base.OnDisconnected(); + + // If disconnected, no more data can be received + this._channelEof.Set(); + } + partial void ExecuteThread(Action action); partial void InternalSocketReceive(byte[] buffer, ref int read); diff --git a/Renci.SshNet/Channels/ChannelForwardedTcpip.NET40.cs b/Renci.SshNet/Channels/ChannelForwardedTcpip.NET40.cs index 1545927..35060b8 100644 --- a/Renci.SshNet/Channels/ChannelForwardedTcpip.NET40.cs +++ b/Renci.SshNet/Channels/ChannelForwardedTcpip.NET40.cs @@ -13,9 +13,9 @@ namespace Renci.SshNet.Channels /// internal partial class ChannelForwardedTcpip : Channel { - partial void OpenSocket(string connectedHost, uint connectedPort) + partial void OpenSocket(IPAddress connectedHost, uint connectedPort) { - var ep = new IPEndPoint(Dns.GetHostEntry(connectedHost).AddressList[0], (int)connectedPort); + var ep = new IPEndPoint(connectedHost, (int)connectedPort); this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); this._socket.Connect(ep); this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1); diff --git a/Renci.SshNet/Channels/ChannelForwardedTcpip.cs b/Renci.SshNet/Channels/ChannelForwardedTcpip.cs index 4f1d538..ad7c4be 100644 --- a/Renci.SshNet/Channels/ChannelForwardedTcpip.cs +++ b/Renci.SshNet/Channels/ChannelForwardedTcpip.cs @@ -40,7 +40,7 @@ public ChannelForwardedTcpip() /// /// The connected host. /// The connected port. - public void Bind(string connectedHost, uint connectedPort) + public void Bind(IPAddress connectedHost, uint connectedPort) { byte[] buffer = null; @@ -71,8 +71,8 @@ public void Bind(string connectedHost, uint connectedPort) } // Start reading data from the port and send to channel - while (this._socket.Connected || this.IsConnected) - { + while (this._socket != null && this._socket.CanRead()) + { try { int read = 0; @@ -106,12 +106,18 @@ public void Bind(string connectedHost, uint connectedPort) } } - this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber)); - this.Close(); } - partial void OpenSocket(string connectedHost, uint connectedPort); + partial void OpenSocket(IPAddress connectedHost, uint connectedPort); + + public override void Close() + { + // Send EOF message first when channel need to be closed + this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber)); + + base.Close(); + } /// /// Called when channel data is received. diff --git a/Renci.SshNet/Channels/ChannelSession.cs b/Renci.SshNet/Channels/ChannelSession.cs index bfa4a3f..0946725 100644 --- a/Renci.SshNet/Channels/ChannelSession.cs +++ b/Renci.SshNet/Channels/ChannelSession.cs @@ -3,6 +3,8 @@ using Renci.SshNet.Common; using Renci.SshNet.Messages.Connection; using System.Globalization; +using System.Collections.Generic; +using System.Text; namespace Renci.SshNet.Channels { @@ -80,8 +82,6 @@ protected override void OnOpenFailure(uint reasonCode, string description, strin { this._failedOpenAttempts++; - Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Local channel: {0} attempts: {1}.", this.LocalChannelNumber, this._failedOpenAttempts)); - this.SessionSemaphore.Release(); this._channelOpenResponseWaitHandle.Set(); @@ -101,6 +101,16 @@ protected override void OnClose() this.SessionSemaphore.Release(); } + protected override void Close(bool wait) + { + base.Close(wait); + + if (!wait) + { + this.SessionSemaphore.Release(); + } + } + /// /// Sends the pseudo terminal request. /// @@ -109,15 +119,17 @@ protected override void OnClose() /// The rows. /// The width. /// The height. - /// The terminal mode. - /// true if request was successful; otherwise false. - public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, string terminalMode) + /// The terminal mode values. + /// + /// true if request was successful; otherwise false. + /// + public bool SendPseudoTerminalRequest(string environmentVariable, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues) { this._channelRequestResponse.Reset(); - this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalMode))); - - this._channelRequestResponse.WaitOne(); + this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new PseudoTerminalRequestInfo(environmentVariable, columns, rows, width, height, terminalModeValues))); + + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } @@ -136,7 +148,7 @@ public bool SendX11ForwardingRequest(bool isSingleConnection, string protocol, b this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new X11ForwardingRequestInfo(isSingleConnection, protocol, cookie, screenNumber))); - this._channelRequestResponse.WaitOne(); + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } @@ -153,7 +165,7 @@ public bool SendEnvironmentVariableRequest(string variableName, string variableV this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EnvironmentVariableRequestInfo(variableName, variableValue))); - this._channelRequestResponse.WaitOne(); + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } @@ -168,7 +180,7 @@ public bool SendShellRequest() this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ShellRequestInfo())); - this._channelRequestResponse.WaitOne(); + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } @@ -182,9 +194,25 @@ public bool SendExecRequest(string command) { this._channelRequestResponse.Reset(); - this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExecRequestInfo(command))); + this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new ExecRequestInfo(command, this.ConnectionInfo.Encoding))); - this._channelRequestResponse.WaitOne(); + this.WaitHandle(this._channelRequestResponse); + + return this._channelRequestSucces; + } + + /// + /// Sends the exec request. + /// + /// Length of the break. + /// true if request was successful; otherwise false. + public bool SendBreakRequest(uint breakLength) + { + this._channelRequestResponse.Reset(); + + this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new BreakRequestInfo(breakLength))); + + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } @@ -200,7 +228,7 @@ public bool SendSubsystemRequest(string subsystem) this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new SubsystemRequestInfo(subsystem))); - this._channelRequestResponse.WaitOne(); + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } @@ -281,7 +309,7 @@ public bool SendEndOfWriteRequest() this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new EndOfWriteRequestInfo())); - this._channelRequestResponse.WaitOne(); + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } @@ -296,12 +324,11 @@ public bool SendKeepAliveRequest() this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new KeepAliveRequestInfo())); - this._channelRequestResponse.WaitOne(); + this.WaitHandle(this._channelRequestResponse); return this._channelRequestSucces; } - /// /// Called when channel request was successful /// @@ -356,9 +383,5 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - - - - } } diff --git a/Renci.SshNet/CipherInfo.cs b/Renci.SshNet/CipherInfo.cs index 4cea1e0..4c0ffff 100644 --- a/Renci.SshNet/CipherInfo.cs +++ b/Renci.SshNet/CipherInfo.cs @@ -22,14 +22,14 @@ public class CipherInfo /// /// Gets the cipher. /// - public Func Cipher { get; private set; } + public Func Cipher { get; private set; } /// /// Initializes a new instance of the class. /// /// Size of the key. /// The cipher. - public CipherInfo(int keySize, Func cipher) + public CipherInfo(int keySize, Func cipher) { this.KeySize = keySize; this.Cipher = (key, iv) => (cipher(key.Take(this.KeySize / 8).ToArray(), iv)); diff --git a/Renci.SshNet/Common/ASCIIEncoding.cs b/Renci.SshNet/Common/ASCIIEncoding.cs new file mode 100644 index 0000000..daae60b --- /dev/null +++ b/Renci.SshNet/Common/ASCIIEncoding.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// Implementation of ASCII Encoding + /// + public class ASCIIEncoding : Encoding + { + private readonly char _fallbackChar; + + private static char[] _byteToChar; + + static ASCIIEncoding() + { + if (_byteToChar == null) + { + _byteToChar = new char[128]; + var ch = '\0'; + for (byte i = 0; i < 128; i++) + { + _byteToChar[i] = ch++; + } + } + } + + /// + /// Initializes a new instance of the class. + /// + public ASCIIEncoding() + { + this._fallbackChar = '?'; + } + + /// + /// Calculates the number of bytes produced by encoding a set of characters from the specified character array. + /// + /// The character array containing the set of characters to encode. + /// The index of the first character to encode. + /// The number of characters to encode. + /// + /// The number of bytes produced by encoding the specified characters. + /// + /// + /// is null. + /// + /// + /// or is less than zero.-or- and do not denote a valid range in . + /// + /// A fallback occurred (see Understanding Encodings for complete explanation)-and- is set to . + public override int GetByteCount(char[] chars, int index, int count) + { + return count; + } + + /// + /// Encodes a set of characters from the specified character array into the specified byte array. + /// + /// The character array containing the set of characters to encode. + /// The index of the first character to encode. + /// The number of characters to encode. + /// The byte array to contain the resulting sequence of bytes. + /// The index at which to start writing the resulting sequence of bytes. + /// + /// The actual number of bytes written into . + /// + /// + /// is null.-or- is null. + /// + /// + /// or or is less than zero.-or- and do not denote a valid range in .-or- is not a valid index in . + /// + /// + /// does not have enough capacity from to the end of the array to accommodate the resulting bytes. + /// + /// A fallback occurred (see Understanding Encodings for complete explanation)-and- is set to . + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + for (int i = 0; i < charCount && i < chars.Length; i++) + { + var b = (byte)chars[i + charIndex]; + + if (b > 127) + b = (byte)this._fallbackChar; + + bytes[i + byteIndex] = b; + } + return charCount; + } + + /// + /// Calculates the number of characters produced by decoding a sequence of bytes from the specified byte array. + /// + /// The byte array containing the sequence of bytes to decode. + /// The index of the first byte to decode. + /// The number of bytes to decode. + /// + /// The number of characters produced by decoding the specified sequence of bytes. + /// + /// + /// is null. + /// + /// + /// or is less than zero.-or- and do not denote a valid range in . + /// + /// A fallback occurred (see Understanding Encodings for complete explanation)-and- is set to . + public override int GetCharCount(byte[] bytes, int index, int count) + { + return count; + } + + /// + /// Decodes a sequence of bytes from the specified byte array into the specified character array. + /// + /// The byte array containing the sequence of bytes to decode. + /// The index of the first byte to decode. + /// The number of bytes to decode. + /// The character array to contain the resulting set of characters. + /// The index at which to start writing the resulting set of characters. + /// + /// The actual number of characters written into . + /// + /// + /// is null.-or- is null. + /// + /// + /// or or is less than zero.-or- and do not denote a valid range in .-or- is not a valid index in . + /// + /// + /// does not have enough capacity from to the end of the array to accommodate the resulting characters. + /// + /// A fallback occurred (see Understanding Encodings for complete explanation)-and- is set to . + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + for (int i = 0; i < byteCount; i++) + { + var b = bytes[i + byteIndex]; + char ch; + + if (b > 127) + { + ch = this._fallbackChar; + } + else + { + ch = _byteToChar[b]; + } + + chars[i + charIndex] = ch; + } + return byteCount; + } + + /// + /// Calculates the maximum number of bytes produced by encoding the specified number of characters. + /// + /// The number of characters to encode. + /// + /// The maximum number of bytes produced by encoding the specified number of characters. + /// + /// + /// is less than zero. + /// + /// A fallback occurred (see Understanding Encodings for complete explanation)-and- is set to . + public override int GetMaxByteCount(int charCount) + { + return charCount; + } + + /// + /// Calculates the maximum number of characters produced by decoding the specified number of bytes. + /// + /// The number of bytes to decode. + /// + /// The maximum number of characters produced by decoding the specified number of bytes. + /// + /// + /// is less than zero. + /// + /// A fallback occurred (see Understanding Encodings for complete explanation)-and- is set to . + public override int GetMaxCharCount(int byteCount) + { + return byteCount; + } + } +} diff --git a/Renci.SshNet/Common/AsyncResult.cs b/Renci.SshNet/Common/AsyncResult.cs index 919aa53..c28bba2 100644 --- a/Renci.SshNet/Common/AsyncResult.cs +++ b/Renci.SshNet/Common/AsyncResult.cs @@ -90,7 +90,7 @@ public void EndInvoke() // Operation is done: if an exception occurred, throw it if (this._exception != null) - throw this._exception; + throw new SshException(this._exception.Message, this._exception); } #region Implementation of IAsyncResult @@ -155,6 +155,7 @@ public Boolean IsCompleted /// /// Base class to encapsulates the results of an asynchronous operation that returns result. /// + /// The type of the result. public abstract class AsyncResult : AsyncResult { // Field set when operation completes @@ -186,7 +187,7 @@ public void SetAsCompleted(TResult result, Boolean completedSynchronously) /// /// Waits until the asynchronous operation completes, and then returns the value generated by the asynchronous operation. /// - /// + /// Invocation result new public TResult EndInvoke() { base.EndInvoke(); // Wait until operation has completed diff --git a/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs b/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs index 65cd23b..7b2d1ec 100644 --- a/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs +++ b/Renci.SshNet/Common/AuthenticationPasswordChangeEventArgs.cs @@ -16,7 +16,7 @@ public class AuthenticationPasswordChangeEventArgs : AuthenticationEventArgs /// /// The new password. /// - public string NewPassword { get; set; } + public byte[] NewPassword { get; set; } /// /// Initializes a new instance of the class. diff --git a/Renci.SshNet/Common/BigInteger.cs b/Renci.SshNet/Common/BigInteger.cs index ffd8d2c..fea8632 100644 --- a/Renci.SshNet/Common/BigInteger.cs +++ b/Renci.SshNet/Common/BigInteger.cs @@ -51,6 +51,7 @@ using System.Globalization; using System.Text; using System.Threading; +using System.Security.Cryptography; /* Optimization @@ -69,6 +70,8 @@ namespace Renci.SshNet.Common /// public struct BigInteger : IComparable, IFormattable, IComparable, IEquatable { + private static RNGCryptoServiceProvider _randomizer = new System.Security.Cryptography.RNGCryptoServiceProvider(); + private const ulong _BASE = 0x100000000; private const Int32 _DECIMALSIGNMASK = unchecked((Int32)0x80000000); private const int _BIAS = 1075; @@ -1960,6 +1963,19 @@ public int CompareTo(ulong other) return LongCompare(low, high); } + /// + /// Generates random BigInteger number + /// + /// Length of random number in bits. + /// Big random number. + public static BigInteger Random(int bitLength) + { + var bytesArray = new byte[bitLength / 8 + (((bitLength % 8) > 0) ? 1 : 0)]; + _randomizer.GetBytes(bytesArray); + bytesArray[bytesArray.Length - 1] = (byte)(bytesArray[bytesArray.Length - 1] & 0x7F); // Ensure not a negative value + return new BigInteger(bytesArray.ToArray()); + } + /// /// Divides one System.Numerics.BigInteger value by another and returns the result. /// @@ -2312,7 +2328,7 @@ public static BigInteger ModPow(BigInteger value, BigInteger exponent, BigIntege /// /// The bi. /// The modulus. - /// + /// Modulus inverted number. public static BigInteger ModInverse(BigInteger bi, BigInteger modulus) { BigInteger a = modulus, b = bi % modulus; @@ -2383,7 +2399,7 @@ public static BigInteger Negate(BigInteger value) /// A string that contains a number to convert. /// A bitwise combination of the enumeration values that specify the permitted format of value. /// An object that provides culture-specific formatting information about value. - /// + /// Parsed number public static BigInteger Parse(string value, System.Globalization.NumberStyles style, IFormatProvider provider) { Exception ex; diff --git a/Renci.SshNet/Common/DerData.cs b/Renci.SshNet/Common/DerData.cs index 70a42e3..6c43cb1 100644 --- a/Renci.SshNet/Common/DerData.cs +++ b/Renci.SshNet/Common/DerData.cs @@ -83,7 +83,7 @@ public DerData(byte[] data) /// /// Encodes written data as DER byte array. /// - /// + /// DER Encoded array. public byte[] Encode() { var length = this._data.Count(); diff --git a/Renci.SshNet/Common/Extensions.NET.cs b/Renci.SshNet/Common/Extensions.NET.cs new file mode 100644 index 0000000..0a426f7 --- /dev/null +++ b/Renci.SshNet/Common/Extensions.NET.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Renci.SshNet +{ + /// + /// Collection of different extension method specific for .NET 4.0 + /// + public static partial class Extensions + { + /// + /// Determines whether [is null or white space] [the specified value]. + /// + /// The value. + /// + /// true if [is null or white space] [the specified value]; otherwise, false. + /// + internal static bool IsNullOrWhiteSpace(this string value) + { + if (string.IsNullOrEmpty(value)) return true; + + return value.All(char.IsWhiteSpace); + } + + internal static bool CanRead(this Socket socket) + { + return socket.Connected && socket.Poll(-1, SelectMode.SelectRead) && socket.Available > 0; + } + + internal static bool CanWrite(this Socket socket) + { + return socket.Connected && socket.Poll(-1, SelectMode.SelectWrite); + } + + internal static IPAddress GetIPAddress(this string host) + { + IPAddress ipAddress; + if (!IPAddress.TryParse(host, out ipAddress)) + ipAddress = Dns.GetHostAddresses(host).First(); + + return ipAddress; + } + } +} diff --git a/Renci.SshNet/Common/Extensions.NET40.cs b/Renci.SshNet/Common/Extensions.NET40.cs deleted file mode 100644 index 1403271..0000000 --- a/Renci.SshNet/Common/Extensions.NET40.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet -{ - /// - /// Collection of different extension method specific for .NET 4.0 - /// - public static partial class Extensions - { - /// - /// Indicates whether a specified string is null, empty, or consists only of white-space characters. - /// - /// The string to test. - /// - /// true if the value parameter is null or System.String.Empty, or if value consists exclusively of white-space characters; otherwise, false. - /// - internal static bool IsNullOrWhiteSpace(this string value) - { - return string.IsNullOrWhiteSpace(value); - } - } -} diff --git a/Renci.SshNet/Common/Extensions.cs b/Renci.SshNet/Common/Extensions.cs index 427e58e..258c1da 100644 --- a/Renci.SshNet/Common/Extensions.cs +++ b/Renci.SshNet/Common/Extensions.cs @@ -61,8 +61,8 @@ internal static bool IsEqualTo(this IEnumerable value, IEnumer } finally { - enumerator1.Dispose(); - enumerator2.Dispose(); + if (enumerator1 != null) enumerator1.Dispose(); + if (enumerator2 != null) enumerator2.Dispose(); } } } @@ -79,14 +79,12 @@ internal static bool IsEqualTo(this IEnumerable value, IEnumer } #if SILVERLIGHT -#else - +#else /// /// Prints out /// /// The bytes. - internal static void DebugPrint(this IEnumerable bytes) { foreach (var b in bytes) @@ -101,7 +99,7 @@ internal static void DebugPrint(this IEnumerable bytes) /// Trims the leading zero from bytes array. /// /// The data. - /// + /// Data without leading zeros. internal static IEnumerable TrimLeadingZero(this IEnumerable data) { bool leadingZero = true; @@ -174,30 +172,52 @@ internal static byte[] GetBytes(this Int64 value) } #if SILVERLIGHT - private static Regex _rehost = new Regex(@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", RegexOptions.IgnoreCase); + private static Regex _rehost = new Regex(@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$", RegexOptions.IgnoreCase); private static Regex _reIPv6 = new Regex(@"^(((?=(?>.*?::)(?!.*::)))(::)?([0-9A-F]{1,4}::?){0,5}|([0-9A-F]{1,4}:){6})(\2([0-9A-F]{1,4}(::?|$)){0,2}|((25[0-5]|(2[0-4]|1\d|[1-9])?\d)(\.|$)){4}|[0-9A-F]{1,4}:[0-9A-F]{1,4})(?.*?::)(?!.*::)))(::)?([0-9A-F]{1,4}::?){0,5}|([0-9A-F]{1,4}:){6})(\2([0-9A-F]{1,4}(::?|$)){0,2}|((25[0-5]|(2[0-4]|1\d|[1-9])?\d)(\.|$)){4}|[0-9A-F]{1,4}:[0-9A-F]{1,4})(?.*?::)(?!.*::)))(::)?([0-9A-F]{1,4}::?){0,5}|([0-9A-F]{1,4}:){6})(\2([0-9A-F]{1,4}(::?|$)){0,2}|((25[0-5]|(2[0-4]|1\d|[1-9])?\d)(\.|$)){4}|[0-9A-F]{1,4}:[0-9A-F]{1,4})(?= IPEndPoint.MinPort && value <= IPEndPoint.MaxPort; + if (value < IPEndPoint.MinPort) + return false; + + if (value > IPEndPoint.MaxPort) + return false; + return true; } internal static bool IsValidPort(this int value) { - return value >= IPEndPoint.MinPort && value <= IPEndPoint.MaxPort; + if (value < IPEndPoint.MinPort) + return false; + + if (value > IPEndPoint.MaxPort) + return false; + return true; } + } } diff --git a/Renci.SshNet/Common/HostKeyEventArgs.cs b/Renci.SshNet/Common/HostKeyEventArgs.cs new file mode 100644 index 0000000..8c10615 --- /dev/null +++ b/Renci.SshNet/Common/HostKeyEventArgs.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Renci.SshNet.Security.Cryptography; +using Renci.SshNet.Security; + +namespace Renci.SshNet.Common +{ + /// + /// Provides data for the HostKeyReceived event. + /// + public class HostKeyEventArgs : EventArgs + { + /// + /// Gets or sets a value indicating whether host key can be trusted. + /// + /// + /// true if host key can be trusted; otherwise, false. + /// + public bool CanTrust { get; set; } + + /// + /// Gets the host key. + /// + public byte[] HostKey { get; private set; } + + /// + /// Gets the host key name. + /// + public string HostKeyName{ get; private set; } + + /// + /// Gets the finger print. + /// + public byte[] FingerPrint { get; private set; } + + /// + /// Gets the length of the key in bits. + /// + /// + /// The length of the key in bits. + /// + public int KeyLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The host. + public HostKeyEventArgs(KeyHostAlgorithm host) + { + this.CanTrust = true; // Set default value + + this.HostKey = host.Data; + + this.HostKeyName = host.Name; + + this.KeyLength = host.Key.KeyLength; + + using (var md5 = new MD5Hash()) + { + this.FingerPrint = md5.ComputeHash(host.Data); + } + } + } +} diff --git a/Renci.SshNet/Common/NetConfServerException.NET40.cs b/Renci.SshNet/Common/NetConfServerException.NET40.cs new file mode 100644 index 0000000..c723fde --- /dev/null +++ b/Renci.SshNet/Common/NetConfServerException.NET40.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.Serialization; + +namespace Renci.SshNet.Common +{ + /// + /// The exception that is thrown when there is something wrong with the server capabilities. + /// + [Serializable] + public partial class NetConfServerException : SshException + { + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// + /// The class name is null or is zero (0). + protected NetConfServerException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/Renci.SshNet/Common/NetConfServerException.cs b/Renci.SshNet/Common/NetConfServerException.cs new file mode 100644 index 0000000..29ff4bd --- /dev/null +++ b/Renci.SshNet/Common/NetConfServerException.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// The exception that is thrown when there is something wrong with the server capabilities. + /// + public partial class NetConfServerException : SshException + { + /// + /// Initializes a new instance of the class. + /// + public NetConfServerException() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public NetConfServerException(string message) + : base(message) + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public NetConfServerException(string message, Exception innerException) : + base(message, innerException) + { + } + } +} diff --git a/Renci.SshNet/Common/PipeStream.cs b/Renci.SshNet/Common/PipeStream.cs index 214448c..a542181 100644 --- a/Renci.SshNet/Common/PipeStream.cs +++ b/Renci.SshNet/Common/PipeStream.cs @@ -181,7 +181,7 @@ public override int Read(byte[] buffer, int offset, int count) Monitor.Wait(this._buffer); // fill the read buffer - for (; readLength < count && Length > 0; readLength++) + for (; readLength < count && Length > 0 && this._buffer.Count > 0; readLength++) { buffer[readLength] = this._buffer.Dequeue(); } @@ -192,10 +192,10 @@ public override int Read(byte[] buffer, int offset, int count) } /// - /// Returns true if there are + /// Returns true if there are /// - /// - /// + /// The count. + /// True if data available; otherwisefalse. private bool ReadAvailable(int count) { return (this.Length >= count || this._isFlushed) && diff --git a/Renci.SshNet/Common/ProxyException.NET40.cs b/Renci.SshNet/Common/ProxyException.NET40.cs new file mode 100644 index 0000000..527283c --- /dev/null +++ b/Renci.SshNet/Common/ProxyException.NET40.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.Serialization; + +namespace Renci.SshNet.Common +{ + [Serializable] + public partial class ProxyException : SshException + { + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// + /// The class name is null or is zero (0). + protected ProxyException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + +} diff --git a/Renci.SshNet/Common/ProxyException.cs b/Renci.SshNet/Common/ProxyException.cs new file mode 100644 index 0000000..304a051 --- /dev/null +++ b/Renci.SshNet/Common/ProxyException.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// The exception that is thrown when SCP error occurred. + /// + public partial class ProxyException : SshException + { + /// + /// Initializes a new instance of the class. + /// + public ProxyException() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ProxyException(string message) + : base(message) + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public ProxyException(string message, Exception innerException) : + base(message, innerException) + { + } + } + +} diff --git a/Renci.SshNet/Common/ScpDownloadEventArgs.cs b/Renci.SshNet/Common/ScpDownloadEventArgs.cs new file mode 100644 index 0000000..a6abbe8 --- /dev/null +++ b/Renci.SshNet/Common/ScpDownloadEventArgs.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// Provides data for the Downloading event. + /// + public class ScpDownloadEventArgs : EventArgs + { + /// + /// Gets the downloaded filename. + /// + public string Filename { get; private set; } + + /// + /// Gets the downloaded file size. + /// + public long Size { get; private set; } + + /// + /// Gets number of downloaded bytes so far. + /// + public long Downloaded { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The downloaded filename. + /// The downloaded file size. + /// The number of downloaded bytes so far. + public ScpDownloadEventArgs(string filename, long size, long downloaded) + { + this.Filename = filename; + this.Size = size; + this.Downloaded = downloaded; + } + } +} diff --git a/Renci.SshNet/Common/ScpException.NET40.cs b/Renci.SshNet/Common/ScpException.NET40.cs new file mode 100644 index 0000000..3b05f87 --- /dev/null +++ b/Renci.SshNet/Common/ScpException.NET40.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.Serialization; + +namespace Renci.SshNet.Common +{ + [Serializable] + public partial class ScpException : SshException + { + /// + /// Initializes a new instance of the class. + /// + /// The that holds the serialized object data about the exception being thrown. + /// The that contains contextual information about the source or destination. + /// The parameter is null. + /// + /// The class name is null or is zero (0). + protected ScpException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/Renci.SshNet/Common/ScpException.cs b/Renci.SshNet/Common/ScpException.cs new file mode 100644 index 0000000..9a5d8b6 --- /dev/null +++ b/Renci.SshNet/Common/ScpException.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// The exception that is thrown when SCP error occurred. + /// + public partial class ScpException : SshException + { + /// + /// Initializes a new instance of the class. + /// + public ScpException() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ScpException(string message) + : base(message) + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public ScpException(string message, Exception innerException) : + base(message, innerException) + { + } + } +} diff --git a/Renci.SshNet/Common/ScpUploadEventArgs.cs b/Renci.SshNet/Common/ScpUploadEventArgs.cs new file mode 100644 index 0000000..5772525 --- /dev/null +++ b/Renci.SshNet/Common/ScpUploadEventArgs.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// Provides data for the Uploading event. + /// + public class ScpUploadEventArgs : EventArgs + { + /// + /// Gets the uploaded filename. + /// + public string Filename { get; private set; } + + /// + /// Gets the uploaded file size. + /// + public long Size { get; private set; } + + /// + /// Gets number of uploaded bytes so far. + /// + public long Uploaded { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The uploaded filename. + /// The the uploaded file size. + /// The number of uploaded bytes so far. + public ScpUploadEventArgs(string filename, long size, long uploaded) + { + this.Filename = filename; + this.Size = size; + this.Uploaded = uploaded; + } + } +} diff --git a/Renci.SshNet/Common/ShellDataEventArgs.cs b/Renci.SshNet/Common/ShellDataEventArgs.cs new file mode 100644 index 0000000..a4d2689 --- /dev/null +++ b/Renci.SshNet/Common/ShellDataEventArgs.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// Provides data for Shell DataReceived event + /// + public class ShellDataEventArgs : EventArgs + { + /// + /// Gets the data. + /// + public byte[] Data { get; private set; } + + /// + /// Gets the line data. + /// + public string Line { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The data. + public ShellDataEventArgs(byte[] data) + { + this.Data = data; + } + + /// + /// Initializes a new instance of the class. + /// + /// The line. + public ShellDataEventArgs(string line) + { + this.Line = line; + } + } +} diff --git a/Renci.SshNet/Common/SshData.cs b/Renci.SshNet/Common/SshData.cs index 6a3de60..cea315a 100644 --- a/Renci.SshNet/Common/SshData.cs +++ b/Renci.SshNet/Common/SshData.cs @@ -11,6 +11,14 @@ namespace Renci.SshNet.Common /// public abstract class SshData { + private static Encoding _ascii = new ASCIIEncoding(); + +#if SILVERLIGHT + private static Encoding _utf8 = Encoding.UTF8; +#else + private static Encoding _utf8 = Encoding.Default; +#endif + /// /// Data byte array that hold message unencrypted data /// @@ -51,7 +59,7 @@ protected virtual int ZeroReaderIndex /// /// Gets data bytes array /// - /// + /// Byte array representation of data structure. public virtual byte[] GetBytes() { this._data = new List(); @@ -194,7 +202,7 @@ protected UInt32 ReadUInt32() protected UInt64 ReadUInt64() { var data = this.ReadBytes(8); - return (uint)(data[0] << 56 | data[1] << 48 | data[2] << 40 | data[3] << 32 | data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]); + return ((ulong)data[0] << 56 | (ulong)data[1] << 48 | (ulong)data[2] << 40 | (ulong)data[3] << 32 | (ulong)data[4] << 24 | (ulong)data[5] << 16 | (ulong)data[6] << 8 | data[7]); } /// @@ -207,11 +215,35 @@ protected Int64 ReadInt64() return (int)(data[0] << 56 | data[1] << 48 | data[2] << 40 | data[3] << 32 | data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]); } + /// + /// Reads next string data type from internal buffer. + /// + /// string read + protected string ReadAsciiString() + { + var length = this.ReadUInt32(); + + if (length > (uint)int.MaxValue) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue)); + } + return _ascii.GetString(this.ReadBytes((int)length), 0, (int)length); + } + /// /// Reads next string data type from internal buffer. /// /// string read protected string ReadString() + { + return this.ReadString(SshData._utf8); + } + + /// + /// Reads next string data type from internal buffer. + /// + /// string read + protected string ReadString(Encoding encoding) { var length = this.ReadUInt32(); @@ -219,9 +251,10 @@ protected string ReadString() { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue)); } - return Encoding.UTF8.GetString(this.ReadBytes((int)length), 0, (int)length); + return encoding.GetString(this.ReadBytes((int)length), 0, (int)length); } + /// /// Reads next string data type from internal buffer. /// @@ -254,7 +287,7 @@ protected BigInteger ReadBigInt() /// /// Reads next name-list data type from internal buffer. /// - /// + /// String array or read data.. protected string[] ReadNamesList() { var namesList = this.ReadString(); @@ -264,7 +297,7 @@ protected string[] ReadNamesList() /// /// Reads next extension-pair data type from internal buffer. /// - /// + /// Extensions pair dictionary. protected IDictionary ReadExtensionPair() { Dictionary result = new Dictionary(); @@ -348,12 +381,26 @@ protected void Write(Int64 data) this.Write(data.GetBytes()); } + /// - /// Writes string data into internal buffer. + /// Writes string data into internal buffer as ASCII. + /// + /// string data to write. + protected void WriteAscii(string data) + { + this.Write(data, SshData._ascii); + } + + /// + /// Writes string data into internal buffer using default encoding. /// /// string data to write. - /// String text encoding to use. /// is null. + protected void Write(string data) + { + this.Write(data, SshData._utf8); + } + protected void Write(string data, Encoding encoding) { if (data == null) @@ -364,15 +411,6 @@ protected void Write(string data, Encoding encoding) this.Write(bytes); } - /// - /// Writes string data into internal buffer. - /// - /// string data to write. - protected void Write(string data) - { - this.Write(data, Encoding.UTF8); - } - /// /// Writes string data into internal buffer. /// @@ -404,9 +442,10 @@ protected void Write(BigInteger data) /// name-list data to write. protected void Write(string[] data) { - this.Write(string.Join(",", data)); + this.WriteAscii(string.Join(",", data)); } + /// /// Writes extension-pair data into internal buffer. /// @@ -415,8 +454,8 @@ protected void Write(IDictionary data) { foreach (var item in data) { - this.Write(item.Key); - this.Write(item.Value); + this.WriteAscii(item.Key); + this.WriteAscii(item.Value); } } } diff --git a/Renci.SshNet/Common/TerminalModes.cs b/Renci.SshNet/Common/TerminalModes.cs new file mode 100644 index 0000000..3afbaa2 --- /dev/null +++ b/Renci.SshNet/Common/TerminalModes.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Common +{ + /// + /// Specifies the initial assignments of the opcode values that are used in the 'encoded terminal modes' valu + /// + public enum TerminalModes : byte + { + /// + /// Indicates end of options. + /// + TTY_OP_END = 0, + + /// + /// Interrupt character; 255 if none. Similarly for the other characters. Not all of these characters are supported on all systems. + /// + VINTR = 1, + + /// + /// The quit character (sends SIGQUIT signal on POSIX systems). + /// + VQUIT = 2, + + /// + /// Erase the character to left of the cursor. + /// + VERASE = 3, + + /// + /// Kill the current input line. + /// + VKILL = 4, + + /// + /// End-of-file character (sends EOF from the terminal). + /// + VEOF = 5, + + /// + /// End-of-line character in addition to carriage return and/or linefeed. + /// + VEOL = 6, + + /// + /// Additional end-of-line character. + /// + VEOL2 = 7, + + /// + /// Continues paused output (normally control-Q). + /// + VSTART = 8, + + /// + /// Pauses output (normally control-S). + /// + VSTOP = 9, + + /// + /// Suspends the current program. + /// + VSUSP = 10, + + /// + /// Another suspend character. + /// + VDSUSP = 11, + + /// + /// Reprints the current input line. + /// + VREPRINT = 12, + + /// + /// Erases a word left of cursor. + /// + VWERASE = 13, + + /// + /// Enter the next character typed literally, even if it is a special character + /// + VLNEXT = 14, + + /// + /// Character to flush output. + /// + VFLUSH = 15, + + /// + /// Switch to a different shell layer. + /// + VSWTCH = 16, + + /// + /// Prints system status line (load, command, pid, etc). + /// + VSTATUS = 17, + + /// + /// Toggles the flushing of terminal output. + /// + VDISCARD = 18, + + /// + /// The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, and 1 if it is TRUE. + /// + IGNPAR = 30, + + /// + /// Mark parity and framing errors. + /// + PARMRK = 31, + + /// + /// Enable checking of parity errors. + /// + INPCK = 32, + + /// + /// Strip 8th bit off characters. + /// + ISTRIP = 33, + + /// + /// Map NL into CR on input. + /// + INLCR = 34, + + /// + /// Ignore CR on input. + /// + IGNCR = 35, + + /// + /// Map CR to NL on input. + /// + ICRNL = 36, + + /// + /// Translate uppercase characters to lowercase. + /// + IUCLC = 37, + + /// + /// Enable output flow control. + /// + IXON = 38, + + /// + /// Any char will restart after stop. + /// + IXANY = 39, + + /// + /// Enable input flow control. + /// + IXOFF = 40, + + /// + /// Ring bell on input queue full. + /// + IMAXBEL = 41, + + /// + /// Enable signals INTR, QUIT, [D]SUSP. + /// + ISIG = 50, + + /// + /// Canonicalize input lines. + /// + ICANON = 51, + + /// + /// Enable input and output of uppercase characters by preceding their lowercase equivalents with "\". + /// + XCASE = 52, + + /// + /// Enable echoing. + /// + ECHO = 53, + + /// + /// Visually erase chars. + /// + ECHOE = 54, + + /// + /// Kill character discards current line. + /// + ECHOK = 55, + + /// + /// Echo NL even if ECHO is off. + /// + ECHONL = 56, + + /// + /// Don't flush after interrupt. + /// + NOFLSH = 57, + + /// + /// Stop background jobs from output. + /// + TOSTOP = 58, + + /// + /// Enable extensions. + /// + IEXTEN = 59, + + /// + /// Echo control characters as ^(Char). + /// + ECHOCTL = 60, + + /// + /// Visual erase for line kill. + /// + ECHOKE = 61, + + /// + /// Retype pending input. + /// + PENDIN = 62, + + /// + /// Enable output processing. + /// + OPOST = 70, + + /// + /// Convert lowercase to uppercase. + /// + OLCUC = 71, + + /// + /// Map NL to CR-NL. + /// + ONLCR = 72, + + /// + /// Translate carriage return to newline (output). + /// + OCRNL = 73, + + /// + /// Translate newline to carriage return-newline (output). + /// + ONOCR = 74, + + /// + /// Newline performs a carriage return (output). + /// + ONLRET = 75, + + /// + /// 7 bit mode. + /// + CS7 = 90, + + /// + /// 8 bit mode. + /// + CS8 = 91, + + /// + /// Parity enable. + /// + PARENB = 92, + + /// + /// Odd parity, else even. + /// + PARODD = 93, + + /// + /// Specifies the input baud rate in bits per second. + /// + TTY_OP_ISPEED = 128, + + /// + /// Specifies the output baud rate in bits per second. + /// + TTY_OP_OSPEED = 129, + } +} diff --git a/Renci.SshNet/Compression/Adler32.cs b/Renci.SshNet/Compression/Adler32.cs new file mode 100644 index 0000000..c38258f --- /dev/null +++ b/Renci.SshNet/Compression/Adler32.cs @@ -0,0 +1,88 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class Adler32{ + + // largest prime smaller than 65536 + private const int BASE=65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX=5552; + + internal long adler32(long adler, byte[] buf, int index, int len){ + if(buf == null){ return 1L; } + + long s1=adler&0xffff; + long s2=(adler>>16)&0xffff; + int k; + + while(len > 0) { + k=len=16){ + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + k-=16; + } + if(k!=0){ + do{ + s1+=buf[index++]&0xff; s2+=s1; + } + while(--k!=0); + } + s1%=BASE; + s2%=BASE; + } + return (s2<<16)|s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/CompressionMode.cs b/Renci.SshNet/Compression/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/Compressor.cs b/Renci.SshNet/Compression/Compressor.cs index e7e84bf..1479bb6 100644 --- a/Renci.SshNet/Compression/Compressor.cs +++ b/Renci.SshNet/Compression/Compressor.cs @@ -1,17 +1,46 @@ using System.Collections.Generic; using Renci.SshNet.Security; +using System.IO; +using System; + namespace Renci.SshNet.Compression { /// /// Represents base class for compression algorithm implementation /// - public abstract class Compressor : Algorithm + public abstract class Compressor : Algorithm, IDisposable { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + /// /// Gets the session. /// protected Session Session { get; private set; } + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + /// /// Initializes the algorithm /// @@ -26,13 +55,97 @@ public virtual void Init(Session session) /// /// Data to compress. /// Compressed data - public abstract byte[] Compress(byte[] data); + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } /// /// Decompresses the specified data. /// /// Compressed data. /// Decompressed data. - public abstract byte[] Decompress(byte[] data); + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion } } diff --git a/Renci.SshNet/Compression/Deflate.cs b/Renci.SshNet/Compression/Deflate.cs new file mode 100644 index 0000000..ca04309 --- /dev/null +++ b/Renci.SshNet/Compression/Deflate.cs @@ -0,0 +1,1640 @@ +using System; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + public sealed class Deflate{ + + private const int MAX_MEM_LEVEL=9; + + private const int Z_DEFAULT_COMPRESSION=-1; + + private const int MAX_WBITS=15; // 32K LZ77 window + private const int DEF_MEM_LEVEL=8; + + internal class Config{ + internal int good_length; // reduce lazy search above this match length + internal int max_lazy; // do not perform lazy search above this match length + internal int nice_length; // quit search above this match length + internal int max_chain; + internal int func; + internal Config(int good_length, int max_lazy, + int nice_length, int max_chain, int func){ + this.good_length=good_length; + this.max_lazy=max_lazy; + this.nice_length=nice_length; + this.max_chain=max_chain; + this.func=func; + } + } + + private const int STORED=0; + private const int FAST=1; + private const int SLOW=2; + private static readonly Config[] config_table; + + static Deflate(){ + config_table=new Config[10]; + // good lazy nice chain + config_table[0]=new Config(0, 0, 0, 0, STORED); + config_table[1]=new Config(4, 4, 8, 4, FAST); + config_table[2]=new Config(4, 5, 16, 8, FAST); + config_table[3]=new Config(4, 6, 32, 32, FAST); + + config_table[4]=new Config(4, 4, 16, 16, SLOW); + config_table[5]=new Config(8, 16, 32, 32, SLOW); + config_table[6]=new Config(8, 16, 128, 128, SLOW); + config_table[7]=new Config(8, 32, 128, 256, SLOW); + config_table[8]=new Config(32, 128, 258, 1024, SLOW); + config_table[9]=new Config(32, 258, 258, 4096, SLOW); + } + + private static readonly String[] z_errmsg = { + "need dictionary", // Z_NEED_DICT 2 + "stream end", // Z_STREAM_END 1 + "", // Z_OK 0 + "file error", // Z_ERRNO (-1) + "stream error", // Z_STREAM_ERROR (-2) + "data error", // Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + // block not completed, need more input or more output + private const int NeedMore=0; + + // block flush performed + private const int BlockDone=1; + + // finish started, need only more output at next deflate + private const int FinishStarted=2; + + // finish done, accept no more input or output + private const int FinishDone=3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT=0x20; + + private const int Z_FILTERED=1; + private const int Z_HUFFMAN_ONLY=2; + private const int Z_DEFAULT_STRATEGY=0; + + private const int Z_NO_FLUSH=0; + private const int Z_PARTIAL_FLUSH=1; + private const int Z_SYNC_FLUSH=2; + private const int Z_FULL_FLUSH=3; + private const int Z_FINISH=4; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int INIT_STATE=42; + private const int BUSY_STATE=113; + private const int FINISH_STATE=666; + + // The deflate compression method + private const int Z_DEFLATED=8; + + private const int STORED_BLOCK=0; + private const int STATIC_TREES=1; + private const int DYN_TREES=2; + + // The three kinds of block type + private const int Z_BINARY=0; + private const int Z_ASCII=1; + private const int Z_UNKNOWN=2; + + private const int Buf_size=8*2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6=16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10=17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138=18; + + private const int MIN_MATCH=3; + private const int MAX_MATCH=258; + private const int MIN_LOOKAHEAD=(MAX_MATCH+MIN_MATCH+1); + + private const int MAX_BITS=15; + private const int D_CODES=30; + private const int BL_CODES=19; + private const int LENGTH_CODES=29; + private const int LITERALS=256; + private const int L_CODES=(LITERALS+1+LENGTH_CODES); + private const int HEAP_SIZE=(2*L_CODES+1); + + private const int END_BLOCK=256; + + internal ZStream strm; // pointer back to this zlib stream + internal int status; // as the name implies + internal byte[] pending_buf; // output still pending + internal int pending_buf_size; // size of pending_buf + internal int pending_out; // next pending byte to output to the stream + internal int pending; // nb of bytes in the pending buffer + internal int noheader; // suppress zlib header and adler32 + internal byte data_type; // UNKNOWN, BINARY or ASCII + internal byte method; // STORED (for zip only) or DEFLATED + internal int last_flush; // value of flush param for previous deflate call + + internal int w_size; // LZ77 window size (32K by default) + internal int w_bits; // log2(w_size) (8..16) + internal int w_mask; // w_size - 1 + + internal byte[] window; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least wSize + // bytes. With this organization, matches are limited to a distance of + // wSize-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: use the user input buffer as sliding window. + + internal int window_size; + // Actual size of window: 2*wSize, except when the user input buffer + // is directly used as sliding window. + + internal short[] prev; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + + internal short[] head; // Heads of the hash chains or NIL. + + internal int ins_h; // hash index of string to be inserted + internal int hash_size; // number of elements in hash table + internal int hash_bits; // log2(hash_size) + internal int hash_mask; // hash_size-1 + + // Number of bits by which ins_h must be shifted at each input + // step. It must be such that after MIN_MATCH steps, the oldest + // byte no longer takes part in the hash key, that is: + // hash_shift * MIN_MATCH >= hash_bits + internal int hash_shift; + + // Window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + internal int block_start; + + internal int match_length; // length of best match + internal int prev_match; // previous match + internal int match_available; // set if previous match exists + internal int strstart; // start of string to insert + internal int match_start; // start of matching string + internal int lookahead; // number of valid bytes ahead in window + + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + internal int prev_length; + + // To speed up deflation, hash chains are never searched beyond this + // length. A higher limit improves compression ratio but degrades the speed. + internal int max_chain_length; + + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + internal int max_lazy_match; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + internal int level; // compression level (1..9) + internal int strategy; // favor or force Huffman coding + + // Use a faster search when the previous match is longer than this + internal int good_match; + + // Stop searching when current match exceeds this + internal int nice_match; + + internal short[] dyn_ltree; // literal and length tree + internal short[] dyn_dtree; // distance tree + internal short[] bl_tree; // Huffman tree for bit lengths + + internal Tree l_desc=new Tree(); // desc for literal tree + internal Tree d_desc=new Tree(); // desc for distance tree + internal Tree bl_desc=new Tree(); // desc for bit length tree + + // number of codes at each bit length for an optimal tree + internal short[] bl_count=new short[MAX_BITS+1]; + + // heap used to build the Huffman trees + internal int[] heap=new int[2*L_CODES+1]; + + internal int heap_len; // number of elements in the heap + internal int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + // Depth of each subtree used as tie breaker for trees of equal frequency + internal byte[] depth=new byte[2*L_CODES+1]; + + internal int l_buf; // index for literals or lengths */ + + // Size of match buffer for literals/lengths. There are 4 reasons for + // limiting lit_bufsize to 64K: + // - frequencies can be kept in 16 bit counters + // - if compression is not successful for the first block, all input + // data is still in the window so we can still emit a stored block even + // when input comes from standard input. (This can also be done for + // all blocks if lit_bufsize is not greater than 32K.) + // - if compression is not successful for a file smaller than 64K, we can + // even emit a stored file instead of a stored block (saving 5 bytes). + // This is applicable only for zip (not gzip or zlib). + // - creating new Huffman trees less frequently may not provide fast + // adaptation to changes in the input data statistics. (Take for + // example a binary file with poorly compressible code followed by + // a highly compressible string table.) Smaller buffer sizes give + // fast adaptation but have of course the overhead of transmitting + // trees more frequently. + // - I can't count above 4 + internal int lit_bufsize; + + internal int last_lit; // running index in l_buf + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + internal int d_buf; // index of pendig_buf + + internal int opt_len; // bit length of current block with optimal trees + internal int static_len; // bit length of current block with static trees + internal int matches; // number of string matches in current block + internal int last_eob_len; // bit length of EOB code for last block + + // Output buffer. bits are inserted starting at the bottom (least + // significant bits). + internal uint bi_buf; + + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + internal int bi_valid; + + internal Deflate(){ + dyn_ltree=new short[HEAP_SIZE*2]; + dyn_dtree=new short[(2*D_CODES+1)*2]; // distance tree + bl_tree=new short[(2*BL_CODES+1)*2]; // Huffman tree for bit lengths + } + + internal void lm_init() { + window_size=2*w_size; + + head[hash_size-1]=0; + for(int i=0; i= 3; max_blindex--) { + if (bl_tree[Tree.bl_order[max_blindex]*2+1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3*(max_blindex+1) + 5+5+4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + internal void send_all_trees(int lcodes, int dcodes, int blcodes){ + int rank; // index in bl_order + + send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { + send_bits(bl_tree[Tree.bl_order[rank]*2+1], 3); + } + send_tree(dyn_ltree, lcodes-1); // literal tree + send_tree(dyn_dtree, dcodes-1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + internal void send_tree (short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ){ + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0*2+1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0){ max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[(n+1)*2+1]; + if(++count < max_count && curlen == nextlen) { + continue; + } + else if(count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if(curlen != 0){ + if(curlen != prevlen){ + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count-3, 2); + } + else if(count <= 10){ + send_code(REPZ_3_10, bl_tree); + send_bits(count-3, 3); + } + else{ + send_code(REPZ_11_138, bl_tree); + send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if(nextlen == 0){ + max_count = 138; min_count = 3; + } + else if(curlen == nextlen){ + max_count = 6; min_count = 3; + } + else{ + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + internal void put_byte(byte[] p, int start, int len){ + System.Array.Copy(p, start, pending_buf, pending, len); + pending+=len; + } + + internal void put_byte(byte c){ + pending_buf[pending++]=c; + } + internal void put_short(int w) { + pending_buf[pending++]=(byte)(w/*&0xff*/); + pending_buf[pending++]=(byte)(w>>8); + } + internal void putShortMSB(int b){ + pending_buf[pending++]=(byte)(b>>8); + pending_buf[pending++]=(byte)(b/*&0xff*/); + } + + internal void send_code(int c, short[] tree){ + int c2=c*2; + send_bits((tree[c2]&0xffff), (tree[c2+1]&0xffff)); + } + + internal void send_bits(int val, int length){ + if (bi_valid > Buf_size - length) { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++]=(byte)(bi_buf/*&0xff*/); + pending_buf[pending++]=(byte)(bi_buf>>8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } else { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } +// int len = length; +// if (bi_valid > (int)Buf_size - len) { +// int val = value; +// // bi_buf |= (val << bi_valid); +// bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); +// put_short(bi_buf); +// bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); +// bi_valid += len - Buf_size; +// } else { +// // bi_buf |= (value) << bi_valid; +// bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); +// bi_valid += len; +// } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + internal void _tr_align(){ + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) { + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + internal bool _tr_tally (int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ){ + + pending_buf[d_buf+last_lit*2] = (byte)(dist>>8); + pending_buf[d_buf+last_lit*2+1] = (byte)dist; + + pending_buf[l_buf+last_lit] = (byte)lc; last_lit++; + + if (dist == 0) { + // lc is the unmatched char + dyn_ltree[lc*2]++; + } + else { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Tree._length_code[lc]+LITERALS+1)*2]++; + dyn_dtree[Tree.d_code(dist)*2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > 2) { + // Compute an upper bound for the compressed length + int out_length = last_lit*8; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (int)((int)dyn_dtree[dcode*2] * + (5L+Tree.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit/2)) && out_length < in_length/2) return true; + } + + return (last_lit == lit_bufsize-1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + internal void compress_block(short[] ltree, short[] dtree){ + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0){ + do{ + dist=((pending_buf[d_buf+lx*2]<<8)&0xff00)| + (pending_buf[d_buf+lx*2+1]&0xff); + lc=(pending_buf[l_buf+lx])&0xff; lx++; + + if(dist == 0){ + send_code(lc, ltree); // send a literal byte + } + else{ + // Here, lc is the match length - MIN_MATCH + code = Tree._length_code[lc]; + + send_code(code+LITERALS+1, ltree); // send the length code + extra = Tree.extra_lbits[code]; + if(extra != 0){ + lc -= Tree.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.d_code(dist); + + send_code(code, dtree); // send the distance code + extra = Tree.extra_dbits[code]; + if (extra != 0) { + dist -= Tree.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK*2+1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + internal void set_data_type(){ + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while(n<7){ bin_freq += dyn_ltree[n*2]; n++;} + while(n<128){ ascii_freq += dyn_ltree[n*2]; n++;} + while(n (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + internal void bi_flush(){ + if (bi_valid == 16) { + pending_buf[pending++]=(byte)(bi_buf/*&0xff*/); + pending_buf[pending++]=(byte)(bi_buf>>8); + bi_buf=0; + bi_valid=0; + } + else if (bi_valid >= 8) { + pending_buf[pending++]=(byte)(bi_buf); + bi_buf>>=8; + bi_buf &= 0x00ff; + bi_valid-=8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + internal void bi_windup(){ + if (bi_valid > 8) { + pending_buf[pending++]=(byte)(bi_buf); + pending_buf[pending++]=(byte)(bi_buf>>8); + } else if (bi_valid > 0) { + pending_buf[pending++]=(byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + internal void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ){ + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(window, buf, len); + } + + internal void flush_block_only(bool eof){ + _tr_flush_block(block_start>=0 ? block_start : -1, + strstart-block_start, + eof); + block_start=strstart; + strm.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + internal int deflate_stored(int flush){ + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if(max_block_size > pending_buf_size - 5) { + max_block_size = pending_buf_size - 5; + } + + // Copy as much as possible from input to output: + while(true){ + // Fill the window as much as possible: + if(lookahead<=1){ + fill_window(); + if(lookahead==0 && flush==Z_NO_FLUSH) return NeedMore; + if(lookahead==0) break; // flush the current block + } + + strstart+=lookahead; + lookahead=0; + + // Emit a stored block if pending_buf will be full: + max_start=block_start+max_block_size; + if(strstart==0|| strstart>=max_start) { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = (int)(strstart-max_start); + strstart = (int)max_start; + + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if(strstart-block_start >= w_size-MIN_LOOKAHEAD) { + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + } + } + + flush_block_only(flush == Z_FINISH); + if(strm.avail_out==0) + return (flush == Z_FINISH) ? FinishStarted : NeedMore; + + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + internal void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ){ + send_bits((STORED_BLOCK<<1)+(eof?1:0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + internal void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if(level > 0) { + // Check if the file is ascii or binary + if(data_type == Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.build_tree(this); + + d_desc.build_tree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex=build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb=(opt_len+3+7)>>3; + static_lenb=(static_len+3+7)>>3; + + if(static_lenb<=opt_lenb) opt_lenb=static_lenb; + } + else { + opt_lenb=static_lenb=stored_len+5; // force a stored block + } + + if(stored_len+4<=opt_lenb && buf != -1){ + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if(static_lenb == opt_lenb){ + send_bits((STATIC_TREES<<1)+(eof?1:0), 3); + compress_block(StaticTree.static_ltree, StaticTree.static_dtree); + } + else{ + send_bits((DYN_TREES<<1)+(eof?1:0), 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if(eof){ + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + internal void fill_window(){ + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do{ + more = (window_size-lookahead-strstart); + + // Deal with !@#$% 64K limit: + if(more==0 && strstart==0 && lookahead==0){ + more = w_size; + } + else if(more==-1) { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if(strstart >= w_size+ w_size-MIN_LOOKAHEAD) { + System.Array.Copy(window, w_size, window, 0, w_size); + match_start-=w_size; + strstart-=w_size; // we now have strstart >= MAX_DIST + block_start-=w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p=n; + do { + m = (head[--p]&0xffff); + head[p]=(short)(m>=w_size ? (m-w_size) : 0); + } + while (--n != 0); + + n = w_size; + p = n; + do { + m = (prev[--p]&0xffff); + prev[p] = (short)(m >= w_size ? (m-w_size) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n!=0); + more += w_size; + } + + if (strm.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = strm.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if(lookahead >= MIN_MATCH) { + ins_h = window[strstart]&0xff; + ins_h=(((ins_h)<= MIN_MATCH){ + ins_h=(((ins_h)<=MIN_MATCH){ + // check_match(strstart, match_start, match_length); + + bflush=_tr_tally(strstart-match_start, match_length-MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if(match_length <= max_lazy_match && + lookahead >= MIN_MATCH) { + match_length--; // string at strstart already in hash table + do{ + strstart++; + + ins_h=((ins_h<= MIN_MATCH) { + ins_h=(((ins_h)< 4096))) { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH-1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if(prev_length >= MIN_MATCH && match_length <= prev_length) { + int max_insert = strstart + lookahead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush=_tr_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= prev_length-1; + prev_length -= 2; + do{ + if(++strstart <= max_insert) { + ins_h=(((ins_h)<(w_size-MIN_LOOKAHEAD) ? + strstart-(w_size-MIN_LOOKAHEAD) : 0; + int nice_match=this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = w_mask; + + int strend = strstart + MAX_MATCH; + byte scan_end1 = window[scan+best_len-1]; + byte scan_end = window[scan+best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= good_match) { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > lookahead) nice_match = lookahead; + + do { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (window[match+best_len] != scan_end || + window[match+best_len-1] != scan_end1 || + window[match] != window[scan] || + window[++match] != window[scan+1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do { + } while (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if(len>best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = window[scan+best_len-1]; + scan_end = window[scan+best_len]; + } + + } while ((cur_match = (prev[cur_match & wmask]&0xffff)) > limit + && --chain_length != 0); + + if (best_len <= lookahead) return best_len; + return lookahead; + } + + internal int deflateInit(ZStream strm, int level, int bits){ + return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY); + } + internal int deflateInit(ZStream strm, int level){ + return deflateInit(strm, level, MAX_WBITS); + } + internal int deflateInit2(ZStream strm, int level, int method, int windowBits, + int memLevel, int strategy){ + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == Z_DEFAULT_COMPRESSION) level = 6; + + if (windowBits < 0) { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + strm.dstate = (Deflate)this; + + this.noheader = noheader; + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits+MIN_MATCH-1)/MIN_MATCH); + + window = new byte[w_size*2]; + prev = new short[w_size]; + head = new short[hash_size]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize*4]; + pending_buf_size = lit_bufsize*4; + + d_buf = lit_bufsize/2; + l_buf = (1+2)*lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this.method = (byte)method; + + return deflateReset(strm); + } + + internal int deflateReset(ZStream strm){ + strm.total_in = strm.total_out = 0; + strm.msg = null; // + strm.data_type = Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if(noheader < 0) { + noheader = 0; // was set to -1 by deflate(..., Z_FINISH); + } + status = (noheader!=0) ? BUSY_STATE : INIT_STATE; + strm.adler=strm._adler.adler32(0, null, 0, 0); + + last_flush = Z_NO_FLUSH; + + tr_init(); + lm_init(); + return Z_OK; + } + + internal int deflateEnd(){ + if(status!=INIT_STATE && status!=BUSY_STATE && status!=FINISH_STATE){ + return Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf=null; + head=null; + prev=null; + window=null; + // free + // dstate=null; + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; + } + + internal int deflateParams(ZStream strm, int _level, int _strategy){ + int err=Z_OK; + + if(_level == Z_DEFAULT_COMPRESSION){ + _level = 6; + } + if(_level < 0 || _level > 9 || + _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + if(config_table[level].func!=config_table[_level].func && + strm.total_in != 0) { + // Flush the last buffer: + err = strm.deflate(Z_PARTIAL_FLUSH); + } + + if(level != _level) { + level = _level; + max_lazy_match = config_table[level].max_lazy; + good_match = config_table[level].good_length; + nice_match = config_table[level].nice_length; + max_chain_length = config_table[level].max_chain; + } + strategy = _strategy; + return err; + } + + internal int deflateSetDictionary (ZStream strm, byte[] dictionary, int dictLength){ + int length = dictLength; + int index=0; + + if(dictionary == null || status != INIT_STATE) + return Z_STREAM_ERROR; + + strm.adler=strm._adler.adler32(strm.adler, dictionary, 0, dictLength); + + if(length < MIN_MATCH) return Z_OK; + if(length > w_size-MIN_LOOKAHEAD){ + length = w_size-MIN_LOOKAHEAD; + index=dictLength-length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, window, 0, length); + strstart = length; + block_start = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0]&0xff; + ins_h=(((ins_h)<Z_FINISH || flush<0){ + return Z_STREAM_ERROR; + } + + if(strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (status == FINISH_STATE && flush != Z_FINISH)) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_STREAM_ERROR)]; + return Z_STREAM_ERROR; + } + if(strm.avail_out == 0){ + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + this.strm = strm; // just in case + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if(status == INIT_STATE) { + int header = (Z_DEFLATED+((w_bits-8)<<4))<<8; + int level_flags=((level-1)&0xff)>>1; + + if(level_flags>3) level_flags=3; + header |= (level_flags<<6); + if(strstart!=0) header |= PRESET_DICT; + header+=31-(header % 31); + + status=BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if(strstart!=0){ + putShortMSB((int)(strm.adler>>16)); + putShortMSB((int)(strm.adler&0xffff)); + } + strm.adler=strm._adler.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if(pending != 0) { + strm.flush_pending(); + if(strm.avail_out == 0) { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = -1; + return Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if(strm.avail_in==0 && flush <= old_flush && + flush != Z_FINISH) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if(status == FINISH_STATE && strm.avail_in != 0) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if(strm.avail_in!=0 || lookahead!=0 || + (flush != Z_NO_FLUSH && status != FINISH_STATE)) { + int bstate=-1; + switch(config_table[level].func){ + case STORED: + bstate = deflate_stored(flush); + break; + case FAST: + bstate = deflate_fast(flush); + break; + case SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate==FinishStarted || bstate==FinishDone) { + status = FINISH_STATE; + } + if (bstate==NeedMore || bstate==FinishStarted) { + if(strm.avail_out == 0) { + last_flush = -1; // avoid BUF_ERROR next call, see above + } + return Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate==BlockDone) { + if(flush == Z_PARTIAL_FLUSH) { + _tr_align(); + } + else { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if(flush == Z_FULL_FLUSH) { + //state.head[s.hash_size-1]=0; + for(int i=0; i>16)); + putShortMSB((int)(strm.adler&0xffff)); + strm.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? Z_OK : Z_STREAM_END; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/InfBlocks.cs b/Renci.SshNet/Compression/InfBlocks.cs new file mode 100644 index 0000000..479d9b5 --- /dev/null +++ b/Renci.SshNet/Compression/InfBlocks.cs @@ -0,0 +1,618 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfBlocks{ + private const int MANY=1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int TYPE=0; // get type bits (3, including end bit) + private const int LENS=1; // get lengths for stored + private const int STORED=2;// processing stored block + private const int TABLE=3; // get table lengths + private const int BTREE=4; // get bit lengths tree for a dynamic block + private const int DTREE=5; // get length, distance trees for a dynamic block + private const int CODES=6; // processing fixed or dynamic block + private const int DRY=7; // output remaining window bytes + private const int DONE=8; // finished last block, done + private const int BAD=9; // ot a data error--stuck here + + internal int mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb=new int[1]; // bit length tree depth + internal int[] tb=new int[1]; // bit length decoding tree + + internal InfCodes codes=new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InfTree inftree=new InfTree(); + + internal InfBlocks(ZStream z, Object checkfn, int w){ + hufts=new int[MANY*3]; + window=new byte[w]; + end=w; + this.checkfn = checkfn; + mode = TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c){ + if(c!=null) c[0]=check; + if(mode==BTREE || mode==DTREE){ + } + if(mode==CODES){ + codes.free(z); + } + mode=TYPE; + bitk=0; + bitb=0; + read=write=0; + + if(checkfn != null) + z.adler=check=z._adler.adler32(0L, null, 0, 0); + } + + internal int proc(ZStream z, int r){ + int t; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=bitb;k=bitk;} { + q=write;m=(int)(q> 1){ + case 0: { // stored + b>>=(3);k-=(3);} + t = k & 7; { // go to byte boundary + + b>>=(t);k-=(t);} + mode = LENS; // get length of stored block + break; + case 1: { // fixed + int[] bl=new int[1]; + int[] bd=new int[1]; + int[][] tl=new int[1][]; + int[][] td=new int[1][]; + + InfTree.inflate_trees_fixed(bl, bd, tl, td, z); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } { + + b>>=(3);k-=(3);} + + mode = CODES; + break; + case 2: { // dynamic + + b>>=(3);k-=(3);} + + mode = TABLE; + break; + case 3: { // illegal + + b>>=(3);k-=(3);} + mode = BAD; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + break; + case LENS: + + while(k<(32)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<> 16) & 0xffff) != (b & 0xffff)){ + mode = BAD; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE); + break; + case STORED: + if (n == 0){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + if(m==0){ + if(q==end&&read!=0){ + q=0; m=(int)(qn) t = n; + if(t>m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last!=0 ? DRY : TYPE; + break; + case TABLE: + + while(k<(14)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)< 29 || ((t >> 5) & 0x1f) > 29) { + mode = BAD; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if(blens==null || blens.Length>=(14);k-=(14);} + + index = 0; + mode = BTREE; + goto case BTREE; + case BTREE: + while (index < 4 + (table >> 10)){ + while(k<(3)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>=(3);k-=(3);} + } + + while(index < 19){ + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK){ + r = t; + if (r == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + index = 0; + mode = DTREE; + goto case DTREE; + case DTREE: + while (true){ + t = table; + if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){ + break; + } + + int i, j, c; + + t = bb[0]; + + while(k<(t)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>=(t);k-=(t); + blens[index++] = c; + } + else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while(k<(t+i)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<>=(t);k-=(t); + + j += (b & inflate_mask[i]); + + b>>=(i);k-=(i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)){ + blens=null; + mode = BAD; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + c = c == 16 ? blens[i-1] : 0; + do{ + blens[i++] = c; + } + while (--j!=0); + index = i; + } + } + + tb[0]=-1; { + int[] bl=new int[1]; + int[] bd=new int[1]; + int[] tl=new int[1]; + int[] td=new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (t != Z_OK){ + if (t == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + r = t; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = CODES; + goto case CODES; + case CODES: + bitb=b; bitk=k; + z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + + if ((r = codes.proc(this, z, r)) != Z_STREAM_END){ + return inflate_flush(z, r); + } + r = Z_OK; + codes.free(z); + + p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk; + q=write;m=(int)(q z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(checkfn != null) + z.adler=check=z._adler.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end){ + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(checkfn != null) + z.adler=check=z._adler.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/InfCodes.cs b/Renci.SshNet/Compression/InfCodes.cs new file mode 100644 index 0000000..6fcafe4 --- /dev/null +++ b/Renci.SshNet/Compression/InfCodes.cs @@ -0,0 +1,611 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfCodes{ + + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + private const int START=0; // x: set up for LEN + private const int LEN=1; // i: get length/literal/eob next + private const int LENEXT=2; // i: getting length extra (have base) + private const int DIST=3; // i: get distance next + private const int DISTEXT=4;// i: getting distance extra + private const int COPY=5; // o: copying bytes in window, waiting for space + private const int LIT=6; // o: got literal, waiting for output space + private const int WASH=7; // o: got eob, possibly still output waiting + private const int END=8; // x: got eob and all data flushed + private const int BADCODE=9;// x: got error + + int mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index=0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InfCodes(){ + } + internal void init(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z){ + mode=START; + lbits=(byte)bl; + dbits=(byte)bd; + ltree=tl; + ltree_index=tl_index; + dtree = td; + dtree_index=td_index; + tree=null; + } + + internal int proc(InfBlocks s, ZStream z, int r){ + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b=0; // bit buffer + int k=0; // bits in bit buffer + int p=0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q= 258 && n >= 10){ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q>=(tree[tindex+1]); + k-=(tree[tindex+1]); + + e=tree[tindex]; + + if(e == 0){ // literal + lit = tree[tindex+2]; + mode = LIT; + break; + } + if((e & 16)!=0 ){ // length + get = e & 15; + len = tree[tindex+2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3+tree[tindex+2]; + break; + } + if ((e & 32)!=0){ // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + need = dbits; + tree = dtree; + tree_index=dtree_index; + mode = DIST; + goto case DIST; + case DIST: // i: get distance next + j = need; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<>=tree[tindex+1]; + k-=tree[tindex+1]; + + e = (tree[tindex]); + if((e & 16)!=0){ // distance + get = e & 15; + dist = tree[tindex+2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3 + tree[tindex+2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + case DISTEXT: // i: getting distance extra + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<>=j; + k-=j; + + mode = COPY; + goto case COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while(f < 0){ // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len!=0){ + + if(m==0){ + if(q==s.end&&s.read!=0){q=0;m=q 7){ // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write=q; r=s.inflate_flush(z,r); + q=s.write;m=q= 258 && n >= 10 + // get literal/length code + while(k<(20)){ // max bits for literal/length code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++] = (byte)tp[tp_index_t_3+2]; + m--; + continue; + } + do { + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + e &= 15; + c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]); + + b>>=e; k-=e; + + // decode distance base of block to copy + while(k<(15)){ // max bits for distance code + n--; + b|=(z.next_in[p++]&0xff)<>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + // get extra bits to add to distance base + e &= 15; + while(k<(e)){ // get extra bits (up to 13) + n--; + b|=(z.next_in[p++]&0xff)<>=(e); k-=(e); + + // do the copy + m -= c; + if (q >= d){ // offset before dest + // just copy + r=q-d; + if(q-r>0 && 2>(q-r)){ + s.window[q++]=s.window[r++]; // minimum count is three, + s.window[q++]=s.window[r++]; // so unroll loop a little + c-=2; + } + else{ + System.Array.Copy(s.window, r, s.window, q, 2); + q+=2; r+=2; c-=2; + } + } + else{ // else offset after destination + r=q-d; + do{ + r+=s.end; // force pointer in window + }while(r<0); // covers invalid distances + e=s.end-r; + if(c>e){ // if source crosses, + c-=e; // wrapped copy + if(q-r>0 && e>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--e!=0); + } + else{ + System.Array.Copy(s.window, r, s.window, q, e); + q+=e; r+=e; e=0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if(q-r>0 && c>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--c!=0); + } + else{ + System.Array.Copy(s.window, r, s.window, q, c); + q+=c; r+=c; c=0; + } + break; + } + else if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + e=tp[tp_index_t_3]; + } + else{ + z.msg = "invalid distance code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + break; + } + + if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + if((e=tp[tp_index_t_3])==0){ + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++]=(byte)tp[tp_index_t_3+2]; + m--; + break; + } + } + else if((e&32)!=0){ + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_STREAM_END; + } + else{ + z.msg="invalid literal/length code"; + + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + } + while(m>=258 && n>= 10); + + // not enough input or output--restore pointers and return + c=z.avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/InfTree.cs b/Renci.SshNet/Compression/InfTree.cs new file mode 100644 index 0000000..6ed7d19 --- /dev/null +++ b/Renci.SshNet/Compression/InfTree.cs @@ -0,0 +1,523 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfTree{ + + private const int MANY=1440; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX=15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ){ + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do { + c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX + }while(i!=0); + + if(c[0] == n){ // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if(c[j]!=0) break; + k = j; // minimum code length + if(l < j){ + l = j; + } + for (i = BMAX; i!=0; i--){ + if(c[i]!=0) break; + } + g = i; // maximum code length + if(l > i){ + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1){ + if ((y -= c[j]) < 0){ + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0){ + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i!=0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do { + if ((j = b[bindex+p]) != 0){ + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++){ + a = c[k]; + while (a--!=0){ + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l){ + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if((f=1<<(j=k-w))>a+1){ // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if(j < z){ + while (++j < z){ // try smaller tables up to z bits + if((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY){ // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if(h!=0){ + x[h]=i; // save pattern for backing up + r[0]=(byte)j; // bits in this table + r[1]=(byte)l; // bits to dump before this table + j=i>>(w - l); + r[2] = (int)(q - u[h-1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table + } + else{ + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n){ + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s){ + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else{ + r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists + r[2]=d[v[p++] - s]; + } + + // fill code-like entries with r + f=1<<(k-w); + for (j=i>>w;j>= 1){ + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]){ + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + internal int inflate_trees_bits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + initWorkArea(19); + hn[0]=0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if(result == Z_BUF_ERROR || bb[0] == 0){ + z.msg = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + internal int inflate_trees_dynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + + // build literal/length tree + initWorkArea(288); + hn[0]=0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0){ + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed literal/length tree"; + } + else if (result != Z_MEM_ERROR){ + z.msg = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)){ + if (result == Z_DATA_ERROR){ + z.msg = "oversubscribed distance tree"; + } + else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } + else if (result != Z_MEM_ERROR){ + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + internal static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ){ + bl[0]=fixed_bl; + bd[0]=fixed_bd; + tl[0]=fixed_tl; + td[0]=fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize){ + if(hn==null){ + hn=new int[1]; + v=new int[vsize]; + c=new int[BMAX+1]; + r=new int[3]; + u=new int[BMAX]; + x=new int[BMAX+1]; + } + if(v.Lengthstate); + return Z_OK; + } + + internal int inflateInit(ZStream z, int w){ + z.msg = null; + blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + nowrap = 0; + if(w < 0){ + w = - w; + nowrap = 1; + } + + // set window size + if(w<8 ||w>15){ + inflateEnd(z); + return Z_STREAM_ERROR; + } + wbits=w; + + z.istate.blocks=new InfBlocks(z, + z.istate.nowrap!=0 ? null : this, + 1<>4)+8>z.istate.wbits){ + z.istate.mode = BAD; + z.msg="invalid window size"; + z.istate.marker = 5; // can't try inflateSync + break; + } + z.istate.mode=FLAG; + goto case FLAG; + case FLAG: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + b = (z.next_in[z.next_in_index++])&0xff; + + if((((z.istate.method << 8)+b) % 31)!=0){ + z.istate.mode = BAD; + z.msg = "incorrect header check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + if((b&PRESET_DICT)==0){ + z.istate.mode = BLOCKS; + break; + } + z.istate.mode = DICT4; + goto case DICT4; + case DICT4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + z.istate.mode=DICT3; + goto case DICT3; + case DICT3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + z.istate.mode=DICT2; + goto case DICT2; + case DICT2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + z.istate.mode=DICT1; + goto case DICT1; + case DICT1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need += (z.next_in[z.next_in_index++]&0xffL); + z.adler = z.istate.need; + z.istate.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z.istate.mode = BAD; + z.msg = "need dictionary"; + z.istate.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + + r = z.istate.blocks.proc(z, r); + if(r == Z_DATA_ERROR){ + z.istate.mode = BAD; + z.istate.marker = 0; // can try inflateSync + break; + } + if(r == Z_OK){ + r = f; + } + if(r != Z_STREAM_END){ + return r; + } + r = f; + z.istate.blocks.reset(z, z.istate.was); + if(z.istate.nowrap!=0){ + z.istate.mode=DONE; + break; + } + z.istate.mode=CHECK4; + goto case CHECK4; + case CHECK4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + z.istate.mode=CHECK3; + goto case CHECK3; + case CHECK3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + z.istate.mode = CHECK2; + goto case CHECK2; + case CHECK2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + z.istate.mode = CHECK1; + goto case CHECK1; + case CHECK1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=(z.next_in[z.next_in_index++]&0xffL); + + if(((int)(z.istate.was[0])) != ((int)(z.istate.need))){ + z.istate.mode = BAD; + z.msg = "incorrect data check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + z.istate.mode = DONE; + goto case DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + } + } + + + internal int inflateSetDictionary(ZStream z, byte[] dictionary, int dictLength){ + int index=0; + int length = dictLength; + if(z==null || z.istate == null|| z.istate.mode != DICT0) + return Z_STREAM_ERROR; + + if(z._adler.adler32(1L, dictionary, 0, dictLength)!=z.adler){ + return Z_DATA_ERROR; + } + + z.adler = z._adler.adler32(0, null, 0, 0); + + if(length >= (1< + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/InflateCodes.cs b/Renci.SshNet/Compression/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/InflateTree.cs b/Renci.SshNet/Compression/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i>7)]); + } + + internal short[] dyn_tree; // the dynamic tree + internal int max_code; // largest code with non zero frequency + internal StaticTree stat_desc; // the corresponding static tree + + // Compute the optimal bit lengths for a tree and update the total bit length + // for the current block. + // IN assertion: the fields freq and dad are set, heap[heap_max] and + // above are the tree nodes sorted by increasing frequency. + // OUT assertions: the field len is set to the optimal bit length, the + // array bl_count contains the frequencies for each bit length. + // The length opt_len is updated; static_len is also updated if stree is + // not null. + internal void gen_bitlen(Deflate s){ + short[] tree = dyn_tree; + short[] stree = stat_desc.static_tree; + int[] extra = stat_desc.extra_bits; + int based = stat_desc.extra_base; + int max_length = stat_desc.max_length; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max]*2+1] = 0; // root of the heap + + for(h=s.heap_max+1; h max_length){ bits = max_length; overflow++; } + tree[n*2+1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > max_code) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n-based]; + f = tree[n*2]; + s.opt_len += f * (bits + xbits); + if (stree!=null) s.static_len += f * (stree[n*2+1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do { + bits = max_length-1; + while(s.bl_count[bits]==0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits+1]+=2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) { + n = s.bl_count[bits]; + while (n != 0) { + m = s.heap[--h]; + if (m > max_code) continue; + if (tree[m*2+1] != bits) { + s.opt_len += (int)(((long)bits - (long)tree[m*2+1])*(long)tree[m*2]); + tree[m*2+1] = (short)bits; + } + n--; + } + } + } + + // Construct one Huffman tree and assigns the code bit strings and lengths. + // Update the total bit length for the current block. + // IN assertion: the field freq is set for all tree elements. + // OUT assertions: the fields len and code are set to the optimal bit length + // and corresponding code. The length opt_len is updated; static_len is + // also updated if stree is not null. The field max_code is set. + internal void build_tree(Deflate s){ + short[] tree=dyn_tree; + short[] stree=stat_desc.static_tree; + int elems=stat_desc.elems; + int n, m; // iterate over heap elements + int max_code=-1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for(n=0; n=1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node=elems; // next internal node of the tree + do{ + // n = node of least frequency + n=s.heap[1]; + s.heap[1]=s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m=s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node*2] = (short)(tree[n*2] + tree[m*2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n],s.depth[m])+1); + tree[n*2+1] = tree[m*2+1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while(s.heap_len>=2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + gen_bitlen(s); + + // The field len is now set, we can generate the bit codes + gen_codes(tree, max_code, s.bl_count); + } + + // Generate the codes for a given tree and bit counts (which need not be + // optimal). + // IN assertion: the array bl_count contains the bit length statistics for + // the given tree and the field len is set for all tree elements. + // OUT assertion: the field code is set for all tree elements of non + // zero code length. + internal static void gen_codes(short[] tree, // the tree to decorate + int max_code, // largest code with non zero frequency + short[] bl_count // number of codes at each bit length + ){ + short[] next_code=new short[MAX_BITS+1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (short)((code + bl_count[bits-1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1<>=1; + res<<=1; + } + while(--len>0); + return res>>1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/Adler32.cs b/Renci.SshNet/Compression/Version.5/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/CompressionMode.cs b/Renci.SshNet/Compression/Version.5/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/Version.5/Compressor.cs b/Renci.SshNet/Compression/Version.5/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/Version.5/Deflate.cs b/Renci.SshNet/Compression/Version.5/Deflate.cs new file mode 100644 index 0000000..4779218 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/Deflate.cs @@ -0,0 +1,2400 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// pointer back to this zlib stream + /// + //private ZStream _stream; + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + internal ZLibStatus deflateInit(ZStream strm, CompressionLevel level, int bits) + { + return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(ZStream strm, CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(strm); + } + + private ZLibStatus deflateReset(ZStream strm) + { + strm.total_in = strm.total_out = 0; + strm.msg = null; // + strm.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + strm.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(ZStream strm, CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + strm.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(ZStream strm, byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + strm.adler = Adler32.adler32(strm.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + //public ZLibStatus deflate(ZStream strm, FlushType flush) + //{ + // FlushType old_flush; + + // if (flush > FlushType.Z_FINISH || flush < 0) + // { + // return ZLibStatus.Z_STREAM_ERROR; + // } + + // if (strm.next_out == null || + // (strm.next_in == null && strm.avail_in != 0) || + // (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + // return ZLibStatus.Z_STREAM_ERROR; + // } + // if (strm.avail_out == 0) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // this._stream = strm; // just in case + // old_flush = last_flush; + // last_flush = flush; + + // // Write the zlib header + // if (_status == INIT_STATE) + // { + // int header = (Z_DEFLATED + ((_windowBits - 8) << 4)) << 8; + // int level_flags = (((int)level - 1) & 0xff) >> 1; + + // if (level_flags > 3) level_flags = 3; + // header |= (level_flags << 6); + // if (_startInsertString != 0) header |= PRESET_DICT; + // header += 31 - (header % 31); + + // _status = BUSY_STATE; + // putShortMSB(header); + + + // // Save the adler32 of the preset dictionary: + // if (_startInsertString != 0) + // { + // putShortMSB((int)(strm.adler >> 16)); + // putShortMSB((int)(strm.adler & 0xffff)); + // } + // strm.adler = Adler32.adler32(0, null, 0, 0); + // } + + // // Flush as much pending output as possible + // if (pending != 0) + // { + // strm.flush_pending(); + // if (strm.avail_out == 0) + // { + // //System.out.println(" avail_out==0"); + // // Since avail_out is 0, deflate will be called again with + // // more output space, but possibly with both pending and + // // avail_in equal to zero. There won't be anything to do, + // // but this is not an error situation so make sure we + // // return OK instead of BUF_ERROR at next call of deflate: + // last_flush = (FlushType)(-1); + // return ZLibStatus.Z_OK; + // } + + // // Make sure there is something to do and avoid duplicate consecutive + // // flushes. For repeated and useless calls with FlushType.Z_FINISH, we keep + // // returning ZLibStatus.Z_STREAM_END instead of Z_BUFF_ERROR. + // } + // else if (strm.avail_in == 0 && flush <= old_flush && + // flush != FlushType.Z_FINISH) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // // User must not provide more input after the first FINISH: + // if (_status == FINISH_STATE && strm.avail_in != 0) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // // Start a new block or continue the current one. + // if (strm.avail_in != 0 || _validBytesAhead != 0 || + // (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + // { + // int bstate = -1; + // switch (config_table[level].Function) + // { + // case DefalteFlavor.STORED: + // bstate = deflate_stored(flush); + // break; + // case DefalteFlavor.FAST: + // bstate = deflate_fast(flush); + // break; + // case DefalteFlavor.SLOW: + // bstate = deflate_slow(flush); + // break; + // default: + // break; + // } + + // if (bstate == FinishStarted || bstate == FinishDone) + // { + // _status = FINISH_STATE; + // } + // if (bstate == NeedMore || bstate == FinishStarted) + // { + // if (strm.avail_out == 0) + // { + // last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + // } + // return ZLibStatus.Z_OK; + // // If flush != FlushType.Z_NO_FLUSH && avail_out == 0, the next call + // // of deflate should use the same flush parameter to make sure + // // that the flush is complete. So we don't have to output an + // // empty block here, this will be done at next call. This also + // // ensures that for a very small output buffer, we emit at most + // // one empty block. + // } + + // if (bstate == BlockDone) + // { + // if (flush == FlushType.Z_PARTIAL_FLUSH) + // { + // _tr_align(); + // } + // else + // { // FULL_FLUSH or SYNC_FLUSH + // _tr_stored_block(0, 0, false); + // // For a full flush, this empty block will be recognized + // // as a special marker by inflate_sync(). + // if (flush == FlushType.Z_FULL_FLUSH) + // { + // //state.head[s.hash_size-1]=0; + // for (int i = 0; i < _hashSize/*-1*/; i++) // forget history + // _head[i] = 0; + // } + // } + // strm.flush_pending(); + // if (strm.avail_out == 0) + // { + // last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + // return ZLibStatus.Z_OK; + // } + // } + // } + + // if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + // if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // // Write the zlib trailer (adler32) + // putShortMSB((int)(strm.adler >> 16)); + // putShortMSB((int)(strm.adler & 0xffff)); + // strm.flush_pending(); + + // // If avail_out is zero, the application will call deflate again + // // to flush the rest. + // noheader = -1; // write the trailer only once! + // return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + //} + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + internal ZLibStatus deflate(ZStream strm, FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (strm.avail_out == 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(strm.adler >> 16)); + putShortMSB((int)(strm.adler & 0xffff)); + } + strm.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (strm.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (strm.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && strm.avail_in != 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (strm.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(strm.adler >> 16)); + putShortMSB((int)(strm.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + public ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit(level, bits, false); + } + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(this, level, nowrap ? -bits : bits); + } + + public ZLibStatus deflate(FlushType flush) + { + return this.deflate(this, flush); + } + public ZLibStatus deflateParams(CompressionLevel level, CompressionStrategy strategy) + { + return this.deflateParams(this, level, strategy); + } + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.deflateSetDictionary(this, dictionary, dictLength); + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + // TODO: Delete this + public ZLibStatus inflateInit() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflateInit(bool nowrap) + { + throw new NotImplementedException(); + } + + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/Inflate.cs b/Renci.SshNet/Compression/Version.5/Inflate.cs new file mode 100644 index 0000000..60658d0 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/Inflate.cs @@ -0,0 +1,518 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd(ZStream z) + { + if (_blocks != null) + _blocks.free(z); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(ZStream z, int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(z); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(z, + this._nowrap != 0 ? null : this, + 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync(ZStream z) + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(this, nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(this); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(this); + } + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/InflateBlocks.cs b/Renci.SshNet/Compression/Version.5/InflateBlocks.cs new file mode 100644 index 0000000..273389f --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/InflateCodes.cs b/Renci.SshNet/Compression/Version.5/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/InflateTree.cs b/Renci.SshNet/Compression/Version.5/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/Tree.cs b/Renci.SshNet/Compression/Version.5/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/ZInputStream.cs b/Renci.SshNet/Compression/Version.5/ZInputStream.cs new file mode 100644 index 0000000..e7ca814 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/ZInputStream.cs @@ -0,0 +1,205 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(); + this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(); + this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + this._compressor.next_out = buffer; + this._compressor.next_out_index = offset; + this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + this._compressor.next_out = _buffer; + this._compressor.next_out_index = 0; + this._compressor.avail_out = _buffer.Length; + + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, this._compressor.msg)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/Version.5/ZStream.cs b/Renci.SshNet/Compression/Version.5/ZStream.cs new file mode 100644 index 0000000..1162e00 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/ZStream.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + //internal Deflate dstate; + //internal Inflate istate; + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler; + + public void free() + { + next_in = null; + next_out = null; + msg = null; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/Zlib.cs b/Renci.SshNet/Compression/Version.5/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/ZlibOpenSsh.cs b/Renci.SshNet/Compression/Version.5/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.5/ZlibStream.cs b/Renci.SshNet/Compression/Version.5/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/Version.5/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/Version.6/Adler32.cs b/Renci.SshNet/Compression/Version.6/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/CompressionMode.cs b/Renci.SshNet/Compression/Version.6/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/Version.6/Compressor.cs b/Renci.SshNet/Compression/Version.6/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/Version.6/Deflate.cs b/Renci.SshNet/Compression/Version.6/Deflate.cs new file mode 100644 index 0000000..fb8fb30 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/Deflate.cs @@ -0,0 +1,2401 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// pointer back to this zlib stream + /// + //private ZStream _stream; + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + internal ZLibStatus deflateInit(ZStream strm, CompressionLevel level, int bits) + { + return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(ZStream strm, CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(strm); + } + + private ZLibStatus deflateReset(ZStream strm) + { + strm.total_in = strm.total_out = 0; + strm.msg = null; // + strm.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + strm.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(ZStream strm, CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + strm.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(ZStream strm, byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + strm.adler = Adler32.adler32(strm.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + //public ZLibStatus deflate(ZStream strm, FlushType flush) + //{ + // FlushType old_flush; + + // if (flush > FlushType.Z_FINISH || flush < 0) + // { + // return ZLibStatus.Z_STREAM_ERROR; + // } + + // if (strm.next_out == null || + // (strm.next_in == null && strm.avail_in != 0) || + // (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + // return ZLibStatus.Z_STREAM_ERROR; + // } + // if (strm.avail_out == 0) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // this._stream = strm; // just in case + // old_flush = last_flush; + // last_flush = flush; + + // // Write the zlib header + // if (_status == INIT_STATE) + // { + // int header = (Z_DEFLATED + ((_windowBits - 8) << 4)) << 8; + // int level_flags = (((int)level - 1) & 0xff) >> 1; + + // if (level_flags > 3) level_flags = 3; + // header |= (level_flags << 6); + // if (_startInsertString != 0) header |= PRESET_DICT; + // header += 31 - (header % 31); + + // _status = BUSY_STATE; + // putShortMSB(header); + + + // // Save the adler32 of the preset dictionary: + // if (_startInsertString != 0) + // { + // putShortMSB((int)(strm.adler >> 16)); + // putShortMSB((int)(strm.adler & 0xffff)); + // } + // strm.adler = Adler32.adler32(0, null, 0, 0); + // } + + // // Flush as much pending output as possible + // if (pending != 0) + // { + // strm.flush_pending(); + // if (strm.avail_out == 0) + // { + // //System.out.println(" avail_out==0"); + // // Since avail_out is 0, deflate will be called again with + // // more output space, but possibly with both pending and + // // avail_in equal to zero. There won't be anything to do, + // // but this is not an error situation so make sure we + // // return OK instead of BUF_ERROR at next call of deflate: + // last_flush = (FlushType)(-1); + // return ZLibStatus.Z_OK; + // } + + // // Make sure there is something to do and avoid duplicate consecutive + // // flushes. For repeated and useless calls with FlushType.Z_FINISH, we keep + // // returning ZLibStatus.Z_STREAM_END instead of Z_BUFF_ERROR. + // } + // else if (strm.avail_in == 0 && flush <= old_flush && + // flush != FlushType.Z_FINISH) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // // User must not provide more input after the first FINISH: + // if (_status == FINISH_STATE && strm.avail_in != 0) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // // Start a new block or continue the current one. + // if (strm.avail_in != 0 || _validBytesAhead != 0 || + // (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + // { + // int bstate = -1; + // switch (config_table[level].Function) + // { + // case DefalteFlavor.STORED: + // bstate = deflate_stored(flush); + // break; + // case DefalteFlavor.FAST: + // bstate = deflate_fast(flush); + // break; + // case DefalteFlavor.SLOW: + // bstate = deflate_slow(flush); + // break; + // default: + // break; + // } + + // if (bstate == FinishStarted || bstate == FinishDone) + // { + // _status = FINISH_STATE; + // } + // if (bstate == NeedMore || bstate == FinishStarted) + // { + // if (strm.avail_out == 0) + // { + // last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + // } + // return ZLibStatus.Z_OK; + // // If flush != FlushType.Z_NO_FLUSH && avail_out == 0, the next call + // // of deflate should use the same flush parameter to make sure + // // that the flush is complete. So we don't have to output an + // // empty block here, this will be done at next call. This also + // // ensures that for a very small output buffer, we emit at most + // // one empty block. + // } + + // if (bstate == BlockDone) + // { + // if (flush == FlushType.Z_PARTIAL_FLUSH) + // { + // _tr_align(); + // } + // else + // { // FULL_FLUSH or SYNC_FLUSH + // _tr_stored_block(0, 0, false); + // // For a full flush, this empty block will be recognized + // // as a special marker by inflate_sync(). + // if (flush == FlushType.Z_FULL_FLUSH) + // { + // //state.head[s.hash_size-1]=0; + // for (int i = 0; i < _hashSize/*-1*/; i++) // forget history + // _head[i] = 0; + // } + // } + // strm.flush_pending(); + // if (strm.avail_out == 0) + // { + // last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + // return ZLibStatus.Z_OK; + // } + // } + // } + + // if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + // if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // // Write the zlib trailer (adler32) + // putShortMSB((int)(strm.adler >> 16)); + // putShortMSB((int)(strm.adler & 0xffff)); + // strm.flush_pending(); + + // // If avail_out is zero, the application will call deflate again + // // to flush the rest. + // noheader = -1; // write the trailer only once! + // return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + //} + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + internal ZLibStatus deflate(ZStream strm, FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (strm.avail_out == 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(strm.adler >> 16)); + putShortMSB((int)(strm.adler & 0xffff)); + } + strm.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (strm.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (strm.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && strm.avail_in != 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (strm.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(strm.adler >> 16)); + putShortMSB((int)(strm.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + public ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit(level, bits, false); + } + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(this, level, nowrap ? -bits : bits); + } + + public ZLibStatus deflate(FlushType flush) + { + return this.deflate(this, flush); + } + public ZLibStatus deflateParams(CompressionLevel level, CompressionStrategy strategy) + { + return this.deflateParams(this, level, strategy); + } + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.deflateSetDictionary(this, dictionary, dictLength); + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/Inflate.cs b/Renci.SshNet/Compression/Version.6/Inflate.cs new file mode 100644 index 0000000..8019fd0 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/Inflate.cs @@ -0,0 +1,517 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd(ZStream z) + { + if (_blocks != null) + _blocks.free(z); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(ZStream z, int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(z); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(z, + this._nowrap != 0 ? null : this, + 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync(ZStream z) + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(this, nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(this); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(this); + } + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/InflateBlocks.cs b/Renci.SshNet/Compression/Version.6/InflateBlocks.cs new file mode 100644 index 0000000..273389f --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/InflateCodes.cs b/Renci.SshNet/Compression/Version.6/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/InflateTree.cs b/Renci.SshNet/Compression/Version.6/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/Tree.cs b/Renci.SshNet/Compression/Version.6/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/ZInputStream.cs b/Renci.SshNet/Compression/Version.6/ZInputStream.cs new file mode 100644 index 0000000..2e5aaa6 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/ZInputStream.cs @@ -0,0 +1,205 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + //this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + //this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + this._compressor.next_out = buffer; + this._compressor.next_out_index = offset; + this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + this._compressor.next_out = _buffer; + this._compressor.next_out_index = 0; + this._compressor.avail_out = _buffer.Length; + + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, this._compressor.msg)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/Version.6/ZStream.cs b/Renci.SshNet/Compression/Version.6/ZStream.cs new file mode 100644 index 0000000..6269735 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/ZStream.cs @@ -0,0 +1,73 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler; + + public void free() + { + next_in = null; + next_out = null; + msg = null; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/Zlib.cs b/Renci.SshNet/Compression/Version.6/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/ZlibOpenSsh.cs b/Renci.SshNet/Compression/Version.6/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.6/ZlibStream.cs b/Renci.SshNet/Compression/Version.6/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/Version.6/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/Version.7/Adler32.cs b/Renci.SshNet/Compression/Version.7/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/CompressionMode.cs b/Renci.SshNet/Compression/Version.7/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/Version.7/Compressor.cs b/Renci.SshNet/Compression/Version.7/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/Version.7/Deflate.cs b/Renci.SshNet/Compression/Version.7/Deflate.cs new file mode 100644 index 0000000..ea08bfa --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/Deflate.cs @@ -0,0 +1,2394 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(level, nowrap ? -bits : bits); + } + + internal ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit2(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + base.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(); + } + + private ZLibStatus deflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; // + base.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + base.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(ZStream strm, CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + strm.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(ZStream strm, byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + strm.adler = Adler32.adler32(strm.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + //public ZLibStatus deflate(ZStream strm, FlushType flush) + //{ + // FlushType old_flush; + + // if (flush > FlushType.Z_FINISH || flush < 0) + // { + // return ZLibStatus.Z_STREAM_ERROR; + // } + + // if (strm.next_out == null || + // (strm.next_in == null && strm.avail_in != 0) || + // (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + // return ZLibStatus.Z_STREAM_ERROR; + // } + // if (strm.avail_out == 0) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // this._stream = strm; // just in case + // old_flush = last_flush; + // last_flush = flush; + + // // Write the zlib header + // if (_status == INIT_STATE) + // { + // int header = (Z_DEFLATED + ((_windowBits - 8) << 4)) << 8; + // int level_flags = (((int)level - 1) & 0xff) >> 1; + + // if (level_flags > 3) level_flags = 3; + // header |= (level_flags << 6); + // if (_startInsertString != 0) header |= PRESET_DICT; + // header += 31 - (header % 31); + + // _status = BUSY_STATE; + // putShortMSB(header); + + + // // Save the adler32 of the preset dictionary: + // if (_startInsertString != 0) + // { + // putShortMSB((int)(strm.adler >> 16)); + // putShortMSB((int)(strm.adler & 0xffff)); + // } + // strm.adler = Adler32.adler32(0, null, 0, 0); + // } + + // // Flush as much pending output as possible + // if (pending != 0) + // { + // strm.flush_pending(); + // if (strm.avail_out == 0) + // { + // //System.out.println(" avail_out==0"); + // // Since avail_out is 0, deflate will be called again with + // // more output space, but possibly with both pending and + // // avail_in equal to zero. There won't be anything to do, + // // but this is not an error situation so make sure we + // // return OK instead of BUF_ERROR at next call of deflate: + // last_flush = (FlushType)(-1); + // return ZLibStatus.Z_OK; + // } + + // // Make sure there is something to do and avoid duplicate consecutive + // // flushes. For repeated and useless calls with FlushType.Z_FINISH, we keep + // // returning ZLibStatus.Z_STREAM_END instead of Z_BUFF_ERROR. + // } + // else if (strm.avail_in == 0 && flush <= old_flush && + // flush != FlushType.Z_FINISH) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // // User must not provide more input after the first FINISH: + // if (_status == FINISH_STATE && strm.avail_in != 0) + // { + // strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + // return ZLibStatus.Z_BUF_ERROR; + // } + + // // Start a new block or continue the current one. + // if (strm.avail_in != 0 || _validBytesAhead != 0 || + // (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + // { + // int bstate = -1; + // switch (config_table[level].Function) + // { + // case DefalteFlavor.STORED: + // bstate = deflate_stored(flush); + // break; + // case DefalteFlavor.FAST: + // bstate = deflate_fast(flush); + // break; + // case DefalteFlavor.SLOW: + // bstate = deflate_slow(flush); + // break; + // default: + // break; + // } + + // if (bstate == FinishStarted || bstate == FinishDone) + // { + // _status = FINISH_STATE; + // } + // if (bstate == NeedMore || bstate == FinishStarted) + // { + // if (strm.avail_out == 0) + // { + // last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + // } + // return ZLibStatus.Z_OK; + // // If flush != FlushType.Z_NO_FLUSH && avail_out == 0, the next call + // // of deflate should use the same flush parameter to make sure + // // that the flush is complete. So we don't have to output an + // // empty block here, this will be done at next call. This also + // // ensures that for a very small output buffer, we emit at most + // // one empty block. + // } + + // if (bstate == BlockDone) + // { + // if (flush == FlushType.Z_PARTIAL_FLUSH) + // { + // _tr_align(); + // } + // else + // { // FULL_FLUSH or SYNC_FLUSH + // _tr_stored_block(0, 0, false); + // // For a full flush, this empty block will be recognized + // // as a special marker by inflate_sync(). + // if (flush == FlushType.Z_FULL_FLUSH) + // { + // //state.head[s.hash_size-1]=0; + // for (int i = 0; i < _hashSize/*-1*/; i++) // forget history + // _head[i] = 0; + // } + // } + // strm.flush_pending(); + // if (strm.avail_out == 0) + // { + // last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + // return ZLibStatus.Z_OK; + // } + // } + // } + + // if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + // if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // // Write the zlib trailer (adler32) + // putShortMSB((int)(strm.adler >> 16)); + // putShortMSB((int)(strm.adler & 0xffff)); + // strm.flush_pending(); + + // // If avail_out is zero, the application will call deflate again + // // to flush the rest. + // noheader = -1; // write the trailer only once! + // return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + //} + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + internal ZLibStatus deflate(ZStream strm, FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (strm.avail_out == 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(strm.adler >> 16)); + putShortMSB((int)(strm.adler & 0xffff)); + } + strm.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (strm.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (strm.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && strm.avail_in != 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (strm.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(strm.adler >> 16)); + putShortMSB((int)(strm.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + public ZLibStatus deflate(FlushType flush) + { + return this.deflate(this, flush); + } + public ZLibStatus deflateParams(CompressionLevel level, CompressionStrategy strategy) + { + return this.deflateParams(this, level, strategy); + } + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.deflateSetDictionary(this, dictionary, dictLength); + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/Inflate.cs b/Renci.SshNet/Compression/Version.7/Inflate.cs new file mode 100644 index 0000000..8019fd0 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/Inflate.cs @@ -0,0 +1,517 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd(ZStream z) + { + if (_blocks != null) + _blocks.free(z); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(ZStream z, int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(z); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(z, + this._nowrap != 0 ? null : this, + 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync(ZStream z) + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(this, nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(this); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(this); + } + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/InflateBlocks.cs b/Renci.SshNet/Compression/Version.7/InflateBlocks.cs new file mode 100644 index 0000000..273389f --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/InflateCodes.cs b/Renci.SshNet/Compression/Version.7/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/InflateTree.cs b/Renci.SshNet/Compression/Version.7/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/Tree.cs b/Renci.SshNet/Compression/Version.7/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/ZInputStream.cs b/Renci.SshNet/Compression/Version.7/ZInputStream.cs new file mode 100644 index 0000000..2e5aaa6 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/ZInputStream.cs @@ -0,0 +1,205 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + //this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + //this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + this._compressor.next_out = buffer; + this._compressor.next_out_index = offset; + this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + this._compressor.next_out = _buffer; + this._compressor.next_out_index = 0; + this._compressor.avail_out = _buffer.Length; + + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, this._compressor.msg)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/Version.7/ZStream.cs b/Renci.SshNet/Compression/Version.7/ZStream.cs new file mode 100644 index 0000000..6269735 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/ZStream.cs @@ -0,0 +1,73 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler; + + public void free() + { + next_in = null; + next_out = null; + msg = null; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/Zlib.cs b/Renci.SshNet/Compression/Version.7/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/ZlibOpenSsh.cs b/Renci.SshNet/Compression/Version.7/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.7/ZlibStream.cs b/Renci.SshNet/Compression/Version.7/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/Version.7/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/Version.8/Adler32.cs b/Renci.SshNet/Compression/Version.8/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/CompressionMode.cs b/Renci.SshNet/Compression/Version.8/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/Version.8/Compressor.cs b/Renci.SshNet/Compression/Version.8/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/Version.8/Deflate.cs b/Renci.SshNet/Compression/Version.8/Deflate.cs new file mode 100644 index 0000000..14e76a2 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/Deflate.cs @@ -0,0 +1,2217 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(level, nowrap ? -bits : bits); + } + + internal ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit2(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + base.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(); + } + + private ZLibStatus deflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; // + base.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + base.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + base.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + base.adler = Adler32.adler32(base.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + public ZLibStatus deflate(FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (base.next_out == null || + (base.next_in == null && base.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (base.avail_out == 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + } + base.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (base.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (base.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && base.avail_in != 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (base.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/Inflate.cs b/Renci.SshNet/Compression/Version.8/Inflate.cs new file mode 100644 index 0000000..cb31730 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/Inflate.cs @@ -0,0 +1,516 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd() + { + if (_blocks != null) + _blocks.free(this); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(this, this._nowrap != 0 ? null : this, 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(); + } + + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/InflateBlocks.cs b/Renci.SshNet/Compression/Version.8/InflateBlocks.cs new file mode 100644 index 0000000..273389f --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/InflateCodes.cs b/Renci.SshNet/Compression/Version.8/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/InflateTree.cs b/Renci.SshNet/Compression/Version.8/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/Tree.cs b/Renci.SshNet/Compression/Version.8/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/ZInputStream.cs b/Renci.SshNet/Compression/Version.8/ZInputStream.cs new file mode 100644 index 0000000..a491d99 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/ZInputStream.cs @@ -0,0 +1,202 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + //this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + //this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + this._compressor.next_out = buffer; + this._compressor.next_out_index = offset; + this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + //this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + this._compressor.next_out = _buffer; + this._compressor.next_out_index = 0; + this._compressor.avail_out = _buffer.Length; + + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, err)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/Version.8/ZStream.cs b/Renci.SshNet/Compression/Version.8/ZStream.cs new file mode 100644 index 0000000..1ee045e --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/ZStream.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public interface ICompressor + { + ZLibStatus deflate(FlushType flushType); + + ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + + ZLibStatus deflateEnd(); + + ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + + ZLibStatus inflateEnd(); + + ZLibStatus inflate(FlushType flushType); + + int avail_out { get; set; } + + int avail_in { get; set; } + + int next_out_index { get; set; } + + byte[] next_out { get; set; } + + byte[] next_in { get; set; } + + int next_in_index { get; set; } + } + + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler; + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/Zlib.cs b/Renci.SshNet/Compression/Version.8/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/ZlibOpenSsh.cs b/Renci.SshNet/Compression/Version.8/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Version.8/ZlibStream.cs b/Renci.SshNet/Compression/Version.8/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/Version.8/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/Working.1/CompressionMode.cs b/Renci.SshNet/Compression/Working.1/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/Working.1/Compressor.cs b/Renci.SshNet/Compression/Working.1/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/Working.1/Deflate.cs b/Renci.SshNet/Compression/Working.1/Deflate.cs new file mode 100644 index 0000000..fe7a62a --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/Deflate.cs @@ -0,0 +1,2185 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public sealed class Deflate + { + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + private const int MAX_MEM_LEVEL = 9; + + #region Private static declarations + + private static readonly byte[] _lengthCode ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] _baseLength = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] _baseDistance = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + + + /// + /// The extra bits for each length code + /// + private static int[] ExtraLBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + + /// + /// The extra bits for each distance code + /// + private static int[] ExtraDBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + + /// + /// The extra bits for each bit length code + /// + private static int[] ExtraBLBits = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 }; + + + private static byte[] BLOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + #endregion + + private const int MAX_WBITS = 15; // 32K LZ77 window + private const int DEF_MEM_LEVEL = 8; + + private const int STORED = 0; + private const int FAST = 1; + private const int SLOW = 2; + + private static readonly IDictionary _configurationTable; + + static Deflate() + { + //config_table = new Config[10]; + _configurationTable = new Dictionary(); + // TODO: Improve array initialization + // good lazy nice chain + _configurationTable[(CompressionLevel)0] = new Config(0, 0, 0, 0, STORED); + _configurationTable[(CompressionLevel)1] = new Config(4, 4, 8, 4, FAST); + _configurationTable[(CompressionLevel)2] = new Config(4, 5, 16, 8, FAST); + _configurationTable[(CompressionLevel)3] = new Config(4, 6, 32, 32, FAST); + + _configurationTable[(CompressionLevel)4] = new Config(4, 4, 16, 16, SLOW); + _configurationTable[(CompressionLevel)5] = new Config(8, 16, 32, 32, SLOW); + _configurationTable[(CompressionLevel)6] = new Config(8, 16, 128, 128, SLOW); + _configurationTable[(CompressionLevel)7] = new Config(8, 32, 128, 256, SLOW); + _configurationTable[(CompressionLevel)8] = new Config(32, 128, 258, 1024, SLOW); + _configurationTable[(CompressionLevel)9] = new Config(32, 258, 258, 4096, SLOW); + } + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // ZLibStatus.Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// ZLibStatus.Z_VERSION_ERROR (-6) + "" + }; + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.ExtraLBits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.ExtraDBits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.ExtraBLBits, 0, BL_CODES, MAX_BL_BITS); + + + /// + /// block not completed, need more input or more output + /// + private const int NeedMore = 0; + + /// + // The block flush performed + /// + private const int BlockDone = 1; + + /// + /// finish started, need only more output at next deflate + /// + private const int FinishStarted = 2; + + /// + /// finish done, accept no more input or output + /// + private const int FinishDone = 3; + + /// + /// preset dictionary flag in zlib header + /// + private const int PRESET_DICT = 0x20; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + /// + /// The deflate compression method + /// + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + /// + /// The repeat previous bit length 3-6 times (2 bits of repeat count) + /// + private const int REP_3_6 = 16; + + /// + /// repeat a zero length 3-10 times (3 bits of repeat count) + /// + private const int REPZ_3_10 = 17; + + /// + /// repeat a zero length 11-138 times (7 bits of repeat count) + /// + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + /// + /// pointer back to this zlib stream + /// + private ZStream strm; + + /// + /// as the name implies + /// + private int status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte method; + + /// + /// size of pending_buf + /// + private int pending_buf_size; + + /// + /// LZ77 window size (32K by default) + /// + private int w_size; + + /// + /// log2(w_size) (8..16) + /// + private int w_bits; + + /// + /// w_size - 1 + /// + private int w_mask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int window_size; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] prev; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] head; + + /// + /// hash index of string to be inserted + /// + private int ins_h; + + /// + /// number of elements in hash table + /// + private int hash_size; + + /// + /// log2(hash_size) + /// + private int hash_bits; + + /// + /// hash_size-1 + /// + private int hash_mask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int hash_shift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int block_start; + + /// + /// length of best match + /// + private int match_length; + + /// + /// previous match + /// + private int prev_match; + + /// + /// set if previous match exists + /// + private int match_available; + + /// + /// start of string to insert + /// + private int strstart; + + /// + /// start of matching string + /// + private int match_start; + + /// + /// number of valid bytes ahead in window + /// + private int lookahead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int prev_length; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int max_chain_length; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int max_lazy_match; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public ZLibStatus deflateInit(ZStream strm, CompressionLevel level, int bits) + { + return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, + CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + public ZLibStatus deflateEnd() + { + if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + head = null; + prev = null; + window = null; + // free + // dstate=null; + return status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(ZStream strm, CompressionLevel _level, CompressionStrategy _strategy) + { + var err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (_configurationTable[level].Function != _configurationTable[_level].Function && + strm.total_in != 0) + { + // Flush the last buffer: + err = strm.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + max_lazy_match = _configurationTable[level].MaxLazy; + good_match = _configurationTable[level].GoodLength; + nice_match = _configurationTable[level].NiceLength; + max_chain_length = _configurationTable[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(ZStream strm, byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + //strm.adler = strm.Adler32(strm.adler, dictionary, 0, dictLength); + strm.UpdateAdler(dictionary, 0, dictLength); + + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > w_size - MIN_LOOKAHEAD) + { + length = w_size - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, window, 0, length); + strstart = length; + block_start = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + prev[n & w_mask] = head[ins_h]; + head[ins_h] = (short)n; + } + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflate(ZStream strm, FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (strm.avail_out == 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + this.strm = strm; // just in case + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (status == INIT_STATE) + { + int header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; + int level_flags = (((int)level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (strstart != 0) + { + putShortMSB((int)(strm.Adler >> 16)); + putShortMSB((int)(strm.Adler & 0xffff)); + } + //strm.adler = strm.Adler32(0, null, 0, 0); + strm.UpdateAdler(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + strm.flush_pending(); + if (strm.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with FlushType.Z_FINISH, we keep + // returning ZLibStatus.Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (strm.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (status == FINISH_STATE && strm.avail_in != 0) + { + strm.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (strm.avail_in != 0 || lookahead != 0 || + (flush != FlushType.Z_NO_FLUSH && status != FINISH_STATE)) + { + int bstate = -1; + switch (_configurationTable[level].Function) + { + case STORED: + bstate = deflate_stored(flush); + break; + case FAST: + bstate = deflate_fast(flush); + break; + case SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != FlushType.Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < hash_size/*-1*/; i++) // forget history + head[i] = 0; + } + } + strm.flush_pending(); + if (strm.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(strm.Adler >> 16)); + putShortMSB((int)(strm.Adler & 0xffff)); + strm.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + private void lm_init() + { + window_size = 2 * w_size; + + head[hash_size - 1] = 0; + for (int i = 0; i < hash_size - 1; i++) + { + head[i] = 0; + } + + // Set the default configuration parameters: + max_lazy_match = Deflate._configurationTable[level].MaxLazy; + good_match = Deflate._configurationTable[level].GoodLength; + nice_match = Deflate._configurationTable[level].NiceLength; + max_chain_length = Deflate._configurationTable[level].MaxChain; + + strstart = 0; + block_start = 0; + lookahead = 0; + match_length = prev_length = MIN_MATCH - 1; + match_available = 0; + ins_h = 0; + } + + /// + /// Initialize the tree data structures for a new zlib stream. + /// + private void tr_init() + { + + //l_desc.dyn_tree = dyn_ltree; + //l_desc.stat_desc = Deflate.static_l_desc; + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + //d_desc.dyn_tree = dyn_dtree; + //d_desc.stat_desc = Deflate.static_d_desc; + d_desc.Init(dyn_ltree, Deflate.static_l_desc); + + //bl_desc.dyn_tree = bl_tree; + //bl_desc.stat_desc = Deflate.static_bl_desc; + bl_desc.Init(dyn_ltree, Deflate.static_l_desc); + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + /// + /// Restore the heap property by moving down the tree starting at node k, + /// exchanging a node with the smallest of its two sons if necessary, stopping + /// when the heap property is re-established (each father smaller than its + /// two sons). + /// + /// The tree to restore. + /// The node to move down. + public void pqdownheap(short[] tree, int k) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + /// + /// Scan a literal or distance tree to determine the frequencies of the codes + /// in the bit length tree. + /// + /// The tree to be scanned. + /// The largest code of non zero frequency. + private void scan_tree(short[] tree, int max_code) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + /// + /// Construct the Huffman tree for the bit lengths and return the index in + /// bl_order of the last bit length code to send. + /// + /// + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.BLOrder[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + /// + /// Send the header for a block using dynamic Huffman trees: the counts, the + /// lengths of the bit length codes, the literal tree and the distance tree. + /// + /// The lcodes. + /// The dcodes. + /// The blcodes. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.BLOrder[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + /// + /// Send a literal or distance tree in compressed form, using the codes in + /// bl_tree. + /// + /// The tree to be sent. + /// The largest code of non zero frequency. + private void send_tree(short[] tree, int max_code) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + /// + /// Output a byte on the stream. + /// + /// The p. + /// The start. + /// The len. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + /// + /// Send one empty static block to give enough lookahead for inflate. + /// This takes 10 bits, of which 7 may remain in the bit buffer. + /// The current inflate code requires 9 bits of lookahead. If the + /// last two codes for the previous block (real code plus EOB) were coded + /// on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + /// the last real code. In this case we send two empty static blocks instead + /// of one. (There are no problems if the previous block is stored or fixed.) + /// To simplify the code, we assume the worst case of last real code encoded + /// on one bit only. + /// + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + /// + /// Save the match info and tally the frequency counts. Return true if + /// the current block must be flushed. + /// + /// The distance of matched string. + /// The match length-MIN_MATCH or unmatched char (if dist==0). + /// + private bool _tr_tally(int dist, int lc) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(_lengthCode[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.ExtraDBits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + /// + /// Send the block data compressed using the given Huffman trees + /// + /// The ltree. + /// The dtree. + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = _lengthCode[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.ExtraLBits[code]; + if (extra != 0) + { + lc -= _baseLength[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.ExtraDBits[code]; + if (extra != 0) + { + dist -= _baseDistance[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + /// + /// Set the data type to ASCII or BINARY, using a crude approximation: + /// binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + /// IN assertion: the fields freq of dyn_ltree are set and the total of all + /// frequencies does not exceed 64K (to fit in an int on 16 bit machines). + /// + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + _dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + /// + /// Flush the bit buffer, keeping at most 7 bits in it. + /// + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + /// + /// Flush the bit buffer and align the output on a byte boundary + /// + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + /// + /// Copy a stored block, storing first the length and its + /// one's complement if requested. + /// + /// The input data. + /// The length. + /// if set to true block header must be written. + private void copy_block(int buf, int len, bool header) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(block_start >= 0 ? block_start : -1, + strstart - block_start, + eof); + block_start = strstart; + strm.flush_pending(); + } + + /// + /// Copy without compression as much as possible from the input stream, return + /// the current block state. + /// This function does not insert new strings in the dictionary since + /// uncompressible data is probably not useful. This function is used + /// only for the level=0 compression option. + /// + /// The flush. + /// + private int deflate_stored(FlushType flush) + { + // TODO: this function should be optimized to avoid extra copying from window to pending_buf. + + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > pending_buf_size - 5) + { + max_block_size = pending_buf_size - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (lookahead <= 1) + { + fill_window(); + if (lookahead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (lookahead == 0) break; // flush the current block + } + + strstart += lookahead; + lookahead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = block_start + max_block_size; + if (strstart == 0 || strstart >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = (int)(strstart - max_start); + strstart = (int)max_start; + + flush_block_only(false); + if (strm.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (strstart - block_start >= w_size - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (strm.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (strm.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + /// + /// Send a stored block + /// + /// The input block. + /// The length of input block. + /// if set to true last block for a file. + private void _tr_stored_block(int buf, int stored_len, bool eof) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + /// + /// Determine the best encoding for the current block: dynamic trees, static + /// trees or store, and output the encoded block to the zip file. + /// + /// The input block, or NULL if too old. + /// The length of input block. + /// if set to true the last block for a file. + private void _tr_flush_block(int buf, int stored_len, bool eof) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (_dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + /// + /// Fill the window when the lookahead becomes insufficient. + /// Updates strstart and lookahead. + /// + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (window_size - lookahead - strstart); + + // Deal with !@#$% 64K limit: + if (more == 0 && strstart == 0 && lookahead == 0) + { + more = w_size; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) + { + System.Array.Copy(window, w_size, window, 0, w_size); + match_start -= w_size; + strstart -= w_size; // we now have strstart >= MAX_DIST + block_start -= w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p = n; + do + { + m = (head[--p] & 0xffff); + head[p] = (short)(m >= w_size ? (m - w_size) : 0); + } + while (--n != 0); + + n = w_size; + p = n; + do + { + m = (prev[--p] & 0xffff); + prev[p] = (short)(m >= w_size ? (m - w_size) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += w_size; + } + + if (strm.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = strm.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if (lookahead >= MIN_MATCH) + { + ins_h = window[strstart] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (lookahead < MIN_LOOKAHEAD && strm.avail_in != 0); + } + + /// + /// Compress as much as possible from the input stream, return the current + /// block state. + /// This function does not perform lazy evaluation of matches and inserts + /// new strings in the dictionary only for unmatched strings or for short + /// matches. It is used only for the fast compression options. + /// + /// The flush. + /// + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (lookahead < MIN_LOOKAHEAD) + { + fill_window(); + if (lookahead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (lookahead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (lookahead >= MIN_MATCH) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short)strstart; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + match_length = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (match_length >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (match_length <= max_lazy_match && + lookahead >= MIN_MATCH) + { + match_length--; // string at strstart already in hash table + do + { + strstart++; + + ins_h = ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short)strstart; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--match_length != 0); + strstart++; + } + else + { + strstart += match_length; + match_length = 0; + ins_h = window[strstart] & 0xff; + + ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, window[strstart] & 0xff); + lookahead--; + strstart++; + } + if (bflush) + { + + flush_block_only(false); + if (strm.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (strm.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + /// + /// Same as above, but achieves better compression. We use a lazy + /// evaluation for matches: a match is finally adopted only if there is + /// no better match at the next window position. + /// + /// The flush. + /// + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (lookahead < MIN_LOOKAHEAD) + { + fill_window(); + if (lookahead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (lookahead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (lookahead >= MIN_MATCH) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short)strstart; + } + + // Find the longest match, discarding those <= prev_length. + prev_length = match_length; prev_match = match_start; + match_length = MIN_MATCH - 1; + + if (hash_head != 0 && prev_length < max_lazy_match && + ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + match_length = longest_match(hash_head); + } + // longest_match() sets match_start + + if (match_length <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (match_length == MIN_MATCH && + strstart - match_start > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (prev_length >= MIN_MATCH && match_length <= prev_length) + { + int max_insert = strstart + lookahead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= prev_length - 1; + prev_length -= 2; + do + { + if (++strstart <= max_insert) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short)strstart; + } + } + while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH - 1; + strstart++; + + if (bflush) + { + flush_block_only(false); + if (strm.avail_out == 0) return NeedMore; + } + } + else if (match_available != 0) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + strstart++; + lookahead--; + if (strm.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + match_available = 1; + strstart++; + lookahead--; + } + } + + if (match_available != 0) + { + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + match_available = 0; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (strm.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = max_chain_length; // max hash chain length + int scan = strstart; // current string + int match; // matched string + int len; // length of current match + int best_len = prev_length; // best match length so far + int limit = strstart > (w_size - MIN_LOOKAHEAD) ? + strstart - (w_size - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = w_mask; + + int strend = strstart + MAX_MATCH; + byte scan_end1 = window[scan + best_len - 1]; + byte scan_end = window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > lookahead) nice_match = lookahead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (window[match + best_len] != scan_end || + window[match + best_len - 1] != scan_end1 || + window[match] != window[scan] || + window[++match] != window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = window[scan + best_len - 1]; + scan_end = window[scan + best_len]; + } + + } while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= lookahead) return best_len; + return lookahead; + } + + private ZLibStatus deflateInit2(ZStream strm, CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return ZLibStatus.Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + strm.dstate = (Deflate)this; + + this.noheader = noheader; + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH); + + window = new byte[w_size * 2]; + prev = new short[w_size]; + head = new short[hash_size]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + pending_buf_size = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this.method = (byte)method; + + return deflateReset(strm); + } + + private ZLibStatus deflateReset(ZStream strm) + { + strm.total_in = strm.total_out = 0; + strm.msg = null; // + //strm.data_type = Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + //strm.adler = strm.Adler32(0, null, 0, 0); + strm.UpdateAdler(0L, null, 0, 0); + + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal int Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, int function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/InfBlocks.cs b/Renci.SshNet/Compression/Working.1/InfBlocks.cs new file mode 100644 index 0000000..190c4ef --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/InfBlocks.cs @@ -0,0 +1,1375 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal class InfBlocks : InfTree + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + /// + /// Table for deflate from PKZIP's appnote.txt. + /// + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + private int left; // if STORED, bytes left to copy + + private int table; // table lengths (14 bits) + private int index; // index into blens (or border) + private int[] blens; // bit lengths of codes + private int[] bb = new int[1]; // bit length tree depth + private int[] tb = new int[1]; // bit length decoding tree + + private bool _isLastBlock; // true if this block is the last block + + // mode independent information + private int bitk; // bits in bit buffer + private int bitb; // bit buffer + private int[] hufts; // single malloc for tree space + private byte[] window; // sliding window + private int end; // one byte after sliding window + private int read; // window read pointer + private int write; // window write pointer + private Object checkfn; // check function + private long check; // check on output + + /// + /// Current inflate_codes mode + /// + private InflateCodeMode _mode; + + // mode dependent information + private int _len; + + private int[] _tree; // pointer into tree + private int _treeIndex = 0; + private int _need; // bits needed + + private int _lit; + + // if EXT or InflateCodeMode.COPY, where and how much + private int _get; // bits to get for extra + private int _dist; // distance back to copy from + + private byte _lbits; // ltree bits decoded per branch + private byte _dbits; // dtree bits decoder per branch + private int[] _ltree; // literal/length/eob tree + private int _ltreeIndex; // literal/length/eob tree + private int[] _dtree; // distance tree + /// + /// The distance tree index + /// + private int _dtreeIndex; + + + + //private InfTree inftree = new InfTree(); + + public InfBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + Reset(z, null); + } + + public void Reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + //if (mode == InflateBlockMode.CODES) + //{ + // codes.free(z); + //} + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + { + //z.adler = check = z.Adler32(0L, null, 0, 0); + z.UpdateAdler(0L, null, 0, 0); + check = z.Adler; + } + } + + public ZLibStatus ProcessBlocks(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus t1; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return InflateFlush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + this._isLastBlock = (t & 1) == 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InfTree.InflateTreesFixed(bl, bd, tl, td, z); + this.InitCodes(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (this._isLastBlock ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = InflateFlush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = this._isLastBlock ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + t1 = this.InflateTeesBits(blens, bb, tb, hufts, z); + if (t1 != ZLibStatus.Z_OK) + { + r = t1; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + //t = table; + t1 = this.InflateTreesDynamic(257 + (table & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (t1 != ZLibStatus.Z_OK) + { + if (t1 == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = t1; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + this.InitCodes(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = ProcessCodes(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return InflateFlush(z, r); + } + r = ZLibStatus.Z_OK; + //codes.free(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (this._isLastBlock == false) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = InflateFlush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return InflateFlush(z, r); + } + } + } + + public void free(ZStream z) + { + this.Reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + /// + /// copy as much as possible from the sliding window to the output area + /// + /// The z. + /// The r. + /// + public ZLibStatus InflateFlush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) + r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + { + //z.adler = check = z.Adler32(check, window, q, n); + z.UpdateAdler(check, window, q, n); + check = z.Adler; + } + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) + r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + { + //z.adler = check = z.Adler32(check, window, q, n); + z.UpdateAdler(check, window, q, n); + check = z.Adler; + } + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + + private void InitCodes(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, ZStream z) + { + this._mode = InflateCodeMode.START; + this._lbits = (byte)bl; + this._dbits = (byte)bd; + this._ltree = tl; + this._ltreeIndex = tl_index; + this._dtree = td; + this._dtreeIndex = td_index; + this._tree = null; + } + + private ZLibStatus ProcessCodes(InfBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (this._mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for InflateCodeMode.LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = this.InflateFast(this._lbits, this._dbits, this._ltree, this._ltreeIndex, this._dtree, this._dtreeIndex, s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + this._mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + this._need = this._lbits; + this._tree = this._ltree; + this._treeIndex = this._ltreeIndex; + + this._mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = this._need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (this._treeIndex + (b & inflate_mask[j])) * 3; + + b >>= (this._tree[tindex + 1]); + k -= (this._tree[tindex + 1]); + + e = this._tree[tindex]; + + if (e == 0) + { // literal + this._lit = this._tree[tindex + 2]; + this._mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + this._get = e & 15; + this._len = this._tree[tindex + 2]; + this._mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + this._need = e; + this._treeIndex = tindex / 3 + this._tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + this._mode = InflateCodeMode.WASH; + break; + } + this._mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = this._get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + this._len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + this._need = this._dbits; + this._tree = this._dtree; + this._treeIndex = this._dtreeIndex; + this._mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = this._need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (this._treeIndex + (b & inflate_mask[j])) * 3; + + b >>= this._tree[tindex + 1]; + k -= this._tree[tindex + 1]; + + e = (this._tree[tindex]); + if ((e & 16) != 0) + { // distance + this._get = e & 15; + this._dist = this._tree[tindex + 2]; + this._mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + this._need = e; + this._treeIndex = tindex / 3 + this._tree[tindex + 2]; + break; + } + this._mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = this._get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + this._dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + this._mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - this._dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (this._len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.InflateFlush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + this._len--; + } + this._mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.InflateFlush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)this._lit; m--; + + this._mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.InflateFlush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + this._mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.InflateFlush(z, r); + } + } + } + + /// + /// Inflates the fast. + /// + /// The bl. + /// The bd. + /// The tl. + /// The tl_index. + /// The td. + /// The td_index. + /// The s. + /// The z. + /// + /// + /// Called with number of bytes left to write in window at least 258 + /// (the maximum string length) and number of input bytes available + /// at least ten. The ten bytes are six bytes for the longest length/ + /// distance pair plus four bytes for overloading the bit buffer. + /// + private ZLibStatus InflateFast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, InfBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/InfCodes.cs b/Renci.SshNet/Compression/Working.1/InfCodes.cs new file mode 100644 index 0000000..be7fe62 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/InfCodes.cs @@ -0,0 +1,696 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InfCodes + { + + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + /// + /// Current inflate_codes mode + /// + private InflateCodeMode _mode; + + // mode dependent information + private int _len; + + private int[] _tree; // pointer into tree + private int _treeIndex = 0; + private int _need; // bits needed + + private int _lit; + + // if EXT or InflateCodeMode.COPY, where and how much + private int _get; // bits to get for extra + private int _dist; // distance back to copy from + + private byte _lbits; // ltree bits decoded per branch + private byte _dbits; // dtree bits decoder per branch + private int[] _ltree; // literal/length/eob tree + private int _ltreeIndex; // literal/length/eob tree + private int[] _dtree; // distance tree + private int _dtreeIndex; // distance tree + + internal void InitCodes(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, ZStream z) + { + this._mode = InflateCodeMode.START; + this._lbits = (byte)bl; + this._dbits = (byte)bd; + this._ltree = tl; + this._ltreeIndex = tl_index; + this._dtree = td; + this._dtreeIndex = td_index; + this._tree = null; + } + + internal ZLibStatus ProcessCodes(InfBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (this._mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for InflateCodeMode.LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = InflateFast(this._lbits, this._dbits, + this._ltree, this._ltreeIndex, + this._dtree, _dtreeIndex, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + this._mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + this._need = this._lbits; + this._tree = this._ltree; + this._treeIndex = this._ltreeIndex; + + this._mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = this._need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (this._treeIndex + (b & inflate_mask[j])) * 3; + + b >>= (this._tree[tindex + 1]); + k -= (this._tree[tindex + 1]); + + e = this._tree[tindex]; + + if (e == 0) + { // literal + this._lit = this._tree[tindex + 2]; + this._mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + this._get = e & 15; + this._len = this._tree[tindex + 2]; + this._mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + this._need = e; + this._treeIndex = tindex / 3 + this._tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + this._mode = InflateCodeMode.WASH; + break; + } + this._mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = this._get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + this._len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + this._need = this._dbits; + this._tree = this._dtree; + this._treeIndex = _dtreeIndex; + this._mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = this._need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (this._treeIndex + (b & inflate_mask[j])) * 3; + + b >>= this._tree[tindex + 1]; + k -= this._tree[tindex + 1]; + + e = (this._tree[tindex]); + if ((e & 16) != 0) + { // distance + this._get = e & 15; + this._dist = this._tree[tindex + 2]; + this._mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + this._need = e; + this._treeIndex = tindex / 3 + this._tree[tindex + 2]; + break; + } + this._mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = this._get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + this._dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + this._mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - this._dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (this._len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + this._len--; + } + this._mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)this._lit; m--; + + this._mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + this._mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + /// + /// Inflates the fast. + /// + /// The bl. + /// The bd. + /// The tl. + /// The tl_index. + /// The td. + /// The td_index. + /// The s. + /// The z. + /// + /// + /// Called with number of bytes left to write in window at least 258 + /// (the maximum string length) and number of input bytes available + /// at least ten. The ten bytes are six bytes for the longest length/ + /// distance pair plus four bytes for overloading the bit buffer. + /// + private ZLibStatus InflateFast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, InfBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/InfTree.cs b/Renci.SshNet/Compression/Working.1/InfTree.cs new file mode 100644 index 0000000..798ee7f --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/InfTree.cs @@ -0,0 +1,603 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal class InfTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + /// + /// Maximum bit length of any code + /// + /// + /// If BMAX needs to be larger than 16, then h and x[] should be uLong. + /// + private const int BMAX = 15; + + /// + /// hufts used in space + /// + private int[] _usedHufts = null; + + /// + /// The work area for huft_build + /// + private int[] v = null; + + /// + /// The bit length count table + /// + private int[] c = null; + + /// + /// The table entry for structure assignment + /// + private int[] r = null; + + /// + /// The table stack + /// + private int[] u = null; + + /// + /// The bit offsets, then code stack + /// + private int[] x = null; + + /// + /// Inflate_trees_bitses the specified c. + /// + /// The 19 code lengths. + /// The bits tree desired/actual depth. + /// The bits tree result. + /// The space for trees. + /// The for messages. + /// + protected ZLibStatus InflateTeesBits(int[] c, int[] bb, int[] tb, int[] hp, ZStream z) + { + ZLibStatus result; + InitWorkArea(19); + this._usedHufts[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, this._usedHufts, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + /// + /// Inflate_trees_dynamics the specified nl. + /// + /// The number of literal/length codes. + /// The number of distance codes. + /// The that many (total) code lengths. + /// The literal desired/actual bit depth. + /// The distance desired/actual bit depth . + /// The literal/length tree result. + /// The distance tree result. + /// The space for trees. + /// The for messages. + /// + protected ZLibStatus InflateTreesDynamic(int nl, int nd, int[] c, int[] bl, int[] bd, int[] tl, int[] td, int[] hp, ZStream z) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + this._usedHufts[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, this._usedHufts, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, this._usedHufts, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + /// + /// Inflate_trees_fixeds the specified bl. + /// + /// The literal desired/actual bit depth. + /// The distance desired/actual bit depth. + /// The literal/length tree result. + /// The distance tree result . + /// The for memory allocation. + /// + protected static ZLibStatus InflateTreesFixed(int[] bl, int[] bd, int[][] tl, int[][] td, ZStream z) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + /// + /// Huft_builds the specified b. + /// + /// code lengths in bits (all assumed <= BMAX) + /// The bindex. + /// The number of codes (assumed <= 288). + /// The number of simple-valued codes (0..s-1). + /// The list of base values for non-simple codes. + /// The list of extra bits for non-simple codes. + /// The result: starting table. + /// The maximum lookup bits, returns actual. + /// The space for trees. + /// The hufts used in space. + /// The working area: values in order of bit length. + /// + private ZLibStatus HuftBuild(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return ZLibStatus.Z_OK on success, ZLibStatus.Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), ZLibStatus.Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or ZLibStatus.Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return ZLibStatus.Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (this._usedHufts == null) + { + this._usedHufts = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) + { + v = new int[vsize]; + } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate + { + /// + /// 32K LZ77 window + /// + private const int MAX_WBITS = 15; + + /// + /// Preset dictionary flag in zlib header + /// + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] was = new long[1]; + + /// + /// The stream check value + /// + private long need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int wbits; + + /// + /// Current inflate_blocks state + /// + private InfBlocks blocks; + + /// + /// Gets the current inflate mode. + /// + /// + /// The mode. + /// + internal InflateMode Mode { get; private set; } + + internal ZLibStatus InflateReset(ZStream z) + { + if (z == null || z.istate == null) return ZLibStatus.Z_STREAM_ERROR; + + z.total_in = z.total_out = 0; + z.msg = null; + z.istate.Mode = z.istate.nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + z.istate.blocks.Reset(z, null); + return ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateEnd(ZStream z) + { + if (blocks != null) + blocks.free(z); + blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateInit(ZStream z, int w) + { + z.msg = null; + blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + nowrap = 0; + if (w < 0) + { + w = -w; + nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(z); + return ZLibStatus.Z_STREAM_ERROR; + } + wbits = w; + + z.istate.blocks = new InfBlocks(z, + z.istate.nowrap != 0 ? null : this, + 1 << w); + + // reset state + InflateReset(z); + return ZLibStatus.Z_OK; + } + + internal ZLibStatus inflate(ZStream z, FlushType ff) + { + ZLibStatus r; + int b; + + if (z == null || z.istate == null || z.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+z.istate.mode); + switch (z.istate.Mode) + { + case InflateMode.METHOD: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + if (((z.istate.method = z.next_in[z.next_in_index++]) & 0xf) != Z_DEFLATED) + { + z.istate.Mode = InflateMode.BAD; + z.msg = "unknown compression method"; + z.istate.marker = 5; // can't try inflateSync + break; + } + if ((z.istate.method >> 4) + 8 > z.istate.wbits) + { + z.istate.Mode = InflateMode.BAD; + z.msg = "invalid window size"; + z.istate.marker = 5; // can't try inflateSync + break; + } + z.istate.Mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + b = (z.next_in[z.next_in_index++]) & 0xff; + + if ((((z.istate.method << 8) + b) % 31) != 0) + { + z.istate.Mode = InflateMode.BAD; + z.msg = "incorrect header check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + z.istate.Mode = InflateMode.BLOCKS; + break; + } + z.istate.Mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L; + z.istate.Mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L; + z.istate.Mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L; + z.istate.Mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need += (z.next_in[z.next_in_index++] & 0xffL); + z.Adler = z.istate.need; + z.istate.Mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + z.istate.Mode = InflateMode.BAD; + z.msg = "need dictionary"; + z.istate.marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = z.istate.blocks.ProcessBlocks(z, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + z.istate.Mode = InflateMode.BAD; + z.istate.marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + z.istate.blocks.Reset(z, z.istate.was); + if (z.istate.nowrap != 0) + { + z.istate.Mode = InflateMode.DONE; + break; + } + z.istate.Mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L; + z.istate.Mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L; + z.istate.Mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L; + z.istate.Mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (z.avail_in == 0) return r; r = f; + + z.avail_in--; z.total_in++; + z.istate.need += (z.next_in[z.next_in_index++] & 0xffL); + + if (((int)(z.istate.was[0])) != ((int)(z.istate.need))) + { + z.istate.Mode = InflateMode.BAD; + z.msg = "incorrect data check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + z.istate.Mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + //internal ZLibStatus InflateSetDictionary(ZStream z, byte[] dictionary, int dictLength) + //{ + // int index = 0; + // int length = dictLength; + // if (z == null || z.istate == null || z.istate.Mode != InflateMode.DICT0) + // return ZLibStatus.Z_STREAM_ERROR; + + // if (z.Adler32(1L, dictionary, 0, dictLength) != z.Adler) + // { + // return ZLibStatus.Z_DATA_ERROR; + // } + + // //z.adler = z.Adler32(0, null, 0, 0); + // z.UpdateAdler(0, null, 0, 0); + + // if (length >= (1 << z.istate.wbits)) + // { + // length = (1 << z.istate.wbits) - 1; + // index = dictLength - length; + // } + // z.istate.blocks.set_dictionary(dictionary, index, length); + // z.istate.Mode = InflateMode.BLOCKS; + // return ZLibStatus.Z_OK; + //} + + internal ZLibStatus InflateSync(ZStream z) + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (z == null || z.istate == null) + return ZLibStatus.Z_STREAM_ERROR; + if (z.istate.Mode != InflateMode.BAD) + { + z.istate.Mode = InflateMode.BAD; + z.istate.marker = 0; + } + if ((n = z.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = z.next_in_index; + m = z.istate.marker; + + // search + while (n != 0 && m < 4) + { + if (z.next_in[p] == mark[m]) + { + m++; + } + else if (z.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + z.total_in += p - z.next_in_index; + z.next_in_index = p; + z.avail_in = n; + z.istate.marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = z.total_in; w = z.total_out; + InflateReset(z); + z.total_in = r; z.total_out = w; + z.istate.Mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + // Returns true if inflate is currently at the end of a block generated + // by FlushType.Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. This function is used by one PPP + // implementation to provide an additional safety check. PPP uses FlushType.Z_SYNC_FLUSH + // but removes the length bytes of the resulting empty stored block. When + // decompressing, PPP checks that at the end of input packet, inflate is + // waiting for these length bytes. + //internal ZLibStatus InflateSyncPoint(ZStream z) + //{ + // if (z == null || z.istate == null || z.istate.blocks == null) + // return ZLibStatus.Z_STREAM_ERROR; + // return z.istate.blocks.sync_point(); + //} + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/JZlib.cs b/Renci.SshNet/Compression/Working.1/JZlib.cs new file mode 100644 index 0000000..1e6d7d8 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/JZlib.cs @@ -0,0 +1,111 @@ +using System; +/* + * $Id: JZlib.cs,v 1.3 2011-02-15 05:46:04 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + // TODO: Double posat her, fix array initialization where it is now dictionary + public enum CompressionLevel : int + { + Z_NO_COMPRESSION = 0, + Z_BEST_SPEED = 1, + Z_BEST_COMPRESSION = 9, + Z_DEFAULT_COMPRESSION = -1 + } + public enum FlushType + { + Z_NO_FLUSH = 0, + Z_PARTIAL_FLUSH = 1, + Z_SYNC_FLUSH = 2, + Z_FULL_FLUSH = 3, + Z_FINISH = 4 + } + public enum CompressionStrategy + { + Z_FILTERED = 1, + Z_HUFFMAN_ONLY = 2, + Z_DEFAULT_STRATEGY = 0 + } + + public enum ZLibStatus + { + Z_OK = 0, + Z_STREAM_END = 1, + Z_NEED_DICT = 2, + Z_ERRNO = -1, + Z_STREAM_ERROR = -2, + Z_DATA_ERROR = -3, + Z_MEM_ERROR = -4, + Z_BUF_ERROR = -5, + Z_VERSION_ERROR = -6 + + } + public sealed class JZlib + { + private const String _version = "1.0.7"; + public static String version() + { + return _version; + } + + // compression levels + //public const int Z_NO_COMPRESSION = 0; + //public const int Z_BEST_SPEED = 1; + //public const int Z_BEST_COMPRESSION = 9; + //public const int Z_DEFAULT_COMPRESSION = -1; + + // compression strategy + //public const int CompressionStrategy.Z_FILTERED = 1; + //public const int CompressionStrategy.Z_HUFFMAN_ONLY = 2; + //public const int CompressionStrategy.Z_DEFAULT_STRATEGY = 0; + + //public const int FlushType.Z_NO_FLUSH = 0; + //public const int FlushType.Z_PARTIAL_FLUSH = 1; + //public const int FlushType.Z_SYNC_FLUSH = 2; + //public const int FlushType.Z_FULL_FLUSH = 3; + //public const int FlushType.Z_FINISH = 4; + + //public const int ZLibStatus.Z_OK = 0; + //public const int ZLibStatus.Z_STREAM_END = 1; + //public const int ZLibStatus.Z_NEED_DICT = 2; + //public const int ZLibStatus.Z_ERRNO = -1; + //public const int ZLibStatus.Z_STREAM_ERROR = -2; + //public const int ZLibStatus.Z_DATA_ERROR = -3; + //public const int ZLibStatus.Z_MEM_ERROR = -4; + //public const int ZLibStatus.Z_BUF_ERROR = -5; + //public const int ZLibStatus.Z_VERSION_ERROR = -6; + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/StaticTree.cs b/Renci.SshNet/Compression/Working.1/StaticTree.cs new file mode 100644 index 0000000..1ee3d35 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/StaticTree.cs @@ -0,0 +1,98 @@ +using System; +/* + * $Id: StaticTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class StaticTree + { + /// + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/Tree.cs b/Renci.SshNet/Compression/Working.1/Tree.cs new file mode 100644 index 0000000..33849c8 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/Tree.cs @@ -0,0 +1,333 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/ZInputStream.cs b/Renci.SshNet/Compression/Working.1/ZInputStream.cs new file mode 100644 index 0000000..2a2722e --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/ZInputStream.cs @@ -0,0 +1,192 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : ZStream + { + private const int BufferSize = 512; + + //protected ZStream z = new ZStream(); + protected FlushType flushLevel = FlushType.Z_NO_FLUSH; + // TODO Allow custom buf + protected byte[] buf = new byte[BufferSize]; + protected byte[] buf1 = new byte[1]; + protected bool compress; + + protected Stream input; + protected bool closed; + + private bool nomoreinput = false; + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + { + Debug.Assert(input.CanRead); + + this.input = input; + this.inflateInit(nowrap); + this.compress = false; + this.next_in = buf; + this.next_in_index = 0; + this.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + { + Debug.Assert(input.CanRead); + + this.input = input; + this.deflateInit(level); + this.compress = true; + this.next_in = buf; + this.next_in_index = 0; + this.avail_in = 0; + } + + /*public int available() throws IOException { + return inf.finished() ? 0 : 1; + }*/ + + public sealed override bool CanRead { get { return !closed; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return false; } } + + public override void Close() + { + if (!closed) + { + closed = true; + input.Close(); + } + } + + public sealed override void Flush() {} + + public virtual FlushType FlushMode + { + get { return flushLevel; } + set { this.flushLevel = value; } + } + + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] b, int off, int len) + { + if (len==0) + return 0; + + this.next_out = b; + this.next_out_index = off; + this.avail_out = len; + + ZLibStatus err; + do + { + if (this.avail_in == 0 && !nomoreinput) + { + // if buffer is empty and more input is available, refill it + this.next_in_index = 0; + this.avail_in = input.Read(buf, 0, buf.Length); //(bufsize 0 || this.avail_out == 0); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (this._isDisposed) + return; + + try + { + do + { + this.next_out = this._bufffer; + this.next_out_index = 0; + this.avail_out = this._bufffer.Length; + + var err = compress? this.deflate(FlushType.Z_FINISH) : this.inflate(FlushType.Z_FINISH); + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + throw new IOException((compress ? "de" : "in") + "flating: " + this.msg); + + int count = this._bufffer.Length - this.avail_out; + if (count > 0) + { + this._output.Write(this._bufffer, 0, count); + } + } + while (this.avail_in > 0 || this.avail_out == 0); + + this.Flush(); + } + finally + { + this._isDisposed = true; + if (compress) + this.deflateEnd(); + else + this.inflateEnd(); + this._output.Close(); + this._output = null; + } + } + } +} diff --git a/Renci.SshNet/Compression/Working.1/ZStream.cs b/Renci.SshNet/Compression/Working.1/ZStream.cs new file mode 100644 index 0000000..bebfebc --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/ZStream.cs @@ -0,0 +1,274 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public abstract class ZStream : Stream + { + private const int MAX_WBITS = 15; // 32K LZ77 window + private const int DEF_WBITS = MAX_WBITS; + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in; // next input byte + public int next_in_index; + public int avail_in; // number of bytes available at next_in + public long total_in; // total nb of input bytes read so far + + public byte[] next_out; // next output byte should be put there + public int next_out_index; + public int avail_out; // remaining free space at next_out + public long total_out; // total nb of bytes output so far + + public String msg; + + internal Deflate dstate; + internal Inflate istate; + + //internal int data_type; // best guess about the data type: ascii or binary + + // TODO: Make setter private + public long Adler { get; set; } + //internal Adler32 _adler = new Adler32(); + + // largest prime smaller than 65536 + private const int ADLER32_BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int ADLER32_NMAX = 5552; + + internal long Adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < ADLER32_NMAX ? len : ADLER32_NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= ADLER32_BASE; + s2 %= ADLER32_BASE; + } + return (s2 << 16) | s1; + } + + internal void UpdateAdler(long adler, byte[] buf, int index, int len) + { + this.Adler = this.Adler32(adler, buf, index, len); + } + internal void UpdateAdler(byte[] buf, int index, int len) + { + this.Adler = this.Adler32(this.Adler, buf, index, len); + } + + + protected ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + protected ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + protected ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + protected ZLibStatus inflateInit(int w, bool nowrap) + { + istate = new Inflate(); + return istate.InflateInit(this, nowrap ? -w : w); + } + protected ZLibStatus inflate(FlushType f) + { + if (istate == null) return ZLibStatus.Z_STREAM_ERROR; + return istate.inflate(this, f); + } + protected ZLibStatus inflateEnd() + { + if (istate == null) return ZLibStatus.Z_STREAM_ERROR; + var ret = istate.InflateEnd(this); + istate = null; + return ret; + } + protected ZLibStatus inflateSync() + { + if (istate == null) + return ZLibStatus.Z_STREAM_ERROR; + return istate.InflateSync(this); + } + //protected ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + //{ + // if (istate == null) + // return ZLibStatus.Z_STREAM_ERROR; + // return istate.InflateSetDictionary(this, dictionary, dictLength); + //} + + protected ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + protected ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + protected ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit(level, bits, false); + } + protected ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + dstate = new Deflate(); + return dstate.deflateInit(this, level, nowrap ? -bits : bits); + } + public ZLibStatus deflate(FlushType flush) + { + if (dstate == null) + { + return ZLibStatus.Z_STREAM_ERROR; + } + return dstate.deflate(this, flush); + } + protected ZLibStatus deflateEnd() + { + if (dstate == null) return ZLibStatus.Z_STREAM_ERROR; + var ret = dstate.deflateEnd(); + dstate = null; + return ret; + } + protected ZLibStatus deflateParams(CompressionLevel level, CompressionStrategy strategy) + { + if (dstate == null) return ZLibStatus.Z_STREAM_ERROR; + return dstate.deflateParams(this, level, strategy); + } + protected ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + if (dstate == null) + return ZLibStatus.Z_STREAM_ERROR; + return dstate.deflateSetDictionary(this, dictionary, dictLength); + } + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = dstate.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (dstate.pending_buf.Length <= dstate.pending_out || + next_out.Length <= next_out_index || + dstate.pending_buf.Length < (dstate.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(dstate.pending_buf, dstate.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + dstate.pending_out += len; + total_out += len; + avail_out -= len; + dstate.pending -= len; + if (dstate.pending == 0) + { + dstate.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (dstate.noheader == 0) + { + Adler = this.Adler32(Adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/Zlib.cs b/Renci.SshNet/Compression/Working.1/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/ZlibOpenSsh.cs b/Renci.SshNet/Compression/Working.1/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Working.1/ZlibStream.cs b/Renci.SshNet/Compression/Working.1/ZlibStream.cs new file mode 100644 index 0000000..31833e8 --- /dev/null +++ b/Renci.SshNet/Compression/Working.1/ZlibStream.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/ZDeflaterOutputStream.cs b/Renci.SshNet/Compression/ZDeflaterOutputStream.cs new file mode 100644 index 0000000..1d88847 --- /dev/null +++ b/Renci.SshNet/Compression/ZDeflaterOutputStream.cs @@ -0,0 +1,151 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib { + /// + /// Summary description for DeflaterOutputStream. + /// + [Obsolete("Use 'ZOutputStream' instead")] + public class ZDeflaterOutputStream : Stream { + protected ZStream z=new ZStream(); + protected int flushLevel=JZlib.Z_NO_FLUSH; + private const int BUFSIZE = 4192; + protected byte[] buf=new byte[BUFSIZE]; + private byte[] buf1=new byte[1]; + + protected Stream outp; + + public ZDeflaterOutputStream(Stream outp) : this(outp, 6, false) { + } + + public ZDeflaterOutputStream(Stream outp, int level) : this(outp, level, false) { + } + + public ZDeflaterOutputStream(Stream outp, int level, bool nowrap) { + this.outp=outp; + z.deflateInit(level, nowrap); + } + + + public override bool CanRead { + get { + // TODO: Add DeflaterOutputStream.CanRead getter implementation + return false; + } + } + + public override bool CanSeek { + get { + // TODO: Add DeflaterOutputStream.CanSeek getter implementation + return false; + } + } + + public override bool CanWrite { + get { + // TODO: Add DeflaterOutputStream.CanWrite getter implementation + return true; + } + } + + public override long Length { + get { + // TODO: Add DeflaterOutputStream.Length getter implementation + return 0; + } + } + + public override long Position { + get { + // TODO: Add DeflaterOutputStream.Position getter implementation + return 0; + } + set { + // TODO: Add DeflaterOutputStream.Position setter implementation + } + } + + public override void Write(byte[] b, int off, int len) { + if(len==0) + return; + int err; + z.next_in=b; + z.next_in_index=off; + z.avail_in=len; + do{ + z.next_out=buf; + z.next_out_index=0; + z.avail_out=BUFSIZE; + err=z.deflate(flushLevel); + if(err!=JZlib.Z_OK) + throw new IOException("deflating: "+z.msg); + if (z.avail_out < BUFSIZE) + { + outp.Write(buf, 0, BUFSIZE-z.avail_out); + } + } + while(z.avail_in>0 || z.avail_out==0); + } + + public override long Seek(long offset, SeekOrigin origin) { + // TODO: Add DeflaterOutputStream.Seek implementation + return 0; + } + + public override void SetLength(long value) { + // TODO: Add DeflaterOutputStream.SetLength implementation + + } + + public override int Read(byte[] buffer, int offset, int count) { + // TODO: Add DeflaterOutputStream.Read implementation + return 0; + } + + public override void Flush() { + outp.Flush(); + } + + public override void WriteByte(byte b) { + buf1[0]=(byte)b; + Write(buf1, 0, 1); + } + + public void Finish() { + int err; + do{ + z.next_out=buf; + z.next_out_index=0; + z.avail_out=BUFSIZE; + err=z.deflate(JZlib.Z_FINISH); + if(err!=JZlib.Z_STREAM_END && err != JZlib.Z_OK) + throw new IOException("deflating: "+z.msg); + if(BUFSIZE-z.avail_out>0){ + outp.Write(buf, 0, BUFSIZE-z.avail_out); + } + } + while(z.avail_in>0 || z.avail_out==0); + Flush(); + } + + public void End() { + if(z==null) + return; + z.deflateEnd(); + z.free(); + z=null; + } + + public override void Close() { + try{ + try{Finish();} + catch (IOException) {} + } + finally{ + End(); + outp.Close(); + outp=null; + } + } + } +} diff --git a/Renci.SshNet/Compression/ZInflaterInputStream.cs b/Renci.SshNet/Compression/ZInflaterInputStream.cs new file mode 100644 index 0000000..5a3ff5a --- /dev/null +++ b/Renci.SshNet/Compression/ZInflaterInputStream.cs @@ -0,0 +1,127 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib { + /// + /// Summary description for DeflaterOutputStream. + /// + [Obsolete("Use 'ZInputStream' instead")] + public class ZInflaterInputStream : Stream { + protected ZStream z=new ZStream(); + protected int flushLevel=JZlib.Z_NO_FLUSH; + private const int BUFSIZE = 4192; + protected byte[] buf=new byte[BUFSIZE]; + private byte[] buf1=new byte[1]; + + protected Stream inp=null; + private bool nomoreinput=false; + + public ZInflaterInputStream(Stream inp) : this(inp, false) { + } + + public ZInflaterInputStream(Stream inp, bool nowrap) { + this.inp=inp; + z.inflateInit(nowrap); + z.next_in=buf; + z.next_in_index=0; + z.avail_in=0; + } + + public override bool CanRead { + get { + // TODO: Add DeflaterOutputStream.CanRead getter implementation + return true; + } + } + + public override bool CanSeek { + get { + // TODO: Add DeflaterOutputStream.CanSeek getter implementation + return false; + } + } + + public override bool CanWrite { + get { + // TODO: Add DeflaterOutputStream.CanWrite getter implementation + return false; + } + } + + public override long Length { + get { + // TODO: Add DeflaterOutputStream.Length getter implementation + return 0; + } + } + + public override long Position { + get { + // TODO: Add DeflaterOutputStream.Position getter implementation + return 0; + } + set { + // TODO: Add DeflaterOutputStream.Position setter implementation + } + } + + public override void Write(byte[] b, int off, int len) { + } + + public override long Seek(long offset, SeekOrigin origin) { + // TODO: Add DeflaterOutputStream.Seek implementation + return 0; + } + + public override void SetLength(long value) { + // TODO: Add DeflaterOutputStream.SetLength implementation + + } + + public override int Read(byte[] b, int off, int len) { + if(len==0) + return(0); + int err; + z.next_out=b; + z.next_out_index=off; + z.avail_out=len; + do { + if((z.avail_in==0)&&(!nomoreinput)) { // if buffer is empty and more input is avaiable, refill it + z.next_in_index=0; + z.avail_in=inp.Read(buf, 0, BUFSIZE);//(BUFSIZE 0) + { + output.Write(buf, 0, count); + } + } + while (z.avail_in > 0 || z.avail_out == 0); + + Flush(); + } + + public override void Flush() + { + output.Flush(); + } + + public virtual int FlushMode + { + get { return flushLevel; } + set { this.flushLevel = value; } + } + + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + + public virtual long TotalIn + { + get { return z.total_in; } + } + + public virtual long TotalOut + { + get { return z.total_out; } + } + + public override void Write(byte[] b, int off, int len) + { + if (len == 0) + return; + + z.next_in = b; + z.next_in_index = off; + z.avail_in = len; + + do + { + z.next_out = buf; + z.next_out_index = 0; + z.avail_out = buf.Length; + + int err = compress + ? z.deflate(flushLevel) + : z.inflate(flushLevel); + + if (err != JZlib.Z_OK) + // TODO +// throw new ZStreamException((compress ? "de" : "in") + "flating: " + z.msg); + throw new IOException((compress ? "de" : "in") + "flating: " + z.msg); + + output.Write(buf, 0, buf.Length - z.avail_out); + } + while (z.avail_in > 0 || z.avail_out == 0); + } + + public override void WriteByte(byte b) + { + buf1[0] = b; + Write(buf1, 0, 1); + } + } +} diff --git a/Renci.SshNet/Compression/ZStream.cs b/Renci.SshNet/Compression/ZStream.cs new file mode 100644 index 0000000..7ff9614 --- /dev/null +++ b/Renci.SshNet/Compression/ZStream.cs @@ -0,0 +1,214 @@ +using System; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + public sealed class ZStream{ + + private const int MAX_WBITS=15; // 32K LZ77 window + private const int DEF_WBITS=MAX_WBITS; + + private const int Z_NO_FLUSH=0; + private const int Z_PARTIAL_FLUSH=1; + private const int Z_SYNC_FLUSH=2; + private const int Z_FULL_FLUSH=3; + private const int Z_FINISH=4; + + private const int MAX_MEM_LEVEL=9; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + public byte[] next_in; // next input byte + public int next_in_index; + public int avail_in; // number of bytes available at next_in + public long total_in; // total nb of input bytes read so far + + public byte[] next_out; // next output byte should be put there + public int next_out_index; + public int avail_out; // remaining free space at next_out + public long total_out; // total nb of bytes output so far + + public String msg; + + internal Deflate dstate; + internal Inflate istate; + + internal int data_type; // best guess about the data type: ascii or binary + + public long adler; + internal Adler32 _adler=new Adler32(); + + public int inflateInit(){ + return inflateInit(DEF_WBITS); + } + public int inflateInit(bool nowrap){ + return inflateInit(DEF_WBITS, nowrap); + } + public int inflateInit(int w){ + return inflateInit(w, false); + } + + public int inflateInit(int w, bool nowrap){ + istate=new Inflate(); + return istate.inflateInit(this, nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + return istate.inflate(this, f); + } + public int inflateEnd(){ + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(this); + istate = null; + return ret; + } + public int inflateSync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(this); + } + public int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(this, dictionary, dictLength); + } + + public int deflateInit(int level){ + return deflateInit(level, MAX_WBITS); + } + public int deflateInit(int level, bool nowrap){ + return deflateInit(level, MAX_WBITS, nowrap); + } + public int deflateInit(int level, int bits){ + return deflateInit(level, bits, false); + } + public int deflateInit(int level, int bits, bool nowrap){ + dstate=new Deflate(); + return dstate.deflateInit(this, level, nowrap?-bits:bits); + } + public int deflate(int flush){ + if(dstate==null){ + return Z_STREAM_ERROR; + } + return dstate.deflate(this, flush); + } + public int deflateEnd(){ + if(dstate==null) return Z_STREAM_ERROR; + int ret=dstate.deflateEnd(); + dstate=null; + return ret; + } + public int deflateParams(int level, int strategy){ + if(dstate==null) return Z_STREAM_ERROR; + return dstate.deflateParams(this, level, strategy); + } + public int deflateSetDictionary (byte[] dictionary, int dictLength){ + if(dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateSetDictionary(this, dictionary, dictLength); + } + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending(){ + int len=dstate.pending; + + if(len>avail_out) len=avail_out; + if(len==0) return; + + if(dstate.pending_buf.Length<=dstate.pending_out || + next_out.Length<=next_out_index || + dstate.pending_buf.Length<(dstate.pending_out+len) || + next_out.Length<(next_out_index+len)){ + // System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(dstate.pending_buf, dstate.pending_out, + next_out, next_out_index, len); + + next_out_index+=len; + dstate.pending_out+=len; + total_out+=len; + avail_out-=len; + dstate.pending-=len; + if(dstate.pending==0){ + dstate.pending_out=0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) { + int len=avail_in; + + if(len>size) len=size; + if(len==0) return 0; + + avail_in-=len; + + if(dstate.noheader==0) { + adler=_adler.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public void free(){ + next_in=null; + next_out=null; + msg=null; + _adler=null; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/Zlib.cs b/Renci.SshNet/Compression/Zlib.cs index 3921df7..5890675 100644 --- a/Renci.SshNet/Compression/Zlib.cs +++ b/Renci.SshNet/Compression/Zlib.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; namespace Renci.SshNet.Compression { @@ -8,8 +9,6 @@ namespace Renci.SshNet.Compression /// internal class Zlib : Compressor { - private bool _active; - /// /// Gets algorithm name. /// @@ -25,48 +24,7 @@ public override string Name public override void Init(Session session) { base.Init(session); - - session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; - } - - private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) - { - this._active = true; - this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; - } - - /// - /// Compresses the specified data. - /// - /// Data to compress. - /// - /// Compressed data - /// - public override byte[] Compress(byte[] data) - { - if (!this._active) - { - return data; - } - - throw new NotImplementedException(); - } - - /// - /// Decompresses the specified data. - /// - /// Compressed data. - /// - /// Decompressed data. - /// - public override byte[] Decompress(byte[] data) - { - if (!this._active) - { - return data; - } - - throw new NotImplementedException(); + this.IsActive = true; } } } \ No newline at end of file diff --git a/Renci.SshNet/Compression/ZlibOpenSsh.cs b/Renci.SshNet/Compression/ZlibOpenSsh.cs index 73dea13..f82a4c0 100644 --- a/Renci.SshNet/Compression/ZlibOpenSsh.cs +++ b/Renci.SshNet/Compression/ZlibOpenSsh.cs @@ -1,15 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; namespace Renci.SshNet.Compression { /// /// Represents "zlib@openssh.org" compression implementation /// - internal class ZlibOpenSsh : Compressor + public class ZlibOpenSsh : Compressor { - private bool _active; - /// /// Gets algorithm name. /// @@ -31,80 +30,8 @@ public override void Init(Session session) private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) { - this._active = true; + this.IsActive = true; this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; } - - /// - /// Compresses the specified data. - /// - /// Data to compress. - /// - /// Compressed data - /// - public override byte[] Compress(byte[] data) - { - if (!this._active) - { - return data; - } - throw new NotImplementedException(); - - //using (var output = new MemoryStream()) - //{ - // using (var input = new MemoryStream(data.ToArray())) - // using (var compress = new DeflateStream(output, CompressionMode.Compress)) - // { - // compress.FlushMode = FlushType.Partial; - - // input.CopyTo(compress); - - // var result = new List(); - - // result.Add(0x78); - // result.Add(0x9c); - - // result.AddRange(output.ToArray()); - - // return result; - // } - //} - } - - /// - /// Decompresses the specified data. - /// - /// Compressed data. - /// - /// Decompressed data. - /// - public override byte[] Decompress(byte[] data) - { - if (!this._active) - { - return data; - } - - throw new NotImplementedException(); - - //Create the decompressed file. - //using (var output = new MemoryStream()) - //{ - // using (var input = new MemoryStream(data.ToArray())) - // { - // input.ReadByte(); - // input.ReadByte(); - - // using (var decompress = new DeflateStream(input, CompressionMode.Decompress)) - // { - // // Copy the decompression stream - // // into the output file. - // decompress.CopyTo(output); - // } - // } - - // return output.ToArray(); - //} - } } } \ No newline at end of file diff --git a/Renci.SshNet/Compression/ZlibStream.cs b/Renci.SshNet/Compression/ZlibStream.cs new file mode 100644 index 0000000..f3b6f32 --- /dev/null +++ b/Renci.SshNet/Compression/ZlibStream.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + //private readonly Ionic.Zlib.ZlibStream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + //switch (mode) + //{ + // case CompressionMode.Compress: + // this._baseStream = new Ionic.Zlib.ZlibStream(stream, Ionic.Zlib.CompressionMode.Compress, Ionic.Zlib.CompressionLevel.Default); + // break; + // case CompressionMode.Decompress: + // this._baseStream = new Ionic.Zlib.ZlibStream(stream, Ionic.Zlib.CompressionMode.Decompress, Ionic.Zlib.CompressionLevel.Default); + // break; + // default: + // break; + //} + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + //this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/v10/Adler32.cs b/Renci.SshNet/Compression/v10/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/v10/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/CompressionMode.cs b/Renci.SshNet/Compression/v10/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/v10/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/v10/Compressor.cs b/Renci.SshNet/Compression/v10/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/v10/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/v10/Deflate.cs b/Renci.SshNet/Compression/v10/Deflate.cs new file mode 100644 index 0000000..fe69f52 --- /dev/null +++ b/Renci.SshNet/Compression/v10/Deflate.cs @@ -0,0 +1,2290 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint _bitBuffer; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + base.next_in = inputBufer; + base.next_in_index = inputOffset; + base.avail_in = inputCount; + base.next_out = outputBuffer; + base.next_out_index = outputOffset; + base.avail_out = outputCount; + return this.deflate(flushType); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + private ZLibStatus deflate(FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (base.next_out == null || + (base.next_in == null && base.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (base.avail_out == 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + PutShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + PutShortMSB((int)(base.adler >> 16)); + PutShortMSB((int)(base.adler & 0xffff)); + } + base.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.FlushPending(); + if (base.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (base.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && base.avail_in != 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (base.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = DeflateStored(flush); + break; + case DefalteFlavor.FAST: + bstate = DeflateFast(flush); + break; + case DefalteFlavor.SLOW: + bstate = DeflateSlow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + SendStoredBlock(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.FlushPending(); + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + PutShortMSB((int)(base.adler >> 16)); + PutShortMSB((int)(base.adler & 0xffff)); + this.FlushPending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + /// + /// Restore the heap property by moving down the tree starting at node k, + /// exchanging a node with the smallest of its two sons if necessary, stopping + /// when the heap property is re-established (each father smaller than its + /// two sons). + /// + /// The tree to restore. + /// The node to move down. + internal void RestoreHeap(short[] tree, int k) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + Smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (Smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + private ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + + private ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + + private ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(level, nowrap ? -bits : bits); + } + + private ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit2(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + base.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(); + } + + private ZLibStatus deflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; // + base.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + base.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + InitializeTreeData(); + lm_init(); + return ZLibStatus.Z_OK; + } + + private ZLibStatus deflateParams(CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + base.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + private ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + base.adler = Adler32.adler32(base.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + /// + /// Initialize the tree data structures for a new zlib stream. + /// + private void InitializeTreeData() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + _bitBuffer = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + InitializeBlock(); + } + + private void InitializeBlock() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool Smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + /// + /// Scan a literal or distance tree to determine the frequencies of the codes + /// in the bit length tree. + /// + /// The tree to be scanned. + /// The tree's largest code of non zero frequency. + private void ScanTree(short[] tree, int max_code) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + /// + /// Construct the Huffman tree for the bit lengths and return the index in bl_order of the last bit length code to send. + /// + /// + private int BuildBlTree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + ScanTree(dyn_ltree, l_desc.LargestCode); + ScanTree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + /// + /// Send the header for a block using dynamic Huffman trees: the counts, the lengths of the bit length codes, the literal tree and the distance tree. + /// + /// The lcodes. + /// The dcodes. + /// The blcodes. + private void SendAllTrees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + SendBits(lcodes - 257, 5); // not +255 as stated in appnote.txt + SendBits(dcodes - 1, 5); + SendBits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + SendBits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + SendTree(dyn_ltree, lcodes - 1); // literal tree + SendTree(dyn_dtree, dcodes - 1); // distance tree + } + + /// + /// Send a literal or distance tree in compressed form, using the codes in bl_tree. + /// + /// The tree to be sent. + /// The tree's largest code of non zero frequency. + private void SendTree(short[] tree, int max_code) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { SendCode(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + SendCode(curlen, bl_tree); count--; + } + SendCode(REP_3_6, bl_tree); + SendBits(count - 3, 2); + } + else if (count <= 10) + { + SendCode(REPZ_3_10, bl_tree); + SendBits(count - 3, 3); + } + else + { + SendCode(REPZ_11_138, bl_tree); + SendBits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + /// + /// Puts a bytes into the stream. + /// + /// The p. + /// The start. + /// The len. + private void PutByte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + /// + /// Puts a byte into the stream. + /// + /// The c. + private void PutByte(byte c) + { + pending_buf[pending++] = c; + } + + /// + /// Puts a short into the stream. + /// + /// The w. + private void PutShort(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + + /// + /// Puts the short MSB. + /// + /// The b. + private void PutShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void SendCode(int code, short[] tree) + { + int c2 = code * 2; + SendBits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void SendBits(int bits, int length) + { + if (bi_valid > Buf_size - length) + { + _bitBuffer |= (uint)(bits << bi_valid); + pending_buf[pending++] = (byte)(_bitBuffer/*&0xff*/); + pending_buf[pending++] = (byte)(_bitBuffer >> 8); + _bitBuffer = ((uint)bits) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + _bitBuffer |= (uint)(bits << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + /// + /// Send one empty static block to give enough lookahead for inflate. + /// This takes 10 bits, of which 7 may remain in the bit buffer. + /// The current inflate code requires 9 bits of lookahead. If the + /// last two codes for the previous block (real code plus EOB) were coded + /// on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + /// the last real code. In this case we send two empty static blocks instead + /// of one. (There are no problems if the previous block is stored or fixed.) + /// To simplify the code, we assume the worst case of last real code encoded + /// on one bit only. + /// + private void _tr_align() + { + SendBits(STATIC_TREES << 1, 3); + SendCode(END_BLOCK, Deflate.static_ltree); + + FlushBitBuffer(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + SendBits(STATIC_TREES << 1, 3); + SendCode(END_BLOCK, Deflate.static_ltree); + FlushBitBuffer(); + } + last_eob_len = 7; + } + + /// + /// Save the match info and tally the frequency counts. Return true if + /// the current block must be flushed. + /// + /// The distance of matched string. + /// The match length-MIN_MATCH or unmatched char (if dist==0). + /// + private bool _tr_tally(int dist, int lc) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + /// + /// Send the block data compressed using the given Huffman trees + /// + /// The ltree. + /// The dtree. + private void CompressBlock(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + SendCode(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + SendCode(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + SendBits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + SendCode(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + SendBits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + SendCode(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + /// + /// Set the data type to ASCII or BINARY, using a crude approximation: + /// binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + /// IN assertion: the fields freq of dyn_ltree are set and the total of all + /// frequencies does not exceed 64K (to fit in an int on 16 bit machines). + /// + private void SetDataType() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + /// + /// Flush the bit buffer, keeping at most 7 bits in it. + /// + private void FlushBitBuffer() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(_bitBuffer/*&0xff*/); + pending_buf[pending++] = (byte)(_bitBuffer >> 8); + _bitBuffer = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(_bitBuffer); + _bitBuffer >>= 8; + _bitBuffer &= 0x00ff; + bi_valid -= 8; + } + } + + /// + /// Flush the bit buffer and align the output on a byte boundary + /// + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(_bitBuffer); + pending_buf[pending++] = (byte)(_bitBuffer >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(_bitBuffer); + } + _bitBuffer = 0; + bi_valid = 0; + } + + /// + /// Copy a stored block, storing first the length and its one's complement if requested. + /// + /// The input data. + /// The length. + /// if set to true block header must be written. + private void CopyBlock(int buf, int len, bool header) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + PutShort((short)len); + PutShort((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + PutByte(_window, buf, len); + } + + private void FlushBlockOnly(bool eof) + { + FlushBlock(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.FlushPending(); + } + + /// + /// Copy without compression as much as possible from the input stream, return + /// the current block state. + /// This function does not insert new strings in the dictionary since + /// uncompressible data is probably not useful. This function is used + /// only for the level=0 compression option. + /// + /// The flush. + /// + private int DeflateStored(FlushType flush) + { + // TODO: this function should be optimized to avoid extra copying from window to pending_buf. + + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + FillWindow(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + FlushBlockOnly(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + FlushBlockOnly(false); + if (base.avail_out == 0) return NeedMore; + } + } + + FlushBlockOnly(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + /// + /// Send a stored block + /// + /// The input block. + /// The length of input block. + /// if set to true then this is the last block for a file. + private void SendStoredBlock(int buf, int stored_len, bool eof) + { + SendBits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + CopyBlock(buf, stored_len, true); // with header + } + + /// + /// Determine the best encoding for the current block: dynamic trees, static + /// trees or store, and output the encoded block to the zip file. + /// + /// The input block, or NULL if too old. + /// The length of input block. + /// if set to true then this is the last block for a file. + private void FlushBlock(int buf, int stored_len, bool eof) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) SetDataType(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = BuildBlTree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + SendStoredBlock(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + SendBits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + CompressBlock(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + SendBits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + SendAllTrees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + CompressBlock(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + InitializeBlock(); + + if (eof) + { + bi_windup(); + } + } + + /// + /// Fill the window when the lookahead becomes insufficient. + /// Updates strstart and lookahead. + /// + private void FillWindow() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.ReadBuffer(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + /// + /// Compress as much as possible from the input stream, return the current + /// block state. + /// This function does not perform lazy evaluation of matches and inserts + /// new strings in the dictionary only for unmatched strings or for short + /// matches. It is used only for the fast compression options. + /// + /// The flush. + /// + private int DeflateFast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + FillWindow(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = LongestMatch(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + FlushBlockOnly(false); + if (base.avail_out == 0) return NeedMore; + } + } + + FlushBlockOnly(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + /// + /// Same as above, but achieves better compression. We use a lazy + /// evaluation for matches: a match is finally adopted only if there is + /// no better match at the next window position. + /// + /// The flush. + /// + private int DeflateSlow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + FillWindow(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = LongestMatch(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + FlushBlockOnly(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + FlushBlockOnly(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + FlushBlockOnly(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int LongestMatch(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + /// + /// Flush as much pending output as possible. All deflate() output goes + /// through this function so some applications may wish to modify it + /// to avoid allocating a large strm->next_out buffer and copying into it. + /// (See also ReadBuffer()). + /// + private void FlushPending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + /// + /// Reads a new buffer from the current input stream, update the adler32 + /// and total number of bytes read. All deflate() input goes through + /// this function so some applications may wish to modify it to avoid + /// allocating a large strm->next_in buffer and copying from it. + /// (See also FlushPending()). + /// + /// The buf. + /// The start. + /// The size. + /// + private int ReadBuffer(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/Inflate.cs b/Renci.SshNet/Compression/v10/Inflate.cs new file mode 100644 index 0000000..c05e2d8 --- /dev/null +++ b/Renci.SshNet/Compression/v10/Inflate.cs @@ -0,0 +1,524 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd() + { + if (_blocks != null) + _blocks.free(this); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(this, this._nowrap != 0 ? null : this, 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(); + } + + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/InflateBlocks.cs b/Renci.SshNet/Compression/v10/InflateBlocks.cs new file mode 100644 index 0000000..273389f --- /dev/null +++ b/Renci.SshNet/Compression/v10/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/InflateCodes.cs b/Renci.SshNet/Compression/v10/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/v10/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/InflateTree.cs b/Renci.SshNet/Compression/v10/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/v10/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/Tree.cs b/Renci.SshNet/Compression/v10/Tree.cs new file mode 100644 index 0000000..b0978af --- /dev/null +++ b/Renci.SshNet/Compression/v10/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.RestoreHeap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.RestoreHeap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.RestoreHeap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/ZInputStream.cs b/Renci.SshNet/Compression/v10/ZInputStream.cs new file mode 100644 index 0000000..d316955 --- /dev/null +++ b/Renci.SshNet/Compression/v10/ZInputStream.cs @@ -0,0 +1,200 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + //this._compressor.next_out = buffer; + //this._compressor.next_out_index = offset; + //this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + //this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, err)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/v10/ZStream.cs b/Renci.SshNet/Compression/v10/ZStream.cs new file mode 100644 index 0000000..b151ba1 --- /dev/null +++ b/Renci.SshNet/Compression/v10/ZStream.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public interface ICompressor + { + ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + + ZLibStatus deflateEnd(); + + ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + + ZLibStatus inflateEnd(); + + //ZLibStatus inflate(FlushType flushType); + + int avail_out { get; set; } + + int avail_in { get; set; } + + //int next_out_index { get; set; } + + //byte[] next_out { get; set; } + + byte[] next_in { get; set; } + + int next_in_index { get; set; } + } + + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler; + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/Zlib.cs b/Renci.SshNet/Compression/v10/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/v10/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/ZlibOpenSsh.cs b/Renci.SshNet/Compression/v10/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/v10/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v10/ZlibStream.cs b/Renci.SshNet/Compression/v10/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/v10/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/v11/Adler32.cs b/Renci.SshNet/Compression/v11/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/v11/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/CompressionMode.cs b/Renci.SshNet/Compression/v11/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/v11/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/v11/Compressor.cs b/Renci.SshNet/Compression/v11/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/v11/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/v11/Deflate.cs b/Renci.SshNet/Compression/v11/Deflate.cs new file mode 100644 index 0000000..cc42021 --- /dev/null +++ b/Renci.SshNet/Compression/v11/Deflate.cs @@ -0,0 +1,2225 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(level, nowrap ? -bits : bits); + } + + internal ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit2(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + base.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(); + } + + private ZLibStatus deflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; // + base.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + base.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + base.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + base.adler = Adler32.adler32(base.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + public ZLibStatus deflate(FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (base.next_out == null || + (base.next_in == null && base.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (base.avail_out == 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + } + base.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (base.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (base.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && base.avail_in != 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (base.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/Inflate.cs b/Renci.SshNet/Compression/v11/Inflate.cs new file mode 100644 index 0000000..c05e2d8 --- /dev/null +++ b/Renci.SshNet/Compression/v11/Inflate.cs @@ -0,0 +1,524 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd() + { + if (_blocks != null) + _blocks.free(this); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(this, this._nowrap != 0 ? null : this, 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(); + } + + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/InflateBlocks.cs b/Renci.SshNet/Compression/v11/InflateBlocks.cs new file mode 100644 index 0000000..273389f --- /dev/null +++ b/Renci.SshNet/Compression/v11/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/InflateCodes.cs b/Renci.SshNet/Compression/v11/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/v11/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/InflateTree.cs b/Renci.SshNet/Compression/v11/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/v11/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/Tree.cs b/Renci.SshNet/Compression/v11/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/v11/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/ZInputStream.cs b/Renci.SshNet/Compression/v11/ZInputStream.cs new file mode 100644 index 0000000..0ef972c --- /dev/null +++ b/Renci.SshNet/Compression/v11/ZInputStream.cs @@ -0,0 +1,202 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + //this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + //this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + //this._compressor.next_out = buffer; + //this._compressor.next_out_index = offset; + //this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + //this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, err)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/v11/ZStream.cs b/Renci.SshNet/Compression/v11/ZStream.cs new file mode 100644 index 0000000..b151ba1 --- /dev/null +++ b/Renci.SshNet/Compression/v11/ZStream.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public interface ICompressor + { + ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + + ZLibStatus deflateEnd(); + + ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + + ZLibStatus inflateEnd(); + + //ZLibStatus inflate(FlushType flushType); + + int avail_out { get; set; } + + int avail_in { get; set; } + + //int next_out_index { get; set; } + + //byte[] next_out { get; set; } + + byte[] next_in { get; set; } + + int next_in_index { get; set; } + } + + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler; + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/Zlib.cs b/Renci.SshNet/Compression/v11/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/v11/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/ZlibOpenSsh.cs b/Renci.SshNet/Compression/v11/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/v11/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v11/ZlibStream.cs b/Renci.SshNet/Compression/v11/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/v11/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/v12/Adler32.cs b/Renci.SshNet/Compression/v12/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/v12/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/CompressionMode.cs b/Renci.SshNet/Compression/v12/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/v12/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/v12/Compressor.cs b/Renci.SshNet/Compression/v12/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/v12/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/v12/Deflate.cs b/Renci.SshNet/Compression/v12/Deflate.cs new file mode 100644 index 0000000..cc42021 --- /dev/null +++ b/Renci.SshNet/Compression/v12/Deflate.cs @@ -0,0 +1,2225 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(level, nowrap ? -bits : bits); + } + + internal ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit2(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + base.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(); + } + + private ZLibStatus deflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; // + base.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + base.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + base.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + base.adler = Adler32.adler32(base.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + public ZLibStatus deflate(FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (base.next_out == null || + (base.next_in == null && base.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (base.avail_out == 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + } + base.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (base.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (base.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && base.avail_in != 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (base.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/Inflate.cs b/Renci.SshNet/Compression/v12/Inflate.cs new file mode 100644 index 0000000..c05e2d8 --- /dev/null +++ b/Renci.SshNet/Compression/v12/Inflate.cs @@ -0,0 +1,524 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd() + { + if (_blocks != null) + _blocks.free(this); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(this, this._nowrap != 0 ? null : this, 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(); + } + + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/InflateBlocks.cs b/Renci.SshNet/Compression/v12/InflateBlocks.cs new file mode 100644 index 0000000..dde3654 --- /dev/null +++ b/Renci.SshNet/Compression/v12/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ICompressor z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ICompressor z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ICompressor z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ICompressor z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ICompressor z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/InflateCodes.cs b/Renci.SshNet/Compression/v12/InflateCodes.cs new file mode 100644 index 0000000..5edecb7 --- /dev/null +++ b/Renci.SshNet/Compression/v12/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ICompressor z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ICompressor z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ICompressor z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ICompressor z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/InflateTree.cs b/Renci.SshNet/Compression/v12/InflateTree.cs new file mode 100644 index 0000000..a97d4cb --- /dev/null +++ b/Renci.SshNet/Compression/v12/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ICompressor z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ICompressor z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ICompressor z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/Tree.cs b/Renci.SshNet/Compression/v12/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/v12/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/ZInputStream.cs b/Renci.SshNet/Compression/v12/ZInputStream.cs new file mode 100644 index 0000000..0ef972c --- /dev/null +++ b/Renci.SshNet/Compression/v12/ZInputStream.cs @@ -0,0 +1,202 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + //this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + //this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + //this._compressor.next_out = buffer; + //this._compressor.next_out_index = offset; + //this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + //this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, err)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/v12/ZStream.cs b/Renci.SshNet/Compression/v12/ZStream.cs new file mode 100644 index 0000000..3e9d952 --- /dev/null +++ b/Renci.SshNet/Compression/v12/ZStream.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public interface ICompressor + { + ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + + ZLibStatus deflateEnd(); + + ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + + ZLibStatus inflateEnd(); + + //ZLibStatus inflate(FlushType flushType); + + int avail_out { get; set; } + + int avail_in { get; set; } + + int next_out_index { get; set; } + + byte[] next_out { get; set; } + + byte[] next_in { get; set; } + + int next_in_index { get; set; } + + String msg { get; set; } + + long adler { get; set; } + + long total_in { get; set; } + + long total_out { get; set; } + } + + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler { get; set; } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/Zlib.cs b/Renci.SshNet/Compression/v12/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/v12/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/ZlibOpenSsh.cs b/Renci.SshNet/Compression/v12/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/v12/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v12/ZlibStream.cs b/Renci.SshNet/Compression/v12/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/v12/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/v13/Adler32.cs b/Renci.SshNet/Compression/v13/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/v13/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/CompressionMode.cs b/Renci.SshNet/Compression/v13/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/v13/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/v13/Compressor.cs b/Renci.SshNet/Compression/v13/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/v13/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/v13/Deflate.cs b/Renci.SshNet/Compression/v13/Deflate.cs new file mode 100644 index 0000000..cc42021 --- /dev/null +++ b/Renci.SshNet/Compression/v13/Deflate.cs @@ -0,0 +1,2225 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(level, nowrap ? -bits : bits); + } + + internal ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit2(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + base.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(); + } + + private ZLibStatus deflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; // + base.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + base.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + base.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + base.adler = Adler32.adler32(base.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + public ZLibStatus deflate(FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (base.next_out == null || + (base.next_in == null && base.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (base.avail_out == 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + } + base.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (base.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (base.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && base.avail_in != 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (base.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/Inflate.cs b/Renci.SshNet/Compression/v13/Inflate.cs new file mode 100644 index 0000000..c05e2d8 --- /dev/null +++ b/Renci.SshNet/Compression/v13/Inflate.cs @@ -0,0 +1,524 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd() + { + if (_blocks != null) + _blocks.free(this); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(this, this._nowrap != 0 ? null : this, 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(); + } + + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/InflateBlocks.cs b/Renci.SshNet/Compression/v13/InflateBlocks.cs new file mode 100644 index 0000000..609265f --- /dev/null +++ b/Renci.SshNet/Compression/v13/InflateBlocks.cs @@ -0,0 +1,717 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ICompressor z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ICompressor z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ICompressor z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0]); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ICompressor z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ICompressor z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/InflateCodes.cs b/Renci.SshNet/Compression/v13/InflateCodes.cs new file mode 100644 index 0000000..5bc7260 --- /dev/null +++ b/Renci.SshNet/Compression/v13/InflateCodes.cs @@ -0,0 +1,685 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ICompressor z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ICompressor z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/InflateTree.cs b/Renci.SshNet/Compression/v13/InflateTree.cs new file mode 100644 index 0000000..c8a3dbf --- /dev/null +++ b/Renci.SshNet/Compression/v13/InflateTree.cs @@ -0,0 +1,554 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ICompressor z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ICompressor z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td //distance tree result + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/Tree.cs b/Renci.SshNet/Compression/v13/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/v13/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/ZInputStream.cs b/Renci.SshNet/Compression/v13/ZInputStream.cs new file mode 100644 index 0000000..0ef972c --- /dev/null +++ b/Renci.SshNet/Compression/v13/ZInputStream.cs @@ -0,0 +1,202 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + //this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + //this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + //this._compressor.next_out = buffer; + //this._compressor.next_out_index = offset; + //this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + //this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, err)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/v13/ZStream.cs b/Renci.SshNet/Compression/v13/ZStream.cs new file mode 100644 index 0000000..0d5204b --- /dev/null +++ b/Renci.SshNet/Compression/v13/ZStream.cs @@ -0,0 +1,100 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public interface ICompressor + { + ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + ZLibStatus deflateEnd(); + + ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + ZLibStatus inflateEnd(); + + //ZLibStatus inflate(FlushType flushType); + + int avail_out { get; set; } + + int avail_in { get; set; } + + int next_out_index { get; set; } + + byte[] next_out { get; set; } + + byte[] next_in { get; set; } + + int next_in_index { get; set; } + + String msg { get; set; } + + long adler { get; set; } + + long total_in { get; set; } + + long total_out { get; set; } + } + + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler { get; set; } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/Zlib.cs b/Renci.SshNet/Compression/v13/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/v13/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/ZlibOpenSsh.cs b/Renci.SshNet/Compression/v13/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/v13/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v13/ZlibStream.cs b/Renci.SshNet/Compression/v13/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/v13/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/v9/Adler32.cs b/Renci.SshNet/Compression/v9/Adler32.cs new file mode 100644 index 0000000..c132fa9 --- /dev/null +++ b/Renci.SshNet/Compression/v9/Adler32.cs @@ -0,0 +1,95 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal static class Adler32 + { + + // largest prime smaller than 65536 + private const int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX = 5552; + + internal static long adler32(long adler, byte[] buf, int index, int len) + { + if (buf == null) { return 1L; } + + long s1 = adler & 0xffff; + long s2 = (adler >> 16) & 0xffff; + int k; + + while (len > 0) + { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + s1 += buf[index++] & 0xff; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++] & 0xff; s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; + } + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/CompressionMode.cs b/Renci.SshNet/Compression/v9/CompressionMode.cs new file mode 100644 index 0000000..1577e0b --- /dev/null +++ b/Renci.SshNet/Compression/v9/CompressionMode.cs @@ -0,0 +1,18 @@ +namespace Renci.SshNet.Compression +{ + /// + /// Specifies compression modes + /// + public enum CompressionMode + { + /// + /// Specifies that content should be compressed. + /// + Compress = 0, + + /// + /// Specifies that content should be decompressed. + /// + Decompress = 1, + } +} diff --git a/Renci.SshNet/Compression/v9/Compressor.cs b/Renci.SshNet/Compression/v9/Compressor.cs new file mode 100644 index 0000000..1479bb6 --- /dev/null +++ b/Renci.SshNet/Compression/v9/Compressor.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using Renci.SshNet.Security; +using System.IO; +using System; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents base class for compression algorithm implementation + /// + public abstract class Compressor : Algorithm, IDisposable + { + private readonly ZlibStream _compressor; + private readonly ZlibStream _decompressor; + + private MemoryStream _compressorStream; + private MemoryStream _decompressorStream; + + /// + /// Gets or sets a value indicating whether compression is active. + /// + /// + /// true if compression is active; otherwise, false. + /// + protected bool IsActive { get; set; } + + /// + /// Gets the session. + /// + protected Session Session { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Compressor() + { + this._compressorStream = new MemoryStream(); + this._decompressorStream = new MemoryStream(); + + this._compressor = new ZlibStream(this._compressorStream, CompressionMode.Compress); + this._decompressor = new ZlibStream(this._decompressorStream, CompressionMode.Decompress); + } + + /// + /// Initializes the algorithm + /// + /// The session. + public virtual void Init(Session session) + { + this.Session = session; + } + + /// + /// Compresses the specified data. + /// + /// Data to compress. + /// Compressed data + public virtual byte[] Compress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._compressorStream.SetLength(0); + + this._compressor.Write(data, 0, data.Length); + + return this._compressorStream.ToArray(); + } + + /// + /// Decompresses the specified data. + /// + /// Compressed data. + /// Decompressed data. + public virtual byte[] Decompress(byte[] data) + { + if (!this.IsActive) + { + return data; + } + + this._decompressorStream.SetLength(0); + + this._decompressor.Write(data, 0, data.Length); + + return this._decompressorStream.ToArray(); + } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._compressorStream != null) + { + this._compressorStream.Dispose(); + this._compressorStream = null; + } + + if (this._decompressorStream != null) + { + this._decompressorStream.Dispose(); + this._decompressorStream = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Compressor() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Compression/v9/Deflate.cs b/Renci.SshNet/Compression/v9/Deflate.cs new file mode 100644 index 0000000..7425e49 --- /dev/null +++ b/Renci.SshNet/Compression/v9/Deflate.cs @@ -0,0 +1,2226 @@ +using System; +using System.Collections.Generic; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum BlockType : byte + { + Z_BINARY = 0, + Z_ASCII = 1, + Z_UNKNOWN = 2 + } + + public enum DefalteFlavor : int + { + STORED = 0, + FAST = 1, + SLOW = 2 + } + + public sealed class Deflate : ZStream, ICompressor + { + private const int MAX_MEM_LEVEL = 9; + + private const int Z_DEFAULT_COMPRESSION = -1; + + private const int MAX_WBITS = 15; // 32K LZ77 window + + private const int DEF_MEM_LEVEL = 8; + + /// + /// The Bit length codes must not exceed MAX_BL_BITS bits + /// + private const int MAX_BL_BITS = 7; + + // block not completed, need more input or more output + private const int NeedMore = 0; + + // block flush performed + private const int BlockDone = 1; + + // finish started, need only more output at next deflate + private const int FinishStarted = 2; + + // finish done, accept no more input or output + private const int FinishDone = 3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_VERSION_ERROR = -6; + + private const int INIT_STATE = 42; + private const int BUSY_STATE = 113; + private const int FINISH_STATE = 666; + + // The deflate compression method + private const int Z_DEFLATED = 8; + + private const int STORED_BLOCK = 0; + private const int STATIC_TREES = 1; + private const int DYN_TREES = 2; + + private const int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138 = 18; + + private const int MIN_MATCH = 3; + private const int MAX_MATCH = 258; + private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private const int MAX_BITS = 15; + private const int D_CODES = 30; + private const int BL_CODES = 19; + private const int LENGTH_CODES = 29; + private const int LITERALS = 256; + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private const int END_BLOCK = 256; + + private static readonly IDictionary config_table = new Dictionary() { + {(CompressionLevel)0, new Config(0, 0, 0, 0, DefalteFlavor.STORED)}, + {(CompressionLevel)1, new Config(4, 4, 8, 4, DefalteFlavor.FAST)}, + {(CompressionLevel)2, new Config(4, 5, 16, 8, DefalteFlavor.FAST)}, + {(CompressionLevel)3, new Config(4, 6, 32, 32, DefalteFlavor.FAST)}, + + {(CompressionLevel)4, new Config(4, 4, 16, 16, DefalteFlavor.SLOW)}, + {(CompressionLevel)5, new Config(8, 16, 32, 32, DefalteFlavor.SLOW)}, + {(CompressionLevel)6, new Config(8, 16, 128, 128, DefalteFlavor.SLOW)}, + {(CompressionLevel)7, new Config(8, 32, 128, 256, DefalteFlavor.SLOW)}, + {(CompressionLevel)8, new Config(32, 128, 258, 1024, DefalteFlavor.SLOW)}, + {(CompressionLevel)9, new Config(32, 258, 258, 4096, DefalteFlavor.SLOW)}, + }; + + #region Static definitions + + private static readonly byte[] _length_code ={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + private static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + private static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // extra bits for each length code + private static readonly int[] extra_lbits ={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + private static readonly int[] extra_dbits ={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + private static readonly int[] extra_blbits ={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + private static readonly byte[] bl_order ={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + private static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + private static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + #endregion + + private static readonly String[] z_errmsg = { + "need dictionary", // ZLibStatus.Z_NEED_DICT 2 + "stream end", // ZLibStatus.Z_STREAM_END 1 + "", // ZLibStatus.Z_OK 0 + "file error", // ZLibStatus.Z_ERRNO (-1) + "stream error", // ZLibStatus.Z_STREAM_ERROR (-2) + "data error", // ZLibStatus.Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // ZLibStatus.Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + + + /// + /// as the name implies + /// + private int _status; + + private BlockType _dataType; + + /// + /// STORED (for zip only) or DEFLATED + /// + private byte _method; + + /// + /// size of pending_buf + /// + private int _pendingBufferSize; + + /// + /// LZ77 window size (32K by default) + /// + private int _windowSize; + + /// + /// log2(w_size) (8..16) + /// + private int _windowBits; + + /// + /// w_size - 1 + /// + private int _windowMask; + + /// + /// Sliding window. Input bytes are read into the second half of the window, + /// and move to the first half later to keep a dictionary of at least wSize + /// bytes. With this organization, matches are limited to a distance of + /// wSize-MAX_MATCH bytes, but this ensures that IO is always + /// performed with a length multiple of the block size. Also, it limits + /// the window size to 64K, which is quite useful on MSDOS. + /// To do: use the user input buffer as sliding window. + /// + private byte[] _window; + + /// + /// Actual size of window: 2*wSize, except when the user input buffer + /// is directly used as sliding window. + /// + private int _windowActualSize; + + /// + /// Link to older string with same hash index. To limit the size of this + /// array to 64K, this link is maintained only for the last 32K strings. + /// An index in this array is thus a window index modulo 32K. + /// + private short[] _previous; + + /// + /// Heads of the hash chains or NIL. + /// + private short[] _head; + + /// + /// hash index of string to be inserted + /// + private int _insertedHashIndex; + + /// + /// number of elements in hash table + /// + private int _hashSize; + + /// + /// log2(hash_size) + /// + private int _hashBits; + + /// + /// hash_size-1 + /// + private int _hashMask; + + /// + /// Number of bits by which ins_h must be shifted at each input + /// step. It must be such that after MIN_MATCH steps, the oldest + /// byte no longer takes part in the hash key, that is: + /// hash_shift * MIN_MATCH >= hash_bits + /// + private int _hashShift; + + /// + /// Window position at the beginning of the current output block. Gets + /// negative when the window is moved backwards. + /// + private int _blockStart; + + /// + /// length of best match + /// + private int _matchLength; + + /// + /// previous match + /// + private int _previousMatch; + + /// + /// set if previous match exists + /// + private bool _matchAvailable; + + /// + /// start of string to insert + /// + private int _startInsertString; + + /// + /// start of matching string + /// + private int _startMatchString; + + /// + /// number of valid bytes ahead in window + /// + private int _validBytesAhead; + + /// + /// Length of the best match at previous step. Matches not greater than this + /// are discarded. This is used in the lazy match evaluation. + /// + private int _previousLength; + + /// + /// To speed up deflation, hash chains are never searched beyond this + /// length. A higher limit improves compression ratio but degrades the speed. + /// + private int _maxChainLength; + + /// + /// Attempt to find a better match only when the current match is strictly + /// smaller than this value. This mechanism is used only for compression + /// levels >= 4. + /// + private int _maxLazyMatch; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + /// + /// The compression level (1..9) + /// + private CompressionLevel level; + + /// + /// The favor or force Huffman coding + /// + private CompressionStrategy strategy; + + /// + /// Use a faster search when the previous match is longer than this + /// + private int good_match; + + /// + /// Stop searching when current match exceeds this + /// + private int nice_match; + + /// + /// literal and length tree + /// + private short[] dyn_ltree; + + /// + /// The distance tree + /// + private short[] dyn_dtree; + + /// + /// The Huffman tree for bit lengths + /// + private short[] bl_tree; + + /// + /// The desc for literal tree + /// + private Tree l_desc = new Tree(); + + /// + /// The desc for distance tree + /// + private Tree d_desc = new Tree(); + + /// + /// The desc for bit length tree + /// + private Tree bl_desc = new Tree(); + + /// + /// index for literals or lengths + /// + private int l_buf; + + /// + /// Size of match buffer for literals/lengths. There are 4 reasons for + /// limiting lit_bufsize to 64K: + /// - frequencies can be kept in 16 bit counters + /// - if compression is not successful for the first block, all input + /// data is still in the window so we can still emit a stored block even + /// when input comes from standard input. (This can also be done for + /// all blocks if lit_bufsize is not greater than 32K.) + /// - if compression is not successful for a file smaller than 64K, we can + /// even emit a stored file instead of a stored block (saving 5 bytes). + /// This is applicable only for zip (not gzip or zlib). + /// - creating new Huffman trees less frequently may not provide fast + /// adaptation to changes in the input data statistics. (Take for + /// example a binary file with poorly compressible code followed by + /// a highly compressible string table.) Smaller buffer sizes give + /// fast adaptation but have of course the overhead of transmitting + /// trees more frequently. + /// - I can't count above 4 + /// + private int lit_bufsize; + + /// + /// running index in l_buf + /// + private int last_lit; + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + /// + /// The index of pendig_buf + /// + private int d_buf; + + /// + /// The number of string matches in current block + /// + private int matches; + + /// + /// The bit length of EOB code for last block + /// + private int last_eob_len; + + /// + /// Output buffer. bits are inserted starting at the bottom (least + /// significant bits). + /// + private uint bi_buf; + + /// + /// Number of valid bits in bi_buf. All bits above the last valid bit + /// are always zero. + /// + private int bi_valid; + + /// + /// value of flush param for previous deflate call + /// + private FlushType last_flush; + + /// + /// bit length of current block with optimal trees + /// + internal int opt_len; + + /// + /// bit length of current block with static trees + /// + internal int static_len; + + /// + /// The output still pending + /// + internal byte[] pending_buf; + + /// + /// next pending byte to output to the stream + /// + internal int pending_out; + + /// + /// nb of bytes in the pending buffer + /// + internal int pending; + + /// + /// suppress zlib header and adler32 + /// + internal int noheader; + + /// + /// number of codes at each bit length for an optimal tree + /// + internal short[] bl_count = new short[MAX_BITS + 1]; + + /// + /// heap used to build the Huffman trees + /// + internal int[] heap = new int[2 * L_CODES + 1]; + + /// + /// The number of elements in the heap + /// + internal int heap_len; + /// + /// The element of largest frequency + /// + internal int heap_max; + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + /// + /// Depth of each subtree used as tie breaker for trees of equal frequency + /// + internal byte[] depth = new byte[2 * L_CODES + 1]; + + public Deflate() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + public Deflate(CompressionLevel level) + : this() + { + this.deflateInit(level); + } + + public Deflate(CompressionLevel level, bool nowrap) + : this() + { + this.deflateInit(level, nowrap); + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + public ZLibStatus deflateInit(CompressionLevel level) + { + return deflateInit(level, MAX_WBITS); + } + public ZLibStatus deflateInit(CompressionLevel level, bool nowrap) + { + return deflateInit(level, MAX_WBITS, nowrap); + } + + public ZLibStatus deflateInit(CompressionLevel level, int bits, bool nowrap) + { + return this.deflateInit(level, nowrap ? -bits : bits); + } + + internal ZLibStatus deflateInit(CompressionLevel level, int bits) + { + return deflateInit2(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, CompressionStrategy.Z_DEFAULT_STRATEGY); + } + + private ZLibStatus deflateInit2(CompressionLevel level, int method, int windowBits, int memLevel, CompressionStrategy strategy) + { + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + base.msg = null; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) level = (CompressionLevel)6; + + if (windowBits < 0) + { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > (CompressionLevel)9 || + strategy < 0 || strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + this.noheader = noheader; + _windowBits = windowBits; + _windowSize = 1 << _windowBits; + _windowMask = _windowSize - 1; + + _hashBits = memLevel + 7; + _hashSize = 1 << _hashBits; + _hashMask = _hashSize - 1; + _hashShift = ((_hashBits + MIN_MATCH - 1) / MIN_MATCH); + + _window = new byte[_windowSize * 2]; + _previous = new short[_windowSize]; + _head = new short[_hashSize]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 4]; + _pendingBufferSize = lit_bufsize * 4; + + d_buf = lit_bufsize / 2; + l_buf = (1 + 2) * lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this._method = (byte)method; + + return deflateReset(); + } + + private ZLibStatus deflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; // + base.data_type = BlockType.Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (noheader < 0) + { + noheader = 0; // was set to -1 by deflate(..., FlushType.Z_FINISH); + } + _status = (noheader != 0) ? BUSY_STATE : INIT_STATE; + base.adler = Adler32.adler32(0, null, 0, 0); + + last_flush = FlushType.Z_NO_FLUSH; + + tr_init(); + lm_init(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus deflateEnd() + { + if (_status != INIT_STATE && _status != BUSY_STATE && _status != FINISH_STATE) + { + return ZLibStatus.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + _head = null; + _previous = null; + _window = null; + // free + // dstate=null; + return _status == BUSY_STATE ? ZLibStatus.Z_DATA_ERROR : ZLibStatus.Z_OK; + } + + public ZLibStatus deflateParams(CompressionLevel _level, CompressionStrategy _strategy) + { + ZLibStatus err = ZLibStatus.Z_OK; + + if (_level == CompressionLevel.Z_DEFAULT_COMPRESSION) + { + _level = (CompressionLevel)6; + } + if (_level < 0 || _level > (CompressionLevel)9 || + _strategy < 0 || _strategy > CompressionStrategy.Z_HUFFMAN_ONLY) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (config_table[level].Function != config_table[_level].Function && + base.total_in != 0) + { + // Flush the last buffer: + err = this.deflate(FlushType.Z_PARTIAL_FLUSH); + } + + if (level != _level) + { + level = _level; + _maxLazyMatch = config_table[level].MaxLazy; + good_match = config_table[level].GoodLength; + nice_match = config_table[level].NiceLength; + _maxChainLength = config_table[level].MaxChain; + } + strategy = _strategy; + return err; + } + + public ZLibStatus deflateSetDictionary(byte[] dictionary, int dictLength) + { + int length = dictLength; + int index = 0; + + if (dictionary == null || _status != INIT_STATE) + return ZLibStatus.Z_STREAM_ERROR; + + base.adler = Adler32.adler32(base.adler, dictionary, 0, dictLength); + + if (length < MIN_MATCH) return ZLibStatus.Z_OK; + if (length > _windowSize - MIN_LOOKAHEAD) + { + length = _windowSize - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, _window, 0, length); + _startInsertString = length; + _blockStart = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + _insertedHashIndex = _window[0] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[1] & 0xff)) & _hashMask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(n) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + _previous[n & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)n; + } + return ZLibStatus.Z_OK; + } + + private void lm_init() + { + _windowActualSize = 2 * _windowSize; + + _head[_hashSize - 1] = 0; + for (int i = 0; i < _hashSize - 1; i++) + { + _head[i] = 0; + } + + // Set the default configuration parameters: + _maxLazyMatch = Deflate.config_table[level].MaxLazy; + good_match = Deflate.config_table[level].GoodLength; + nice_match = Deflate.config_table[level].NiceLength; + _maxChainLength = Deflate.config_table[level].MaxChain; + + _startInsertString = 0; + _blockStart = 0; + _validBytesAhead = 0; + _matchLength = _previousLength = MIN_MATCH - 1; + _matchAvailable = false; + _insertedHashIndex = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void tr_init() + { + + l_desc.Init(dyn_ltree, Deflate.static_l_desc); + + d_desc.Init(dyn_dtree, Deflate.static_d_desc); + + bl_desc.Init(bl_tree, Deflate.static_bl_desc); + + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + private void init_block() + { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + private static bool smaller(short[] tree, int n, int m, byte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || + (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + private void scan_tree(short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + tree[(max_code + 1) * 2 + 1] = -1; // guard + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] += (short)count; + } + else if (curlen != 0) + { + if (curlen != prevlen) bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[REPZ_3_10 * 2]++; + } + else + { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + private int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.LargestCode); + scan_tree(dyn_dtree, d_desc.LargestCode); + + // Build the bit length tree: + bl_desc.BuildTree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Deflate.bl_order[max_blindex] * 2 + 1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + private void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Deflate.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + private void send_tree(short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_byte(byte[] p, int start, int len) + { + System.Array.Copy(p, start, pending_buf, pending, len); + pending += len; + } + + private void put_byte(byte c) + { + pending_buf[pending++] = c; + } + private void put_short(int w) + { + pending_buf[pending++] = (byte)(w/*&0xff*/); + pending_buf[pending++] = (byte)(w >> 8); + } + private void putShortMSB(int b) + { + pending_buf[pending++] = (byte)(b >> 8); + pending_buf[pending++] = (byte)(b/*&0xff*/); + } + + private void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + private void send_bits(int val, int length) + { + if (bi_valid > Buf_size - length) + { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } + else + { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } + // int len = length; + // if (bi_valid > (int)Buf_size - len) { + // int val = value; + // // bi_buf |= (val << bi_valid); + // bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); + // put_short(bi_buf); + // bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); + // bi_valid += len - Buf_size; + // } else { + // // bi_buf |= (value) << bi_valid; + // bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); + // bi_valid += len; + // } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + private void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, Deflate.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + private bool _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) + { + + pending_buf[d_buf + last_lit * 2] = (byte)(dist >> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte)dist; + + pending_buf[l_buf + last_lit] = (byte)lc; last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Deflate._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > (CompressionLevel)2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = _startInsertString - _blockStart; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) + { + out_length += (int)((int)dyn_dtree[dcode * 2] * + (5L + Deflate.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + private void compress_block(short[] ltree, short[] dtree) + { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | + (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (pending_buf[l_buf + lx]) & 0xff; lx++; + + if (dist == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // Here, lc is the match length - MIN_MATCH + code = Deflate._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Deflate.extra_lbits[code]; + if (extra != 0) + { + lc -= Deflate.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.DistanceCode(dist); + + send_code(code, dtree); // send the distance code + extra = Deflate.extra_dbits[code]; + if (extra != 0) + { + dist -= Deflate.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + private void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { bin_freq += dyn_ltree[n * 2]; n++; } + while (n < 128) { ascii_freq += dyn_ltree[n * 2]; n++; } + while (n < LITERALS) { bin_freq += dyn_ltree[n * 2]; n++; } + this._dataType = (bin_freq > (ascii_freq >> 2) ? BlockType.Z_BINARY : BlockType.Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + private void bi_flush() + { + if (bi_valid == 16) + { + pending_buf[pending++] = (byte)(bi_buf/*&0xff*/); + pending_buf[pending++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + pending_buf[pending++] = (byte)(bi_buf); + bi_buf >>= 8; + bi_buf &= 0x00ff; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + private void bi_windup() + { + if (bi_valid > 8) + { + pending_buf[pending++] = (byte)(bi_buf); + pending_buf[pending++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + pending_buf[pending++] = (byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + private void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ) + { + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(_window, buf, len); + } + + private void flush_block_only(bool eof) + { + _tr_flush_block(_blockStart >= 0 ? _blockStart : -1, + _startInsertString - _blockStart, + eof); + _blockStart = _startInsertString; + this.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + private int deflate_stored(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > _pendingBufferSize - 5) + { + max_block_size = _pendingBufferSize - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (_validBytesAhead <= 1) + { + fill_window(); + if (_validBytesAhead == 0 && flush == FlushType.Z_NO_FLUSH) return NeedMore; + if (_validBytesAhead == 0) break; // flush the current block + } + + _startInsertString += _validBytesAhead; + _validBytesAhead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = _blockStart + max_block_size; + if (_startInsertString == 0 || _startInsertString >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + _validBytesAhead = (int)(_startInsertString - max_start); + _startInsertString = (int)max_start; + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (_startInsertString - _blockStart >= _windowSize - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + return (flush == FlushType.Z_FINISH) ? FinishStarted : NeedMore; + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + private void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + private void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) + { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) + { + // Check if the file is ascii or binary + if (this._dataType == BlockType.Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.BuildTree(this); + + d_desc.BuildTree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(Deflate.static_ltree, Deflate.static_dtree); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.LargestCode + 1, d_desc.LargestCode + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void fill_window() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (_windowActualSize - _validBytesAhead - _startInsertString); + + // Deal with !@#$% 64K limit: + if (more == 0 && _startInsertString == 0 && _validBytesAhead == 0) + { + more = _windowSize; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (_startInsertString >= _windowSize + _windowSize - MIN_LOOKAHEAD) + { + System.Array.Copy(_window, _windowSize, _window, 0, _windowSize); + _startMatchString -= _windowSize; + _startInsertString -= _windowSize; // we now have strstart >= MAX_DIST + _blockStart -= _windowSize; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = _hashSize; + p = n; + do + { + m = (_head[--p] & 0xffff); + _head[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + } + while (--n != 0); + + n = _windowSize; + p = n; + do + { + m = (_previous[--p] & 0xffff); + _previous[p] = (short)(m >= _windowSize ? (m - _windowSize) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += _windowSize; + } + + if (base.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = this.read_buf(_window, _startInsertString + _validBytesAhead, more); + _validBytesAhead += n; + + // Initialize the hash value now that we have some input: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = _window[_startInsertString] & 0xff; + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (_validBytesAhead < MIN_LOOKAHEAD && base.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + private int deflate_fast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (_matchLength >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(_startInsertString - _startMatchString, _matchLength - MIN_MATCH); + + _validBytesAhead -= _matchLength; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (_matchLength <= _maxLazyMatch && + _validBytesAhead >= MIN_MATCH) + { + _matchLength--; // string at strstart already in hash table + do + { + _startInsertString++; + + _insertedHashIndex = ((_insertedHashIndex << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--_matchLength != 0); + _startInsertString++; + } + else + { + _startInsertString += _matchLength; + _matchLength = 0; + _insertedHashIndex = _window[_startInsertString] & 0xff; + + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[_startInsertString + 1] & 0xff)) & _hashMask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, _window[_startInsertString] & 0xff); + _validBytesAhead--; + _startInsertString++; + } + if (bflush) + { + + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + + flush_block_only(flush == FlushType.Z_FINISH); + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + private int deflate_slow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (_validBytesAhead < MIN_LOOKAHEAD) + { + fill_window(); + if (_validBytesAhead < MIN_LOOKAHEAD && flush == FlushType.Z_NO_FLUSH) + { + return NeedMore; + } + if (_validBytesAhead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (_validBytesAhead >= MIN_MATCH) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + + // Find the longest match, discarding those <= prev_length. + _previousLength = _matchLength; _previousMatch = _startMatchString; + _matchLength = MIN_MATCH - 1; + + if (hash_head != 0 && _previousLength < _maxLazyMatch && + ((_startInsertString - hash_head) & 0xffff) <= _windowSize - MIN_LOOKAHEAD + ) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != CompressionStrategy.Z_HUFFMAN_ONLY) + { + _matchLength = longest_match(hash_head); + } + // longest_match() sets match_start + + if (_matchLength <= 5 && (strategy == CompressionStrategy.Z_FILTERED || + (_matchLength == MIN_MATCH && + _startInsertString - _startMatchString > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + _matchLength = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (_previousLength >= MIN_MATCH && _matchLength <= _previousLength) + { + int max_insert = _startInsertString + _validBytesAhead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(_startInsertString - 1 - _previousMatch, _previousLength - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + _validBytesAhead -= _previousLength - 1; + _previousLength -= 2; + do + { + if (++_startInsertString <= max_insert) + { + _insertedHashIndex = (((_insertedHashIndex) << _hashShift) ^ (_window[(_startInsertString) + (MIN_MATCH - 1)] & 0xff)) & _hashMask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (_head[_insertedHashIndex] & 0xffff); + _previous[_startInsertString & _windowMask] = _head[_insertedHashIndex]; + _head[_insertedHashIndex] = (short)_startInsertString; + } + } + while (--_previousLength != 0); + _matchAvailable = false; + _matchLength = MIN_MATCH - 1; + _startInsertString++; + + if (bflush) + { + flush_block_only(false); + if (base.avail_out == 0) return NeedMore; + } + } + else if (_matchAvailable != false) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + _startInsertString++; + _validBytesAhead--; + if (base.avail_out == 0) return NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + _matchAvailable = true; + _startInsertString++; + _validBytesAhead--; + } + } + + if (_matchAvailable != false) + { + bflush = _tr_tally(0, _window[_startInsertString - 1] & 0xff); + _matchAvailable = false; + } + flush_block_only(flush == FlushType.Z_FINISH); + + if (base.avail_out == 0) + { + if (flush == FlushType.Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == FlushType.Z_FINISH ? FinishDone : BlockDone; + } + + private int longest_match(int cur_match) + { + int chain_length = _maxChainLength; // max hash chain length + int scan = _startInsertString; // current string + int match; // matched string + int len; // length of current match + int best_len = _previousLength; // best match length so far + int limit = _startInsertString > (_windowSize - MIN_LOOKAHEAD) ? + _startInsertString - (_windowSize - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = _windowMask; + + int strend = _startInsertString + MAX_MATCH; + byte scan_end1 = _window[scan + best_len - 1]; + byte scan_end = _window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (_previousLength >= good_match) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > _validBytesAhead) nice_match = _validBytesAhead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (_window[match + best_len] != scan_end || + _window[match + best_len - 1] != scan_end1 || + _window[match] != _window[scan] || + _window[++match] != _window[scan + 1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } while (_window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + _window[++scan] == _window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + _startMatchString = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = _window[scan + best_len - 1]; + scan_end = _window[scan + best_len]; + } + + } while ((cur_match = (_previous[cur_match & wmask] & 0xffff)) > limit + && --chain_length != 0); + + if (best_len <= _validBytesAhead) return best_len; + return _validBytesAhead; + } + + public ZLibStatus deflate(FlushType flush) + { + FlushType old_flush; + + if (flush > FlushType.Z_FINISH || flush < 0) + { + return ZLibStatus.Z_STREAM_ERROR; + } + + if (base.next_out == null || + (base.next_in == null && base.avail_in != 0) || + (_status == FINISH_STATE && flush != FlushType.Z_FINISH)) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_STREAM_ERROR)]; + return ZLibStatus.Z_STREAM_ERROR; + } + if (base.avail_out == 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (_status == INIT_STATE) + { + int header = (Z_DEFLATED + ((this._windowBits - 8) << 4)) << 8; + int level_flags = (((int)this.level - 1) & 0xff) >> 1; + + if (level_flags > 3) level_flags = 3; + header |= (level_flags << 6); + if (this._startInsertString != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + _status = BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if (this._startInsertString != 0) + { + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + } + base.adler = Adler32.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pending != 0) + { + this.flush_pending(); + if (base.avail_out == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = (FlushType)(-1); + return ZLibStatus.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (base.avail_in == 0 && flush <= old_flush && + flush != FlushType.Z_FINISH) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (_status == FINISH_STATE && base.avail_in != 0) + { + base.msg = z_errmsg[ZLibStatus.Z_NEED_DICT - (ZLibStatus.Z_BUF_ERROR)]; + return ZLibStatus.Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (base.avail_in != 0 || this._validBytesAhead != 0 || + (flush != FlushType.Z_NO_FLUSH && _status != FINISH_STATE)) + { + int bstate = -1; + switch (config_table[level].Function) + { + case DefalteFlavor.STORED: + bstate = deflate_stored(flush); + break; + case DefalteFlavor.FAST: + bstate = deflate_fast(flush); + break; + case DefalteFlavor.SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate == FinishStarted || bstate == FinishDone) + { + _status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) + { + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR next call, see above + } + return ZLibStatus.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) + { + if (flush == FlushType.Z_PARTIAL_FLUSH) + { + _tr_align(); + } + else + { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Z_FULL_FLUSH) + { + //state.head[s.hash_size-1]=0; + for (int i = 0; i < this._hashSize/*-1*/; i++) // forget history + this._head[i] = 0; + } + } + this.flush_pending(); + if (base.avail_out == 0) + { + last_flush = (FlushType)(-1); // avoid BUF_ERROR at next call, see above + return ZLibStatus.Z_OK; + } + } + } + + if (flush != FlushType.Z_FINISH) return ZLibStatus.Z_OK; + if (noheader != 0) return ZLibStatus.Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(base.adler >> 16)); + putShortMSB((int)(base.adler & 0xffff)); + this.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? ZLibStatus.Z_OK : ZLibStatus.Z_STREAM_END; + } + + + private static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Deflate.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + private static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Deflate.extra_dbits, 0, D_CODES, MAX_BITS); + + private static readonly StaticTree static_bl_desc = new StaticTree(null, Deflate.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + private class Config + { + /// + /// Reduce lazy search above this match length + /// + internal int GoodLength { get; private set; } + /// + /// Gets or sets whether not to perform lazy search above this match length + /// + /// + /// The max lazy. + /// + internal int MaxLazy { get; private set; } + /// + /// Gets or sets whether to quit search above this match length + /// + /// + /// The length of the nice. + /// + internal int NiceLength { get; private set; } + internal int MaxChain { get; private set; } + internal DefalteFlavor Function { get; private set; } + internal Config(int goodLength, int maxLazy, int niceLength, int maxChain, DefalteFlavor function) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChain = maxChain; + this.Function = function; + } + } + + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = this.pending; + + if (len > avail_out) len = avail_out; + if (len == 0) return; + + if (this.pending_buf.Length <= this.pending_out || + next_out.Length <= next_out_index || + this.pending_buf.Length < (this.pending_out + len) || + next_out.Length < (next_out_index + len)) + { + // System.out.println(this.pending_buf.length+", "+this.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(this.pending_buf, this.pending_out, + next_out, next_out_index, len); + + next_out_index += len; + this.pending_out += len; + total_out += len; + avail_out -= len; + this.pending -= len; + if (this.pending == 0) + { + this.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + avail_in -= len; + + if (this.noheader == 0) + { + adler = Adler32.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public ZLibStatus deflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.deflate(flushType); + } + + + // TODO: Delete this + public ZLibStatus inflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/Inflate.cs b/Renci.SshNet/Compression/v9/Inflate.cs new file mode 100644 index 0000000..c05e2d8 --- /dev/null +++ b/Renci.SshNet/Compression/v9/Inflate.cs @@ -0,0 +1,524 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateMode : int + { + /// + ///waiting for method byte + /// + METHOD = 0, + /// + /// waiting for flag byte + /// + FLAG = 1, + /// + /// four dictionary check bytes to go + /// + DICT4 = 2, + /// + /// three dictionary check bytes to go + /// + DICT3 = 3, + /// + /// two dictionary check bytes to go + /// + DICT2 = 4, + /// + /// one dictionary check byte to go + /// + DICT1 = 5, + /// + /// waiting for inflateSetDictionary + /// + DICT0 = 6, + /// + /// decompressing blocks + /// + BLOCKS = 7, + /// + /// four check bytes to go + /// + CHECK4 = 8, + /// + /// three check bytes to go + /// + CHECK3 = 9, + /// + /// two check bytes to go + /// + CHECK2 = 10, + /// + /// one check byte to go + /// + CHECK1 = 11, + /// + /// finished check, done + /// + DONE = 12, + /// + /// got an error--stay here + /// + BAD = 13 + } + + internal sealed class Inflate : ZStream, ICompressor + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private static readonly byte[] mark = { (byte)0, (byte)0, (byte)0xff, (byte)0xff }; + + /// + /// The current inflate mode + /// + private InflateMode _mode; + + /// + /// if FLAGS, method byte + /// + private int method; + + /// + /// Computed check value + /// + private long[] _was = new long[1]; + + /// + /// The stream check value + /// + private long _need; + + /// + /// if BAD, inflateSync's marker bytes count + /// + private int _marker; + + // mode independent information + /// + /// Flag for no wrapper + /// + private int _nowrap; + + /// + /// log2(window size) (8..15, defaults to 15) + /// + private int _wbits; + + /// + /// Current inflate_blocks state + /// + private InflateBlocks _blocks; + + public Inflate() + { + this.inflateInit(); + } + + public Inflate(bool nowrap) + { + this.inflateInit(nowrap); + } + + private ZLibStatus InflateReset() + { + base.total_in = base.total_out = 0; + base.msg = null; + this._mode = this._nowrap != 0 ? InflateMode.BLOCKS : InflateMode.METHOD; + this._blocks.reset(this, null); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateEnd() + { + if (_blocks != null) + _blocks.free(this); + _blocks = null; + // ZFREE(z, z->state); + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateInit(int w) + { + base.msg = null; + _blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + _nowrap = 0; + if (w < 0) + { + w = -w; + _nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + InflateEnd(); + return ZLibStatus.Z_STREAM_ERROR; + } + _wbits = w; + + this._blocks = new InflateBlocks(this, this._nowrap != 0 ? null : this, 1 << w); + + // reset state + InflateReset(); + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflate(FlushType ff) + { + ZLibStatus r; + int b; + + if (base.next_in == null) + return ZLibStatus.Z_STREAM_ERROR; + var f = ff == FlushType.Z_FINISH ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + r = ZLibStatus.Z_BUF_ERROR; + while (true) + { + //System.out.println("mode: "+this.mode); + switch (this._mode) + { + case InflateMode.METHOD: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + if (((this.method = base.next_in[base.next_in_index++]) & 0xf) != Z_DEFLATED) + { + this._mode = InflateMode.BAD; + base.msg = "unknown compression method"; + this._marker = 5; // can't try inflateSync + break; + } + if ((this.method >> 4) + 8 > this._wbits) + { + this._mode = InflateMode.BAD; + base.msg = "invalid window size"; + this._marker = 5; // can't try inflateSync + break; + } + this._mode = InflateMode.FLAG; + goto case InflateMode.FLAG; + case InflateMode.FLAG: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + b = (base.next_in[base.next_in_index++]) & 0xff; + + if ((((this.method << 8) + b) % 31) != 0) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect header check"; + this._marker = 5; // can't try inflateSync + break; + } + + if ((b & PRESET_DICT) == 0) + { + this._mode = InflateMode.BLOCKS; + break; + } + this._mode = InflateMode.DICT4; + goto case InflateMode.DICT4; + case InflateMode.DICT4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.DICT3; + goto case InflateMode.DICT3; + case InflateMode.DICT3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.DICT2; + goto case InflateMode.DICT2; + case InflateMode.DICT2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.DICT1; + goto case InflateMode.DICT1; + case InflateMode.DICT1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + base.adler = this._need; + this._mode = InflateMode.DICT0; + return ZLibStatus.Z_NEED_DICT; + case InflateMode.DICT0: + this._mode = InflateMode.BAD; + base.msg = "need dictionary"; + this._marker = 0; // can try inflateSync + return ZLibStatus.Z_STREAM_ERROR; + case InflateMode.BLOCKS: + + r = this._blocks.proc(this, r); + if (r == ZLibStatus.Z_DATA_ERROR) + { + this._mode = InflateMode.BAD; + this._marker = 0; // can try inflateSync + break; + } + if (r == ZLibStatus.Z_OK) + { + r = f; + } + if (r != ZLibStatus.Z_STREAM_END) + { + return r; + } + r = f; + this._blocks.reset(this, this._was); + if (this._nowrap != 0) + { + this._mode = InflateMode.DONE; + break; + } + this._mode = InflateMode.CHECK4; + goto case InflateMode.CHECK4; + case InflateMode.CHECK4: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need = ((base.next_in[base.next_in_index++] & 0xff) << 24) & 0xff000000L; + this._mode = InflateMode.CHECK3; + goto case InflateMode.CHECK3; + case InflateMode.CHECK3: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 16) & 0xff0000L; + this._mode = InflateMode.CHECK2; + goto case InflateMode.CHECK2; + case InflateMode.CHECK2: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += ((base.next_in[base.next_in_index++] & 0xff) << 8) & 0xff00L; + this._mode = InflateMode.CHECK1; + goto case InflateMode.CHECK1; + case InflateMode.CHECK1: + + if (base.avail_in == 0) return r; r = f; + + base.avail_in--; base.total_in++; + this._need += (base.next_in[base.next_in_index++] & 0xffL); + + if (((int)(this._was[0])) != ((int)(this._need))) + { + this._mode = InflateMode.BAD; + base.msg = "incorrect data check"; + this._marker = 5; // can't try inflateSync + break; + } + + this._mode = InflateMode.DONE; + goto case InflateMode.DONE; + case InflateMode.DONE: + return ZLibStatus.Z_STREAM_END; + case InflateMode.BAD: + return ZLibStatus.Z_DATA_ERROR; + default: + return ZLibStatus.Z_STREAM_ERROR; + } + } + } + + public ZLibStatus InflateSetDictionary(byte[] dictionary, int dictLength) + { + int index = 0; + int length = dictLength; + if (this._mode != InflateMode.DICT0) + return ZLibStatus.Z_STREAM_ERROR; + + if (Adler32.adler32(1L, dictionary, 0, dictLength) != base.adler) + { + return ZLibStatus.Z_DATA_ERROR; + } + + base.adler = Adler32.adler32(0, null, 0, 0); + + if (length >= (1 << this._wbits)) + { + length = (1 << this._wbits) - 1; + index = dictLength - length; + } + this._blocks.set_dictionary(dictionary, index, length); + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus InflateSync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (this._mode != InflateMode.BAD) + { + this._mode = InflateMode.BAD; + this._marker = 0; + } + if ((n = base.avail_in) == 0) + return ZLibStatus.Z_BUF_ERROR; + p = base.next_in_index; + m = this._marker; + + // search + while (n != 0 && m < 4) + { + if (base.next_in[p] == mark[m]) + { + m++; + } + else if (base.next_in[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + base.total_in += p - base.next_in_index; + base.next_in_index = p; + base.avail_in = n; + this._marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZLibStatus.Z_DATA_ERROR; + } + r = base.total_in; w = base.total_out; + InflateReset(); + base.total_in = r; base.total_out = w; + this._mode = InflateMode.BLOCKS; + return ZLibStatus.Z_OK; + } + + public ZLibStatus inflateInit() + { + return inflateInit(DEF_WBITS); + } + public ZLibStatus inflateInit(bool nowrap) + { + return inflateInit(DEF_WBITS, nowrap); + } + public ZLibStatus inflateInit(int w) + { + return inflateInit(w, false); + } + + public ZLibStatus inflateInit(int w, bool nowrap) + { + return this.InflateInit(nowrap ? -w : w); + } + + public ZLibStatus inflateEnd() + { + var ret = this.InflateEnd(); + return ret; + } + public ZLibStatus inflateSync() + { + return this.InflateSync(); + } + + public ZLibStatus inflateSetDictionary(byte[] dictionary, int dictLength) + { + return this.InflateSetDictionary(dictionary, dictLength); + } + + public ZLibStatus inflate(byte[] inputBufer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_in = inputBufer; + this.next_in_index = inputOffset; + this.avail_in = inputCount; + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + public ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + this.next_out = outputBuffer; + this.next_out_index = outputOffset; + this.avail_out = outputCount; + return this.inflate(flushType); + } + + // TODO: Dlete this + public ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType) + { + throw new NotImplementedException(); + } + + public ZLibStatus deflateEnd() + { + throw new NotImplementedException(); + } + + public ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/InflateBlocks.cs b/Renci.SshNet/Compression/v9/InflateBlocks.cs new file mode 100644 index 0000000..273389f --- /dev/null +++ b/Renci.SshNet/Compression/v9/InflateBlocks.cs @@ -0,0 +1,721 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateBlockMode + { + /// + /// get type bits (3, including end bit) + /// + TYPE = 0, + /// + /// get lengths for stored + /// + LENS = 1, + /// + /// cessing stored block + /// + STORED = 2, + /// + /// get table lengths + /// + TABLE = 3, + /// + /// get bit lengths tree for a dynamic block + /// + BTREE = 4, + /// + /// get length, distance trees for a dynamic block + /// + DTREE = 5, + /// + /// processing fixed or dynamic block + /// + CODES = 6, + /// + /// output remaining window bytes + /// + DRY = 7, + /// + /// finished last block, done + /// + DONE = 8, + /// + /// ot a data error--stuck here + /// + BAD = 9 + } + + internal sealed class InflateBlocks : InflateCodes + { + private const int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + internal InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + //internal InfCodes codes = new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InflateTree inftree = new InflateTree(); + + internal InflateBlocks(ZStream z, Object checkfn, int w) + { + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c) + { + if (c != null) c[0] = check; + if (mode == InflateBlockMode.BTREE || mode == InflateBlockMode.DTREE) + { + } + if (mode == InflateBlockMode.CODES) + { + codesfree(z); + } + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + + if (checkfn != null) + z.adler = check = Adler32.adler32(0L, null, 0, 0); + } + + internal ZLibStatus proc(ZStream z, ZLibStatus r) + { + int t; // temporary storage + ZLibStatus tt; + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; + { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + } + { + q = write; m = (int)(q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1) + { + case 0: + { // stored + b >>= (3); k -= (3); + } + t = k & 7; + { // go to byte boundary + + b >>= (t); k -= (t); + } + mode = InflateBlockMode.LENS; // get length of stored block + break; + case 1: + { // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + + InflateTree.InflateTreesFixed(bl, bd, tl, td, z); + codesinit(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } + { + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.CODES; + break; + case 2: + { // dynamic + + b >>= (3); k -= (3); + } + + mode = InflateBlockMode.TABLE; + break; + case 3: + { // illegal + + b >>= (3); k -= (3); + } + mode = InflateBlockMode.BAD; + z.msg = "invalid block type"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + break; + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + z.msg = "invalid stored block lengths"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + if (m == 0) + { + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (q == end && read != 0) + { + q = 0; m = (int)(q < read ? read - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + t = left; + if (t > n) t = n; + if (t > m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + z.msg = "too many length or distance symbols"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + for (int i = 0; i < t; i++) { blens[i] = 0; } + } + { + + b >>= (14); k -= (14); + } + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + { + + b >>= (3); k -= (3); + } + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + tt = inftree.InflateTreesBits(blens, bb, tb, hufts, z); + if (tt != ZLibStatus.Z_OK) + { + r = tt; + if (r == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < (t)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) + { + //System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= (t); k -= (t); + blens[index++] = c; + } + else + { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZLibStatus.Z_OK; + } + else + { + bitb = b; bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + }; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>= (t); k -= (t); + + j += (b & inflate_mask[i]); + + b >>= (i); k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + z.msg = "invalid bit length repeat"; + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + + c = c == 16 ? blens[i - 1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + tt = inftree.InflateTreesDynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (tt != ZLibStatus.Z_OK) + { + if (tt == ZLibStatus.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = tt; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + codesinit(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + case InflateBlockMode.CODES: + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + + if ((r = codesproc(this, z, r)) != ZLibStatus.Z_STREAM_END) + { + return inflate_flush(z, r); + } + r = ZLibStatus.Z_OK; + codesfree(z); + + p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; + q = write; m = (int)(q < read ? read - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + case InflateBlockMode.DRY: + write = q; + r = inflate_flush(z, r); + q = write; m = (int)(q < read ? read - q - 1 : end - q); + if (read != write) + { + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + case InflateBlockMode.DONE: + r = ZLibStatus.Z_STREAM_END; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + case InflateBlockMode.BAD: + r = ZLibStatus.Z_DATA_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + bitb = b; bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + write = q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z) + { + reset(z, null); + window = null; + hufts = null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n) + { + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or FlushType.Z_FULL_FLUSH. + internal ZLibStatus sync_point() + { + return mode == InflateBlockMode.LENS ? ZLibStatus.Z_STREAM_END : ZLibStatus.Z_OK; + } + + // copy as much as possible from the sliding window to the output area + internal ZLibStatus inflate_flush(ZStream z, ZLibStatus r) + { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) + { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n != 0 && r == ZLibStatus.Z_BUF_ERROR) r = ZLibStatus.Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (checkfn != null) + z.adler = check = Adler32.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/InflateCodes.cs b/Renci.SshNet/Compression/v9/InflateCodes.cs new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/v9/InflateCodes.cs @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/InflateTree.cs b/Renci.SshNet/Compression/v9/InflateTree.cs new file mode 100644 index 0000000..f9da579 --- /dev/null +++ b/Renci.SshNet/Compression/v9/InflateTree.cs @@ -0,0 +1,555 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + internal sealed class InflateTree + { + private const int MANY = 1440; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private ZLibStatus HuftBuild(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) + { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return ZLibStatus.Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return ZLibStatus.Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { // (note: doesn't matter for fixed) + return ZLibStatus.Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (byte)j; // bits in this table + r[1] = (byte)l; // bits to dump before this table + j = i >> (w - l); + r[2] = (int)(q - u[h - 1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (byte)(e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + { + System.Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>= 1) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? ZLibStatus.Z_BUF_ERROR : ZLibStatus.Z_OK; + } + + internal ZLibStatus InflateTreesBits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + InitWorkArea(19); + hn[0] = 0; + result = HuftBuild(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR || bb[0] == 0) + { + z.msg = "incomplete dynamic bit lengths tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + internal ZLibStatus InflateTreesDynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) + { + ZLibStatus result; + + // build literal/length tree + InitWorkArea(288); + hn[0] = 0; + result = HuftBuild(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != ZLibStatus.Z_OK || bl[0] == 0) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed literal/length tree"; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "incomplete literal/length tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + // build distance tree + InitWorkArea(288); + result = HuftBuild(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != ZLibStatus.Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == ZLibStatus.Z_DATA_ERROR) + { + z.msg = "oversubscribed distance tree"; + } + else if (result == ZLibStatus.Z_BUF_ERROR) + { + z.msg = "incomplete distance tree"; + result = ZLibStatus.Z_DATA_ERROR; + } + else if (result != ZLibStatus.Z_MEM_ERROR) + { + z.msg = "empty distance tree with lengths"; + result = ZLibStatus.Z_DATA_ERROR; + } + return result; + } + + return ZLibStatus.Z_OK; + } + + internal static ZLibStatus InflateTreesFixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return ZLibStatus.Z_OK; + } + + private void InitWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.Length < vsize) { v = new int[vsize]; } + for (int i = 0; i < vsize; i++) { v[i] = 0; } + for (int i = 0; i < BMAX + 1; i++) { c[i] = 0; } + for (int i = 0; i < 3; i++) { r[i] = 0; } + // for(int i=0; i + /// Gets the static tree or null + /// + /// + /// The static_tree. + /// + public short[] TreeData { get; private set; } + + /// + /// Gets the extra bits for each code or null. + /// + /// + /// The extra bits. + /// + public int[] ExtraBits { get; private set; } + + /// + /// Gets the base index for extra_bits. + /// + /// + /// The extra base. + /// + public int ExtraBase { get; private set; } + + /// + /// Gets the max number of elements in the tree. + /// + /// + /// The elements. + /// + public int Elements { get; private set; } + + /// + /// Gets the max bit length for the codes. + /// + /// + /// The length of the max. + /// + public int MaxLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The tree data. + /// The extra bits. + /// The extra base. + /// The elements. + /// Length of the max. + public StaticTree(short[] treeData, int[] extraBits, int extraBase, int elements, int maxLength) + { + this.TreeData = treeData; + this.ExtraBits = extraBits; + this.ExtraBase = extraBase; + this.Elements = elements; + this.MaxLength = maxLength; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/Tree.cs b/Renci.SshNet/Compression/v9/Tree.cs new file mode 100644 index 0000000..d2142c9 --- /dev/null +++ b/Renci.SshNet/Compression/v9/Tree.cs @@ -0,0 +1,334 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + internal sealed class Tree + { + private const int MAX_BITS = 15; + + private const int LITERALS = 256; + + private const int LENGTH_CODES = 29; + + private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + private const int HEAP_SIZE = (2 * L_CODES + 1); + + private static byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + + /// + /// The corresponding static tree + /// + private StaticTree _staticTree; + + /// + /// The dynamic tree + /// + private short[] _dynamicTree; + + /// + /// Gets the largest code with non zero frequency. + /// + /// + /// The max_code. + /// + public int LargestCode { get; private set; } + + public void Init(short[] dynamicTree, StaticTree staticTree) + { + this._staticTree = staticTree; + this._dynamicTree = dynamicTree; + } + + /// + /// Mapping from a distance to a distance code. dist is the distance - 1 and must not have side effects. _dist_code[256] and _dist_code[257] are never used. + /// + /// The dist. + /// + public static int DistanceCode(int dist) + { + // TODO: Under multiple port forward after more then 50K requests gets index out of range + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >> 7)]); + } + + /// + /// Construct one Huffman tree and assigns the code bit strings and lengths. Update the total bit length for the current block. + /// + /// The s. + public void BuildTree(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int elems = _staticTree.Elements; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree != null) s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.LargestCode = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short)(tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + ComputeBitLength(s); + + // The field len is now set, we can generate the bit codes + GenerateCodes(tree, max_code, s.bl_count); + } + + /// + /// Compute the optimal bit lengths for a tree and update the total bit length for the current block. + /// + /// The s. + private void ComputeBitLength(Deflate s) + { + short[] tree = _dynamicTree; + short[] stree = _staticTree.TreeData; + int[] extra = _staticTree.ExtraBits; + int based = _staticTree.ExtraBase; + int max_length = _staticTree.MaxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { bits = max_length; overflow++; } + tree[n * 2 + 1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > LargestCode) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n - based]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > LargestCode) continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len += (int)(((long)bits - (long)tree[m * 2 + 1]) * (long)tree[m * 2]); + tree[m * 2 + 1] = (short)bits; + } + n--; + } + } + } + + /// + /// Generate the codes for a given tree and bit counts (which need not be optimal). + /// + /// The tree to decorate. + /// The largest code with non zero frequency. + /// The number of codes at each bit length. + private static void GenerateCodes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) + { + next_code[bits] = code = (short)((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1< + /// Reverse the first len bits of a code, using straightforward code (a faster method would use a table) + /// + /// The value to invert. + /// The bit length. + /// + private static int BiReverse(int code, int len) + { + int res = 0; + do + { + res |= code & 1; + code >>= 1; + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/ZInputStream.cs b/Renci.SshNet/Compression/v9/ZInputStream.cs new file mode 100644 index 0000000..0ef972c --- /dev/null +++ b/Renci.SshNet/Compression/v9/ZInputStream.cs @@ -0,0 +1,202 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +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 THE AUTHORS +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. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using Renci.SshNet.Compression; +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream : Stream + { + private const int BufferSize = 512; + //private ZStream z = new ZStream(); + private ICompressor _compressor; + + // TODO Allow custom buf + private byte[] _buffer = new byte[BufferSize]; + private CompressionMode _compressionMode; + private Stream _input; + private bool _isClosed; + private bool _noMoreInput = false; + + public FlushType FlushMode { get; private set; } + + public ZInputStream() + { + this.FlushMode = FlushType.Z_PARTIAL_FLUSH; + } + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Inflate(nowrap); + //this._compressor.inflateInit(nowrap); + this._compressionMode = CompressionMode.Decompress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + public ZInputStream(Stream input, CompressionLevel level) + : this() + { + Debug.Assert(input.CanRead); + + this._input = input; + this._compressor = new Deflate(level); + //this._compressor.deflateInit(level); + this._compressionMode = CompressionMode.Compress; + this._compressor.next_in = _buffer; + this._compressor.next_in_index = 0; + this._compressor.avail_in = 0; + } + + #region Stream implementation + + public sealed override bool CanRead + { + get { return !_isClosed; } + } + + public sealed override bool CanSeek + { + get { return false; } + } + + public sealed override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count == 0) + return 0; + + //this._compressor.next_out = buffer; + //this._compressor.next_out_index = offset; + //this._compressor.avail_out = count; + + ZLibStatus err = ZLibStatus.Z_OK; + do + { + if (this._compressor.avail_in == 0 && !_noMoreInput) + { + // if buffer is empty and more input is available, refill it + this._compressor.next_in_index = 0; + this._compressor.avail_in = _input.Read(_buffer, 0, _buffer.Length); //(bufsize 0 || this._compressor.avail_out == 0); + } + + #endregion + + public override void Close() + { + if (this._isClosed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this._isClosed = true; + End(); + _output.Close(); + _output = null; + } + } + + private void End() + { + if (this._compressor == null) + return; + switch (this._compressionMode) + { + case CompressionMode.Compress: + this._compressor.deflateEnd(); + break; + case CompressionMode.Decompress: + this._compressor.inflateEnd(); + break; + default: + break; + } + + //this._compressor.free(); + this._compressor = null; + } + + private void Finish() + { + do + { + var err = ZLibStatus.Z_OK; + switch (this._compressionMode) + { + case CompressionMode.Compress: + err = this._compressor.deflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + case CompressionMode.Decompress: + err = this._compressor.inflate(_buffer, 0, _buffer.Length, FlushType.Z_FINISH); + break; + default: + break; + } + + if (err != ZLibStatus.Z_STREAM_END && err != ZLibStatus.Z_OK) + // TODO + // throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + //throw new IOException((_compressionMode ? "de" : "in") + "flating: " + z.msg); + throw new IOException(string.Format("{0}ion error: {1}", _compressionMode, err)); + + int count = _buffer.Length - this._compressor.avail_out; + if (count > 0) + { + _output.Write(_buffer, 0, count); + } + } + while (this._compressor.avail_in > 0 || this._compressor.avail_out == 0); + + Flush(); + } + } +} diff --git a/Renci.SshNet/Compression/v9/ZStream.cs b/Renci.SshNet/Compression/v9/ZStream.cs new file mode 100644 index 0000000..b151ba1 --- /dev/null +++ b/Renci.SshNet/Compression/v9/ZStream.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public interface ICompressor + { + ZLibStatus deflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + ZLibStatus deflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + + ZLibStatus deflateEnd(); + + ZLibStatus inflate(byte[] bufer, int offset, int count, byte[] p1, int p2, int p3, FlushType flushType); + ZLibStatus inflate(byte[] outputBuffer, int outputOffset, int outputCount, FlushType flushType); + + ZLibStatus inflateEnd(); + + //ZLibStatus inflate(FlushType flushType); + + int avail_out { get; set; } + + int avail_in { get; set; } + + //int next_out_index { get; set; } + + //byte[] next_out { get; set; } + + byte[] next_in { get; set; } + + int next_in_index { get; set; } + } + + + public abstract class ZStream + { + + protected const int MAX_WBITS = 15; // 32K LZ77 window + protected const int DEF_WBITS = MAX_WBITS; + + private const int MAX_MEM_LEVEL = 9; + + public byte[] next_in { get; set; } // next input byte + public int next_in_index { get; set; } + public int avail_in { get; set; } // number of bytes available at next_in + public long total_in { get; set; } // total nb of input bytes read so far + + public byte[] next_out { get; set; } // next output byte should be put there + public int next_out_index { get; set; } + public int avail_out { get; set; } // remaining free space at next_out + public long total_out { get; set; } // total nb of bytes output so far + + public String msg { get; set; } + + internal BlockType data_type; // best guess about the data type: ascii or binary + + public long adler; + + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/Zlib.cs b/Renci.SshNet/Compression/v9/Zlib.cs new file mode 100644 index 0000000..5890675 --- /dev/null +++ b/Renci.SshNet/Compression/v9/Zlib.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib" compression implementation + /// + internal class Zlib : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + this.IsActive = true; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/ZlibOpenSsh.cs b/Renci.SshNet/Compression/v9/ZlibOpenSsh.cs new file mode 100644 index 0000000..f82a4c0 --- /dev/null +++ b/Renci.SshNet/Compression/v9/ZlibOpenSsh.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Renci.SshNet.Compression +{ + /// + /// Represents "zlib@openssh.org" compression implementation + /// + public class ZlibOpenSsh : Compressor + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "zlib@openssh.org"; } + } + + /// + /// Initializes the algorithm + /// + /// The session. + public override void Init(Session session) + { + base.Init(session); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this.IsActive = true; + this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/Compression/v9/ZlibStream.cs b/Renci.SshNet/Compression/v9/ZlibStream.cs new file mode 100644 index 0000000..fb5f9d2 --- /dev/null +++ b/Renci.SshNet/Compression/v9/ZlibStream.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Renci.SshNet.Compression +{ + /// + /// Implements Zlib compression algorithm. + /// + public class ZlibStream + { + private readonly Stream _baseStream; + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The mode. + public ZlibStream(Stream stream, CompressionMode mode) + { + switch (mode) + { + case CompressionMode.Compress: + //this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + this._baseStream = new ZOutputStream(stream, CompressionLevel.Z_DEFAULT_COMPRESSION); + break; + case CompressionMode.Decompress: + this._baseStream = new ZOutputStream(stream); + break; + default: + break; + } + + //this._baseStream.FlushMode = Ionic.Zlib.FlushType.Partial; + } + + /// + /// Writes the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + public void Write(byte[] buffer, int offset, int count) + { + this._baseStream.Write(buffer, offset, count); + } + } +} diff --git a/Renci.SshNet/Compression/{B60A61FD-33DF-43A1-B48C-A8E3DB53FA13}.tmp b/Renci.SshNet/Compression/{B60A61FD-33DF-43A1-B48C-A8E3DB53FA13}.tmp new file mode 100644 index 0000000..3214e56 --- /dev/null +++ b/Renci.SshNet/Compression/{B60A61FD-33DF-43A1-B48C-A8E3DB53FA13}.tmp @@ -0,0 +1,690 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +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. + */ + +namespace Org.BouncyCastle.Utilities.Zlib +{ + + public enum InflateCodeMode + { + /// + /// x: set up for InflateCodeMode.LEN + /// + START = 0, + /// + /// i: get length/literal/eob next + /// + LEN = 1, + /// + /// i: getting length extra (have base) + /// + LENEXT = 2, + /// + /// i: get distance next + /// + DIST = 3, + /// + /// : getting distance extra + /// + DISTEXT = 4, + /// + /// o: copying bytes in window, waiting for space + /// + COPY = 5, + /// + /// o: got literal, waiting for output space + /// + LIT = 6, + /// + /// o: got eob, possibly still output waiting + /// + WASH = 7, + /// + /// x: got eob and all data flushed + /// + END = 8, + /// + /// x: got error + /// + BADCODE = 9 + } + + internal abstract class InflateCodes// : ZStream + { + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + InflateCodeMode mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InflateCodes() + { + } + internal void codesinit(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z) + { + mode = InflateCodeMode.START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal ZLibStatus codesproc(InflateBlocks s, ZStream z, ZLibStatus r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case InflateCodeMode.START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != ZLibStatus.Z_OK) + { + mode = r == ZLibStatus.Z_STREAM_END ? InflateCodeMode.WASH : InflateCodeMode.BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = InflateCodeMode.LEN; + goto case InflateCodeMode.LEN; + case InflateCodeMode.LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { // literal + lit = tree[tindex + 2]; + mode = InflateCodeMode.LIT; + break; + } + if ((e & 16) != 0) + { // length + get = e & 15; + len = tree[tindex + 2]; + mode = InflateCodeMode.LENEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { // end of block + mode = InflateCodeMode.WASH; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = InflateCodeMode.DIST; + goto case InflateCodeMode.DIST; + case InflateCodeMode.DIST: // i: get distance next + j = need; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) + { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = InflateCodeMode.DISTEXT; + break; + } + if ((e & 64) == 0) + { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = InflateCodeMode.BADCODE; // invalid code + z.msg = "invalid distance code"; + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) + { + if (n != 0) r = ZLibStatus.Z_OK; + else + { + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + n--; b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = InflateCodeMode.COPY; + goto case InflateCodeMode.COPY; + case InflateCodeMode.COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) + { + + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + s.window[q++] = s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = InflateCodeMode.START; + break; + case InflateCodeMode.LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { q = 0; m = q < s.read ? s.read - q - 1 : s.end - q; } + if (m == 0) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + r = ZLibStatus.Z_OK; + + s.window[q++] = (byte)lit; m--; + + mode = InflateCodeMode.START; + break; + case InflateCodeMode.WASH: // o: got eob, possibly more output + if (k > 7) + { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; r = s.inflate_flush(z, r); + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) + { + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + mode = InflateCodeMode.END; + goto case InflateCodeMode.END; + case InflateCodeMode.END: + r = ZLibStatus.Z_STREAM_END; + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + case InflateCodeMode.BADCODE: // x: got error + + r = ZLibStatus.Z_DATA_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + + default: + r = ZLibStatus.Z_STREAM_ERROR; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + return s.inflate_flush(z, r); + } + } + } + + internal void codesfree(ZStream z) + { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal ZLibStatus inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InflateBlocks s, ZStream z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; + q = s.write; m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do + { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & inflate_mask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < (15)) + { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) + { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); k -= (e); + + // do the copy + m -= c; + if (q >= d) + { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + System.Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--e != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do { s.window[q++] = s.window[r++]; } + while (--c != 0); + } + else + { + System.Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.msg = "invalid distance code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_STREAM_END; + } + else + { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= c << 3; + + s.bitb = b; s.bitk = k; + z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; + s.write = q; + + return ZLibStatus.Z_OK; + } + } +} \ No newline at end of file diff --git a/Renci.SshNet/ConnectionInfo.cs b/Renci.SshNet/ConnectionInfo.cs index a8dfb49..d30196c 100644 --- a/Renci.SshNet/ConnectionInfo.cs +++ b/Renci.SshNet/ConnectionInfo.cs @@ -1,48 +1,27 @@ using System; -using System.Linq; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Linq; +using System.Text; using Renci.SshNet.Security; -using Renci.SshNet.Compression; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Common; -using System.Threading; -using System.Net; using Renci.SshNet.Messages.Connection; -using Renci.SshNet.Security.Cryptography.Ciphers; using System.Security.Cryptography; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; +using Renci.SshNet.Security.Cryptography.Ciphers; +using System.Collections.ObjectModel; +using System.Net; +using Renci.SshNet.Compression; + namespace Renci.SshNet { /// - /// Represents remote connection information base class. + /// Represents remote connection information class. /// - public abstract class ConnectionInfo + public class ConnectionInfo { - /// - /// Gets connection name - /// - public abstract string Name { get; } - - /// - /// Gets a value indicating whether connection is authenticated. - /// - /// - /// true if connection is authenticated; otherwise, false. - /// - public bool IsAuthenticated { get; private set; } - - /// - /// Gets the authentication error message. - /// - public string ErrorMessage { get; private set; } - - /// - /// Gets reference to the session object. - /// - protected Session Session { get; private set; } + internal static int DEFAULT_PORT = 22; /// /// Gets supported key exchange algorithms for this connection. @@ -57,17 +36,17 @@ public abstract class ConnectionInfo /// /// Gets supported hash algorithms for this connection. /// - public IDictionary> HmacAlgorithms { get; private set; } + public IDictionary HmacAlgorithms { get; private set; } /// /// Gets supported host key algorithms for this connection. /// - public IDictionary> HostKeyAlgorithms { get; private set; } + public IDictionary> HostKeyAlgorithms { get; private set; } /// /// Gets supported authentication methods for this connection. /// - public IDictionary AuthenticationMethods { get; private set; } + public IEnumerable AuthenticationMethods { get; private set; } /// /// Gets supported compression algorithms for this connection. @@ -79,6 +58,14 @@ public abstract class ConnectionInfo /// public IDictionary ChannelRequests { get; private set; } + /// + /// Gets a value indicating whether connection is authenticated. + /// + /// + /// true if connection is authenticated; otherwise, false. + /// + public bool IsAuthenticated { get; private set; } + /// /// Gets connection host. /// @@ -94,14 +81,53 @@ public abstract class ConnectionInfo /// public string Username { get; private set; } + /// + /// Gets proxy type. + /// + /// + /// The type of the proxy. + /// + public ProxyTypes ProxyType { get; private set; } + + /// + /// Gets proxy connection host. + /// + public string ProxyHost { get; private set; } + + /// + /// Gets proxy connection port. + /// + public int ProxyPort { get; private set; } + + /// + /// Gets proxy connection username. + /// + public string ProxyUsername { get; private set; } + + /// + /// Gets proxy connection password. + /// + public string ProxyPassword { get; private set; } + /// /// Gets or sets connection timeout. /// /// /// Connection timeout. /// + /// + /// + /// public TimeSpan Timeout { get; set; } + /// + /// Gets or sets the default encoding. + /// + /// + /// The default encoding. + /// + public Encoding Encoding { get; set; } + /// /// Gets or sets number of retry attempts when session channel creation failed. /// @@ -121,17 +147,131 @@ public abstract class ConnectionInfo /// /// Occurs when authentication banner is sent by the server. /// + /// + /// + /// public event EventHandler AuthenticationBanner; /// - /// Prevents a default instance of the class from being created. + /// Gets the current key exchange algorithm. /// - private ConnectionInfo() + public string CurrentKeyExchangeAlgorithm { get; internal set; } + + /// + /// Gets the current server encryption. + /// + public string CurrentServerEncryption { get; internal set; } + + /// + /// Gets the current client encryption. + /// + public string CurrentClientEncryption { get; internal set; } + + /// + /// Gets the current server hash algorithm. + /// + public string CurrentServerHmacAlgorithm { get; internal set; } + + /// + /// Gets the current client hash algorithm. + /// + public string CurrentClientHmacAlgorithm { get; internal set; } + + /// + /// Gets the current host key algorithm. + /// + public string CurrentHostKeyAlgorithm { get; internal set; } + + /// + /// Gets the current server compression algorithm. + /// + public string CurrentServerCompressionAlgorithm { get; internal set; } + + /// + /// Gets the server version. + /// + public string ServerVersion { get; internal set; } + + /// + /// Get the client version. + /// + public string ClientVersion { get; internal set; } + + /// + /// Gets the current client compression algorithm. + /// + public string CurrentClientCompressionAlgorithm { get; internal set; } + + /// + /// Initializes a new instance of the class. + /// + /// The host. + /// The username. + /// The authentication methods. + public ConnectionInfo(string host, string username, params AuthenticationMethod[] authenticationMethods) + : this(host, ConnectionInfo.DEFAULT_PORT, username, ProxyTypes.None, null, 0, null, null, authenticationMethods) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The host. + /// The port. + /// The username. + /// The authentication methods. + public ConnectionInfo(string host, int port, string username, params AuthenticationMethod[] authenticationMethods) + : this(host, port, username, ProxyTypes.None, null, 0, null, null, authenticationMethods) { + } + + // TODO: DOCS Add exception documentation for this class. + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + /// The authentication methods. + /// host + /// proxyPort + /// is invalid, or is null or contains whitespace characters. + /// is not within and . + /// is invalid, or is null or contains whitespace characters. + public ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params AuthenticationMethod[] authenticationMethods) + { + if (!host.IsValidHost()) + throw new ArgumentException("host"); + + if (proxyType != ProxyTypes.None) + { + if (string.IsNullOrEmpty(proxyHost) && !proxyHost.IsValidHost()) + throw new ArgumentException("proxyHost"); + + if (!proxyPort.IsValidPort()) + throw new ArgumentOutOfRangeException("proxyPort"); + } + + if (!port.IsValidPort()) + throw new ArgumentOutOfRangeException("port"); + + if (username.IsNullOrWhiteSpace()) + throw new ArgumentException("username"); + + if (authenticationMethods == null || authenticationMethods.Length < 1) + throw new ArgumentException("authenticationMethods"); + // Set default connection values this.Timeout = TimeSpan.FromSeconds(30); this.RetryAttempts = 10; this.MaxSessions = 10; + this.Encoding = Encoding.UTF8; this.KeyExchangeAlgorithms = new Dictionary() { @@ -139,49 +279,61 @@ private ConnectionInfo() {"diffie-hellman-group-exchange-sha1", typeof(KeyExchangeDiffieHellmanGroupExchangeSha1)}, {"diffie-hellman-group14-sha1", typeof(KeyExchangeDiffieHellmanGroup14Sha1)}, {"diffie-hellman-group1-sha1", typeof(KeyExchangeDiffieHellmanGroup1Sha1)}, + //{"ecdh-sha2-nistp256", typeof(KeyExchangeEllipticCurveDiffieHellman)}, + //{"ecdh-sha2-nistp256", typeof(...)}, + //{"ecdh-sha2-nistp384", typeof(...)}, + //{"ecdh-sha2-nistp521", typeof(...)}, + //"gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD + //"gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD + }; this.Encryptions = new Dictionary() { + {"aes256-ctr", new CipherInfo(256, (key, iv)=>{ return new AesCipher(key, new CtrCipherMode(iv), null); }) }, {"3des-cbc", new CipherInfo(192, (key, iv)=>{ return new TripleDesCipher(key, new CbcCipherMode(iv), null); }) }, {"aes128-cbc", new CipherInfo(128, (key, iv)=>{ return new AesCipher(key, new CbcCipherMode(iv), null); }) }, {"aes192-cbc", new CipherInfo(192, (key, iv)=>{ return new AesCipher(key, new CbcCipherMode(iv), null); }) }, {"aes256-cbc", new CipherInfo(256, (key, iv)=>{ return new AesCipher(key, new CbcCipherMode(iv), null); }) }, {"blowfish-cbc", new CipherInfo(128, (key, iv)=>{ return new BlowfishCipher(key, new CbcCipherMode(iv), null); }) }, - ////{"twofish-cbc", typeof(...)}, - ////{"twofish192-cbc", typeof(...)}, - ////{"twofish128-cbc", typeof(...)}, - ////{"twofish256-cbc", typeof(...)}, + {"twofish-cbc", new CipherInfo(256, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, + {"twofish192-cbc", new CipherInfo(192, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, + {"twofish128-cbc", new CipherInfo(128, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, + {"twofish256-cbc", new CipherInfo(256, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, ////{"serpent256-cbc", typeof(CipherSerpent256CBC)}, ////{"serpent192-cbc", typeof(...)}, ////{"serpent128-cbc", typeof(...)}, - ////{"arcfour128", typeof(...)}, - ////{"arcfour256", typeof(...)}, - ////{"arcfour", typeof(...)}, + {"arcfour", new CipherInfo(128, (key, iv)=>{ return new Arc4Cipher(key, false); }) }, + {"arcfour128", new CipherInfo(128, (key, iv)=>{ return new Arc4Cipher(key, true); }) }, + {"arcfour256", new CipherInfo(256, (key, iv)=>{ return new Arc4Cipher(key, true); }) }, ////{"idea-cbc", typeof(...)}, {"cast128-cbc", new CipherInfo(128, (key, iv)=>{ return new CastCipher(key, new CbcCipherMode(iv), null); }) }, ////{"rijndael-cbc@lysator.liu.se", typeof(...)}, {"aes128-ctr", new CipherInfo(128, (key, iv)=>{ return new AesCipher(key, new CtrCipherMode(iv), null); }) }, {"aes192-ctr", new CipherInfo(192, (key, iv)=>{ return new AesCipher(key, new CtrCipherMode(iv), null); }) }, - {"aes256-ctr", new CipherInfo(256, (key, iv)=>{ return new AesCipher(key, new CtrCipherMode(iv), null); }) }, }; - this.HmacAlgorithms = new Dictionary>() + this.HmacAlgorithms = new Dictionary() { - {"hmac-md5", (key) => { return new HMac(key.Take(16).ToArray());}}, - {"hmac-sha1", (key) => { return new HMac(key.Take(20).ToArray());}}, + {"hmac-md5", new HashInfo(16 * 8, (key)=>{ return new HMac(key); }) }, + {"hmac-sha1", new HashInfo(20 * 8, (key)=>{ return new HMac(key); }) }, + {"hmac-sha2-256", new HashInfo(32 * 8, (key)=>{ return new HMac(key); }) }, + {"hmac-sha2-256-96", new HashInfo(32 * 8, (key)=>{ return new HMac(key, 96); }) }, + //{"hmac-sha2-512", new HashInfo(64 * 8, (key)=>{ return new HMac(key); }) }, + //{"hmac-sha2-512-96", new HashInfo(64 * 8, (key)=>{ return new HMac(key, 96); }) }, //{"umac-64@openssh.com", typeof(HMacSha1)}, - //{"hmac-ripemd160", typeof(HMacSha1)}, - //{"hmac-ripemd160@openssh.com", typeof(HMacSha1)}, - //{"hmac-md5-96", typeof(...)}, - //{"hmac-sha1-96", typeof(...)}, + {"hmac-ripemd160", new HashInfo(160, (key)=>{ return new HMac(key); }) }, + {"hmac-ripemd160@openssh.com", new HashInfo(160, (key)=>{ return new HMac(key); }) }, + {"hmac-md5-96", new HashInfo(16 * 8, (key)=>{ return new HMac(key, 96); }) }, + {"hmac-sha1-96", new HashInfo(20 * 8, (key)=>{ return new HMac(key, 96); }) }, //{"none", typeof(...)}, }; - this.HostKeyAlgorithms = new Dictionary>() + this.HostKeyAlgorithms = new Dictionary>() { {"ssh-rsa", (data) => { return new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data); }}, {"ssh-dss", (data) => { return new KeyHostAlgorithm("ssh-dss", new DsaKey(), data); }}, + //{"ecdsa-sha2-nistp256 "} //{"x509v3-sign-rsa", () => { ... }, //{"x509v3-sign-dss", () => { ... }, //{"spki-sign-rsa", () => { ... }, @@ -190,24 +342,14 @@ private ConnectionInfo() //{"pgp-sign-dss", () => { ... }, }; - this.AuthenticationMethods = new Dictionary() - { - {"none", typeof(ConnectionInfo)}, - {"publickey", typeof(PrivateKeyConnectionInfo)}, - {"password", typeof(PasswordConnectionInfo)}, - {"keyboard-interactive", typeof(KeyboardInteractiveConnectionInfo)}, - //{"hostbased", typeof(...)}, - //{"gssapi-keyex", typeof(...)}, - //{"gssapi-with-mic", typeof(...)}, - }; - this.CompressionAlgorithms = new Dictionary() { + //{"zlib@openssh.com", typeof(ZlibOpenSsh)}, + //{"zlib", typeof(Zlib)}, {"none", null}, - {"zlib", typeof(Zlib)}, - {"zlib@openssh.com", typeof(ZlibOpenSsh)}, }; + this.ChannelRequests = new Dictionary() { {EnvironmentVariableRequestInfo.NAME, new EnvironmentVariableRequestInfo()}, @@ -224,32 +366,18 @@ private ConnectionInfo() {EndOfWriteRequestInfo.NAME, new EndOfWriteRequestInfo()}, {KeepAliveRequestInfo.NAME, new KeepAliveRequestInfo()}, }; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - /// is null or empty. - protected ConnectionInfo(string host, int port, string username) - : this() - { - if (!host.IsValidHost()) - throw new ArgumentException("Hostname not valid"); - - if (!port.IsValidPort()) - throw new ArgumentException("Port value in not in valid range"); - - if (username.IsNullOrWhiteSpace()) - throw new ArgumentException("Username not valid"); this.Host = host; this.Port = port; this.Username = username; + + this.ProxyType = proxyType; + this.ProxyHost = proxyHost; + this.ProxyPort = proxyPort; + this.ProxyUsername = proxyUsername; + this.ProxyPassword = proxyPassword; + + this.AuthenticationMethods = authenticationMethods; } /// @@ -258,100 +386,70 @@ protected ConnectionInfo(string host, int port, string username) /// The session to be authenticated. /// true if authenticated; otherwise false. /// is null. + /// No suitable authentication method found to complete authentication. public bool Authenticate(Session session) { + var authenticated = AuthenticationResult.Failure; + if (session == null) throw new ArgumentNullException("session"); - this.Session = session; - - this.Session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE"); - this.Session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS"); - this.Session.RegisterMessage("SSH_MSG_USERAUTH_BANNER"); + session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE"); + session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS"); + session.RegisterMessage("SSH_MSG_USERAUTH_BANNER"); - this.Session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; - this.Session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessMessageReceived; - this.Session.UserAuthenticationBannerReceived += Session_UserAuthenticationBannerMessageReceived; - this.Session.MessageReceived += Session_MessageReceived; + session.UserAuthenticationBannerReceived += Session_UserAuthenticationBannerReceived; - this.OnAuthenticate(); + // Try to authenticate against none + var noneAuthenticationMethod = new NoneAuthenticationMethod(this.Username); - this.Session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; - this.Session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessMessageReceived; - this.Session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerMessageReceived; - this.Session.MessageReceived -= Session_MessageReceived; + authenticated = noneAuthenticationMethod.Authenticate(session); - this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE"); - this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS"); - this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER"); - - return this.IsAuthenticated; - } + var allowedAuthentications = noneAuthenticationMethod.AllowedAuthentications; - /// - /// Called when connection needs to be authenticated. - /// - protected abstract void OnAuthenticate(); + var triedAuthentications = new List(); + while (authenticated != AuthenticationResult.Success) + { + // Find first authentication method + var method = this.AuthenticationMethods.Where((a) => allowedAuthentications.Contains(a.Name) && !triedAuthentications.Contains(a.Name)).FirstOrDefault(); + if (method == null) + throw new SshAuthenticationException("No suitable authentication method found to complete authentication."); + + triedAuthentications.Add(method.Name); + + authenticated = method.Authenticate(session); + + if (authenticated == AuthenticationResult.PartialSuccess || (method.AllowedAuthentications != null && method.AllowedAuthentications.Count() < allowedAuthentications.Count())) + { + // If further authentication is required then continue to try another method + allowedAuthentications = method.AllowedAuthentications; + continue; + } + + // If authentication Fail, and all the authentication have been tried. + if (authenticated == AuthenticationResult.Failure && (triedAuthentications.Count() == allowedAuthentications.Count())) + { + break; + } + } - /// - /// Sends SSH message to the server. - /// - /// The message. - protected void SendMessage(Message message) - { - this.Session.SendMessage(message); - } + session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerReceived; - /// - /// Waits the handle to signal. - /// - /// The event wait handle. - protected void WaitHandle(WaitHandle eventWaitHandle) - { - this.Session.WaitHandle(eventWaitHandle); - } + session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE"); + session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS"); + session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER"); - /// - /// Handles the UserAuthenticationFailureReceived event of the session. - /// - /// The source of the event. - /// The event data. - protected virtual void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) - { - this.ErrorMessage = e.Message.Message; - this.IsAuthenticated = false; - } + this.IsAuthenticated = authenticated == AuthenticationResult.Success; - /// - /// Handles the UserAuthenticationSuccessMessageReceived event of the session. - /// - /// The source of the event. - /// The event data. - protected virtual void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs e) - { - this.IsAuthenticated = true; + return authenticated == AuthenticationResult.Success; } - /// - /// Handles the UserAuthenticationBannerMessageReceived event of the session. - /// - /// The source of the event. - /// The event data. - protected virtual void Session_UserAuthenticationBannerMessageReceived(object sender, MessageEventArgs e) + private void Session_UserAuthenticationBannerReceived(object sender, MessageEventArgs e) { if (this.AuthenticationBanner != null) { this.AuthenticationBanner(this, new AuthenticationBannerEventArgs(this.Username, e.Message.Message, e.Message.Language)); } } - - /// - /// Handles the MessageReceived event of the session. - /// - /// The source of the event. - /// The event data. - protected virtual void Session_MessageReceived(object sender, MessageEventArgs e) - { - } } } diff --git a/Renci.SshNet/Documentation/Content/About.aml b/Renci.SshNet/Documentation/Content/About.aml deleted file mode 100644 index 848a316..0000000 --- a/Renci.SshNet/Documentation/Content/About.aml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - This preliminary documentation. I will add more information later so if you have any feedback as far as what missing or what need to be corrected please let me know. - At this moment SFTP documentation not yet available. - - - - - - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Asynchronous.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Asynchronous.aml deleted file mode 100644 index b48763e..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Asynchronous.aml +++ /dev/null @@ -1,41 +0,0 @@ - - - - Execut command asynchronously - - If you need to execute long running command you might want to execute it in asynchronously. - To execute command asynchronously you need to create command first and then call M:Renci.SshClient.SshCommand.BeginExecute(System.AsyncCallback, System.Object) method - - If an exception occures while command is being executed it will be thrown when M:Renci.SshClient.SshCommand.EndExecute(System.IAsyncResult) is called. - - See examples below: - - - - Display exit status when command is executed. - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var cmd = client.CreateCommand("sleep 30s;date"); // Perform long running task - var asynch = cmd.BeginExecute(null, null); - while (!asynch.IsCompleted) - { - Console.WriteLine("Waiting for command to complete..."); - Thread.Sleep(2000); - } - cmd.EndExecute(asynch); - client.Disconnect(); - } - - - - - - - M:Renci.SshClient.SshCommand.BeginExecute(System.AsyncCallback, System.Object) - M:Renci.SshClient.SshCommand.EndExecute(System.IAsyncResult) - T:Renci.SshClient.SshCommand - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Error.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Error.aml deleted file mode 100644 index 6a5e18d..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Error.aml +++ /dev/null @@ -1,36 +0,0 @@ - - - - Execution errors - - When command is executed and error occured you can access to errir description by accessing P:Renci.SshClient.SshCommand.Error property. - You can also check P:Renci.SshClient.SshCommand.ExitStatus which is in case of error should not be 0. - See examples below: - - - - Execute invalid command and display execution error. - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var cmd = client.CreateCommand(";"); - cmd.Execute(); - if (!string.IsNullOrEmpty(cmd.Error)) - { - Console.WriteLine(cmd.Error); - } - client.Disconnect(); - } - - - - - - - P:Renci.SshClient.SshCommand.ExitStatus - P:Renci.SshClient.SshCommand.Error - T:Renci.SshClient.SshCommand - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Multitasking.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Multitasking.aml deleted file mode 100644 index fa13204..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Multitasking.aml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - Library support multiple command execution at once. However there are some limitation to be aware of. - While you can run multiple commands on multiple threads at the same time usually only 10 commands are executed at the same time. - This is due to server default limitation which allows only 10 session channels top be open at the same time. - This limitation can be change on the server level, if so you can you T:Renci.SshClient.ConnectionInfo object to increase or decrease this value. - This rule is only true when you try to run multiple commands on the same connection. - Of course if you will be using multiple connection this rul does not apply. - - - - Running multiple command using one connection. - - System.Threading.Tasks.Parallel.For(0, 10000, - () => - { - var client = new SshClient("host", "username", "password"); - client.Connect(); - return client; - }, - (int counter, ParallelLoopState pls, SshClient client) => - { - var cmd = client.RunCommand(string.Format("echo {0}}", counter)); - Console.WriteLine(cmd.Result); - return client; - }, - (SshClient client) => - { - client.Disconnect(); - client.Dispose(); - } - - - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.StatusCode.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Command.StatusCode.aml deleted file mode 100644 index 60ba87b..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.StatusCode.aml +++ /dev/null @@ -1,31 +0,0 @@ - - - - Execution errors - - When command is executed you can retrive command execution exit status by accessing P:Renci.SshClient.SshCommand.ExitStatus property. - See examples below: - - - - Display exit status when command is executed. - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var cmd = client.RunCommand("exit 128"); - cmd.Execute(); - Console.WriteLine(cmd.ExitStatus); // Should display 128 - client.Disconnect(); - } - - - - - - - P:Renci.SshClient.SshCommand.ExitStatus - T:Renci.SshClient.SshCommand - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Timeout.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Timeout.aml deleted file mode 100644 index ff25ce6..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.Timeout.aml +++ /dev/null @@ -1,32 +0,0 @@ - - - - Command execution - - When you need to terminte the command after specific time has passed you can use P:Renci.SshClient.SshCommand.CommandTimeout property. - To specify time out property you must to create T:Renci.SshClient.SshCommand first. - See examples below: - - - - Execute command and specify 10 sec timeout - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var cmd = client.CreateCommand("sleep 10s"); - cmd.CommandTimeout = TimeSpan.FromSeconds(5); - cmd.Execute(); - client.Disconnect(); - } - - - - - - - P:Renci.SshClient.SshCommand.CommandTimeout - T:Renci.SshClient.SshCommand - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Command.aml deleted file mode 100644 index 0268a64..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Command.aml +++ /dev/null @@ -1,88 +0,0 @@ - - - - Command execution - - All commands executed using T:Renci.SshClient.SshCommand object. - See examples below: - - - - Execute single comamnd and display result - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var cmd = client.RunCommand("ls -l"); - Console.WriteLine(cmd.Result); - client.Disconnect(); - } - - - - Create command object then execute it - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var cmd = client.CreateCommand("date"); - var result cmd.Execute(); - Console.WriteLine(result); - client.Disconnect(); - } - - - - Create command object then use it to execute multiple times - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var cmd = client.CreateCommand("date"); - cmd.Execute(); - Console.WriteLine(cmd.Result); - cmd.Execute("ls -l"); - Console.WriteLine(cmd.Result); - client.Disconnect(); - } - - - - - - - - - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.ChangePassword.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.ChangePassword.aml deleted file mode 100644 index 135ce7b..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.ChangePassword.aml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - When user logs on and his password is expired you can ask user for a new passowrd and change it. - Note: the event handler will be executed on new thread. - - - - Change user's password. - - var connectionInfo = new PasswordConnectionInfo("host", "username", "password"); - connectionInfo.PasswordExpired += delegate(object sender, AuthenticationPasswordChangeEventArgs e) - { - e.NewPassword = "123456"; - }; - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - - - T:Renci.SshClient.PasswordConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect() - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Interactive.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Interactive.aml deleted file mode 100644 index c1209ea..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Interactive.aml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - If you use interactive method of authentication you need to attach to E:Renci.SshClient.KeyboardInteractiveConnectionInfo.AuthenticationPrompt event and provide an information requested when an event raised. It can be raised more then once if server needs to. - - - - - Change user's password. - - var connectionInfo = new KeyboardInteractiveConnectionInfo("host", "username"); - connectionInfo.AuthenticationPrompt += delegate(object sender, AuthenticationPromptEventArgs e) - { - Console.WriteLine(e.Instruction); - - foreach (var prompt in e.Prompts) - { - Console.WriteLine(prompt.Request); - prompt.Response = Console.ReadLine(); - } - }; - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - - - T:Renci.SshClient.KeyboardInteractiveConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect() - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Password.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Password.aml deleted file mode 100644 index d38cc83..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Password.aml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - At this point some examples how to connect using password. - - - - - Use T:Renci.SshClient.PasswordConnectionInfo object to connect. - - - var connectionInfo = new PasswordConnectionInfo("host", "username", "password"); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - - Use shortcut version to connet. - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - client.Disconnect(); - } - - - - - - - T:Renci.SshClient.PasswordConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect() - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.PrivateKey.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.PrivateKey.aml deleted file mode 100644 index 6aa6a54..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.PrivateKey.aml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - At this point some examples how to connect using password. - - - - Connecting using username and private key - - using (var client = new SshClient("host", "username", new PrivateKeyFile(File.OpenRead(@"private.key")))) - { - client.Connect(); - client.Disconnect(); - } - - - - Connecting using username, private key and passphrase - - using (var client = new SshClient("host", "username", new PrivateKeyFile(File.OpenRead(@"private.key"), "passphrase"))) - { - client.Connect(); - client.Disconnect(); - } - - - - - - - T:Renci.SshClient.PrivateKeyConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect() - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Prompt.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Prompt.aml deleted file mode 100644 index c9bab9a..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Prompt.aml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - If you need to display banner to the user return by the server when you log on you need to attach to E:Renci.SshClient.ConnectionInfo.AuthenticationBanner event. - - - - - Change user's password. - - var connectionInfo = new PasswordConnectionInfo("host", "username", "password"); - connectionInfo.AuthenticationBanner += delegate(object sender, AuthenticationBannerEventArgs e) - { - Console.WriteLine(e.BannerMessage); - }; - - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - - - T:Renci.SshClient.PasswordConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect() - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Timeout.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Timeout.aml deleted file mode 100644 index d784fdf..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.Timeout.aml +++ /dev/null @@ -1,32 +0,0 @@ - - - - Specify connection time out. - - - If connection is to slow you can set P:Renci.SshClient.ConnectionInfo.Timeout property to specify time to allow connection to complete. - - - - - Specify 30 seconds timeout. - - var connectionInfo = new PasswordConnectionInfo("host", "username", "password"); - connectionInfo.Timeout = TimeSpan.FromSeconds(30); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - - - T:Renci.SshClient.PasswordConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect() - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.aml deleted file mode 100644 index 0c4dfa7..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.Connection.aml +++ /dev/null @@ -1,125 +0,0 @@ - - - -
- Establish connection - - To establish connection you need to provide valid credentials. Both T:Renci.SshClient.SshClient and T:Renci.SshClient.SftpClient accept same paramters. See examples below: - - - - Connecting using username and password to server's default port - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - client.Disconnect(); - } - - - - Connecting using username and password to server's port 1234 - - using (var client = new SftpClient("host", 1234, "username", "password")) - { - client.Connect(); - client.Disconnect(); - } - - - - - Connecting using username and private key to server's default port - - using (var client = new SshClient("host", "username", new PrivateKeyFile(File.OpenRead(@"private.key")))) - { - client.Connect(); - client.Disconnect(); - } - - - - Connecting using username and private key to server's port 1234 - - using (var client = new SftpClient("host", 1234, "username", new PrivateKeyFile(File.OpenRead(@"private.key")))) - { - client.Connect(); - client.Disconnect(); - } - - - - - - -
- -
- - Using ConnectionInfo object to connect. - - - - In more complex scenarious when additional parameters are needed during connection phase you can use T:Renci.SshClient.ConnectionInfo inherited object. - T:Renci.SshClient.ConnectionInfo is an abstact class and you need to use one of the inhertied classes. - Which class to use depends on how you want to connect to the server. - To connect using private key you need to use T:Renci.SshClient.PrivateKeyConnectionInfo object. - To connect using username and password combination use T:Renci.SshClient.PasswordConnectionInfo. - If you have an interactive scenario you need to use T:Renci.SshClient.KeyboardInteractiveConnectionInfo. - - See some usage examples below: - - - - - Use T:Renci.SshClient.PasswordConnectionInfo objet to provide username and password. - - - var connectionInfo = new PasswordConnectionInfo("host", "username", "password"); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - Use T:Renci.SshClient.PrivateKeyConnectionInfo objet to provide username and private key file - - - var connectionInfo = new PrivateKeyConnectionInfo("host", "username", new PrivateKeyFile(File.OpenRead(@"private.key"))); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - Use T:Renci.SshClient.PrivateKeyConnectionInfo objet to provide username and private key file with passphrase. - - - var connectionInfo = new PrivateKeyConnectionInfo("host", "username", new PrivateKeyFile(File.OpenRead(@"private.key"), "passphrase")); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - -
- - - T:Renci.SshClient.ConnectionInfo - T:Renci.SshClient.PrivateKeyConnectionInfo - T:Renci.SshClient.PasswordConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect - -
-
\ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Local.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Local.aml deleted file mode 100644 index 3ca6c2a..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Local.aml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - Start and stop local port forwarding. - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var port = client.AddForwardedPort<ForwardedPortLocal>(8084, "host to be forwarded", 80); - port.Exception += delegate(object sender, ExceptionEventArgs e) - { - Console.WriteLine(e.Exception.ToString()); - }; - port.Start(); - - Thread.Sleep(1000 * 60 * 20); // Wait 20 minutes for port to be forwarded - - port.Stop(); - } - - - - - T:Renci.SshClient.SshClient - T:Renci.SshClient.ForwardedPortLocal - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Remote.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Remote.aml deleted file mode 100644 index 2ecc7f7..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.Remote.aml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - Start and stop local port forwarding. - - using (var client = new SshClient("host", "username", "password")) - { - client.Connect(); - var port = client.AddForwardedPort<ForwardedPortRemote>(8082, "host to be forwarded", 80); - port.Exception += delegate(object sender, ExceptionEventArgs e) - { - Console.WriteLine(e.Exception.ToString()); - }; - port.Start(); - - Thread.Sleep(1000 * 60 * 20); // Wait 20 minutes for port to be forwarded - - port.Stop(); - } - - - - - T:Renci.SshClient.SshClient - T:Renci.SshClient.ForwardedPortLocal - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.aml deleted file mode 100644 index 15d9365..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.PortForward.aml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Port forwarding supported for remote and local ports. - Multiple port can be forwarded simultaneously. - To start port forwarding you need to create an T:Renci.SshClient.ForwardedPortLocal or T:Renci.SshClient.ForwardedPortRemote object. - Once port object is created you can use M:Renci.SshClient.ForwardedPort.Start start port forwarding or M:Renci.SshClient.ForwardedPort.Stop to stop it. - - - - An exception can be thrown while port is forwarded. When this happens an E:Renci.SshClient.ForwardedPort.Exception event is raised. - - - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.SFtp.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.SFtp.aml deleted file mode 100644 index 0305ee4..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.SFtp.aml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - Required introduction - - - -
- Syntax section title - - Syntax content - -
-
- - Parameter reference - -
- - - - - - - - -
- Optional section title - - - Add one or more sections with content - -
- - - -
-
\ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Guide/Guide.aml b/Renci.SshNet/Documentation/Content/Guide/Guide.aml deleted file mode 100644 index f8d285a..0000000 --- a/Renci.SshNet/Documentation/Content/Guide/Guide.aml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - Required introduction - - - - Keyword content - - - - - Namespaces content - - - - - - Background content - - - - "Implementing [Technology Name] Classes" title - - Implementation content - - - - "[Technology Name] Classes at a Glance" title - - At a Glance content - - - - - - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/HowTo.Connect.aml b/Renci.SshNet/Documentation/Content/HowTo.Connect.aml deleted file mode 100644 index e209bb6..0000000 --- a/Renci.SshNet/Documentation/Content/HowTo.Connect.aml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - The library supports differnet method to connect to SSH server. - It also has support to add a custom authentication method if needed. Method supported out of the box are :publickey, password and keyboard-interactive. There is also support for different events that can occure during authentication. For example: server can send an information that need to be presented to the user upon login, user's password is expired and new password could be provided to change user's password and others scenrios. - - By using T:Renci.SshClient.ConnectionInfo you can specify connection timeout, which in this case will wait 30 seconds while tying to connect to the server after which it will throw an exception if it did not connect. - - - -
- Connect using password. - - Following examples show how to connect using username and password combination. - - - Try to connect to the host within 30 seconds. - - var connectionInfo = new PasswordConnectionInfo("host", "username", "password"); - connectionInfo.Timeout = TimeSpan.FromSeconds(30); - using (var client = new SshClient(connectionInfo)) - { - client.Connect(); - client.Disconnect(); - } - - - - - -
- - - - T:Renci.SshClient.ConnectionInfo - T:Renci.SshClient.PrivateKeyConnectionInfo - T:Renci.SshClient.PasswordConnectionInfo - T:Renci.SshClient.SshClient - M:Renci.SshClient.SshBaseClient.Connect() - -
-
\ No newline at end of file diff --git a/Renci.SshNet/Documentation/Content/Reference.aml b/Renci.SshNet/Documentation/Content/Reference.aml deleted file mode 100644 index 2a9679a..0000000 --- a/Renci.SshNet/Documentation/Content/Reference.aml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Assembly name - - - - API member ID - - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/Renci.SshClient.content b/Renci.SshNet/Documentation/Renci.SshClient.content deleted file mode 100644 index 112aed9..0000000 --- a/Renci.SshNet/Documentation/Renci.SshClient.content +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Renci.SshNet/Documentation/SshClient.shfbproj b/Renci.SshNet/Documentation/SshClient.shfbproj deleted file mode 100644 index 566e84c..0000000 --- a/Renci.SshNet/Documentation/SshClient.shfbproj +++ /dev/null @@ -1,140 +0,0 @@ - - - - Debug - AnyCPU - 2.0 - {0b73772a-c19f-4218-bd92-efa8d5f2ddd3} - 1.9.0.0 - - Documentation - Documentation - Documentation - - .\Help\ - SshClient - en-US - - - - 4.0.30319 - A Ssh.Net Client Documentation - - - http://sshnet.codeplex.com - Summary, Parameter, Returns, AutoDocumentCtors, Namespace, TypeParameter - - - - -{@CachedFrameworkCommentList} -{@CommentFileList} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Renci.SshNet/ExpectAction.cs b/Renci.SshNet/ExpectAction.cs new file mode 100644 index 0000000..61bd8c6 --- /dev/null +++ b/Renci.SshNet/ExpectAction.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Renci.SshNet +{ + /// + /// Specifies behavior for expected expression + /// + public class ExpectAction + { + /// + /// Gets the expected regular expression. + /// + public Regex Expect { get; private set; } + + /// + /// Gets the action to perform when expected expression is found. + /// + public Action Action { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The expect regular expression. + /// The action to perform. + /// or is null. + public ExpectAction(Regex expect, Action action) + { + if (expect == null) + throw new ArgumentNullException("expect"); + + if (action == null) + throw new ArgumentNullException("action"); + + this.Expect = expect; + this.Action = action; + } + + /// + /// Initializes a new instance of the class. + /// + /// The expect expression. + /// The action to perform. + /// or is null. + public ExpectAction(string expect, Action action) + { + if (expect == null) + throw new ArgumentNullException("expect"); + + if (action == null) + throw new ArgumentNullException("action"); + + this.Expect = new Regex(Regex.Escape(expect)); + this.Action = action; + } + } +} diff --git a/Renci.SshNet/ExpectAsyncResult.cs b/Renci.SshNet/ExpectAsyncResult.cs new file mode 100644 index 0000000..b89e6f7 --- /dev/null +++ b/Renci.SshNet/ExpectAsyncResult.cs @@ -0,0 +1,23 @@ +using Renci.SshNet.Common; +using System; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Provides additional information for asynchronous command execution + /// + public class ExpectAsyncResult : AsyncResult + { + /// + /// Initializes a new instance of the class. + /// + /// The shell stream. + /// The async callback. + /// The state. + internal ExpectAsyncResult(AsyncCallback asyncCallback, Object state) + : base(asyncCallback, state) + { + } + } +} diff --git a/Renci.SshNet/ForwardedPort.cs b/Renci.SshNet/ForwardedPort.cs index bb8f6c2..a126441 100644 --- a/Renci.SshNet/ForwardedPort.cs +++ b/Renci.SshNet/ForwardedPort.cs @@ -16,26 +16,6 @@ public abstract class ForwardedPort /// internal Session Session { get; set; } - /// - /// Gets the bound host. - /// - public string BoundHost { get; internal set; } - - /// - /// Gets the bound port. - /// - public uint BoundPort { get; internal set; } - - /// - /// Gets the forwarded host. - /// - public string Host { get; internal set; } - - /// - /// Gets the forwarded port. - /// - public uint Port { get; internal set; } - /// /// Gets or sets a value indicating whether port forwarding started. /// @@ -54,14 +34,6 @@ public abstract class ForwardedPort ///
public event EventHandler RequestReceived; - /// - /// Initializes a new instance of the class. - /// - internal ForwardedPort() - { - - } - /// /// Starts port forwarding. /// @@ -85,7 +57,10 @@ public virtual void Start() /// public virtual void Stop() { - this.Session.ErrorOccured -= Session_ErrorOccured; + if (this.Session != null) + { + this.Session.ErrorOccured -= Session_ErrorOccured; + } } /// diff --git a/Renci.SshNet/ForwardedPortDynamic.NET.cs b/Renci.SshNet/ForwardedPortDynamic.NET.cs new file mode 100644 index 0000000..d4aa516 --- /dev/null +++ b/Renci.SshNet/ForwardedPortDynamic.NET.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; + +namespace Renci.SshNet +{ + public partial class ForwardedPortDynamic + { + private TcpListener _listener; + private object _listenerLocker = new object(); + + partial void InternalStart() + { + // If port already started don't start it again + if (this.IsStarted) + return; + + var ip = IPAddress.Any; + if (!string.IsNullOrEmpty(this.BoundHost)) + { + ip = this.BoundHost.GetIPAddress(); + } + + var ep = new IPEndPoint(ip, (int)this.BoundPort); + + this._listener = new TcpListener(ep); + this._listener.Start(); + + this._listenerTaskCompleted = new ManualResetEvent(false); + this.ExecuteThread(() => + { + try + { + while (true) + { + lock (this._listenerLocker) + { + if (this._listener == null) + break; + } + + var socket = this._listener.AcceptSocket(); + + this.ExecuteThread(() => + { + try + { + using (var channel = this.Session.CreateChannel()) + { + var version = new byte[1]; + + socket.Receive(version); + + if (version[0] == 4) + { + this.HandleSocks4(socket, channel); + } + else if (version[0] == 5) + { + this.HandleSocks5(socket, channel); + } + else + { + throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version)); + } + + channel.Bind(); + + channel.Close(); + } + } + catch (Exception exp) + { + this.RaiseExceptionEvent(exp); + } + }); + } + } + catch (SocketException exp) + { + if (!(exp.SocketErrorCode == SocketError.Interrupted)) + { + this.RaiseExceptionEvent(exp); + } + } + catch (Exception exp) + { + this.RaiseExceptionEvent(exp); + } + finally + { + this._listenerTaskCompleted.Set(); + } + }); + + this.IsStarted = true; + } + + partial void InternalStop() + { + // If port not started you cant stop it + if (!this.IsStarted) + return; + + lock (this._listenerLocker) + { + this._listener.Stop(); + this._listener = null; + } + this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout); + this._listenerTaskCompleted.Dispose(); + this._listenerTaskCompleted = null; + this.IsStarted = false; + } + + private void HandleSocks4(Socket socket, ChannelDirectTcpip channel) + { + using (var stream = new NetworkStream(socket)) + { + var commandCode = stream.ReadByte(); + // TODO: See what need to be done depends on the code + + var portBuffer = new byte[2]; + stream.Read(portBuffer, 0, portBuffer.Length); + var port = (uint)(portBuffer[0] * 256 + portBuffer[1]); + + var ipBuffer = new byte[4]; + stream.Read(ipBuffer, 0, ipBuffer.Length); + var ipAddress = new IPAddress(ipBuffer); + + var username = ReadString(stream); + + var host = ipAddress.ToString(); + + this.RaiseRequestReceived(host, port); + + channel.Open(host, port, socket); + + stream.WriteByte(0x00); + + if (channel.IsOpen) + { + stream.WriteByte(0x5a); + } + else + { + stream.WriteByte(0x5b); + } + + stream.Write(portBuffer, 0, portBuffer.Length); + stream.Write(ipBuffer, 0, ipBuffer.Length); + } + } + + private void HandleSocks5(Socket socket, ChannelDirectTcpip channel) + { + using (var stream = new NetworkStream(socket)) + { + var authenticationMethodsCount = stream.ReadByte(); + + var authenticationMethods = new byte[authenticationMethodsCount]; + stream.Read(authenticationMethods, 0, authenticationMethods.Length); + + stream.WriteByte(0x05); + + if (authenticationMethods.Min() == 0) + { + stream.WriteByte(0x00); + } + else + { + stream.WriteByte(0xFF); + } + + var version = stream.ReadByte(); + + if (version != 5) + throw new ProxyException("SOCKS5: Version 5 is expected."); + + var commandCode = stream.ReadByte(); + + if (stream.ReadByte() != 0) + { + throw new ProxyException("SOCKS5: 0 is expected."); + } + + var addressType = stream.ReadByte(); + + IPAddress ipAddress = null; + byte[] addressBuffer = null; + switch (addressType) + { + case 0x01: + { + addressBuffer = new byte[4]; + stream.Read(addressBuffer, 0, 4); + + ipAddress = new IPAddress(addressBuffer); + } + break; + case 0x03: + { + var length = stream.ReadByte(); + addressBuffer = new byte[length]; + stream.Read(addressBuffer, 0, addressBuffer.Length); + + ipAddress = IPAddress.Parse(new Renci.SshNet.Common.ASCIIEncoding().GetString(addressBuffer)); + } + break; + case 0x04: + { + addressBuffer = new byte[16]; + stream.Read(addressBuffer, 0, 16); + + ipAddress = new IPAddress(addressBuffer); + } + break; + default: + throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType)); + } + + var portBuffer = new byte[2]; + stream.Read(portBuffer, 0, portBuffer.Length); + var port = (uint)(portBuffer[0] * 256 + portBuffer[1]); + var host = ipAddress.ToString(); + + this.RaiseRequestReceived(host, port); + + channel.Open(host, port, socket); + + stream.WriteByte(0x05); + + if (channel.IsOpen) + { + stream.WriteByte(0x00); + } + else + { + stream.WriteByte(0x01); + } + + stream.WriteByte(0x00); + + var buffer = ipAddress.GetAddressBytes(); + + if (ipAddress.AddressFamily == AddressFamily.InterNetwork) + { + stream.WriteByte(0x01); + } + else if (ipAddress.AddressFamily == AddressFamily.InterNetwork) + { + stream.WriteByte(0x04); + } + else + { + throw new NotSupportedException("Not supported address family."); + } + + stream.Write(buffer, 0, buffer.Length); + stream.Write(portBuffer, 0, portBuffer.Length); + } + } + + private static string ReadString(NetworkStream stream) + { + StringBuilder text = new StringBuilder(); + var aa = (char)stream.ReadByte(); + while (aa != 0) + { + text.Append(aa); + aa = (char)stream.ReadByte(); + } + return text.ToString(); + } + } +} diff --git a/Renci.SshNet/ForwardedPortDynamic.NET40.cs b/Renci.SshNet/ForwardedPortDynamic.NET40.cs new file mode 100644 index 0000000..952b354 --- /dev/null +++ b/Renci.SshNet/ForwardedPortDynamic.NET40.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Renci.SshNet +{ + public partial class ForwardedPortDynamic + { + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem((o) => { action(); }); + } + } +} diff --git a/Renci.SshNet/ForwardedPortDynamic.cs b/Renci.SshNet/ForwardedPortDynamic.cs new file mode 100644 index 0000000..dc3ed39 --- /dev/null +++ b/Renci.SshNet/ForwardedPortDynamic.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Provides functionality for dynamic port forwarding + /// + public partial class ForwardedPortDynamic : ForwardedPort, IDisposable + { + private EventWaitHandle _listenerTaskCompleted; + + /// + /// Gets the bound host. + /// + public string BoundHost { get; protected set; } + + /// + /// Gets the bound port. + /// + public uint BoundPort { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// The port. + public ForwardedPortDynamic(uint port) + : this(string.Empty, port) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The host. + /// The port. + public ForwardedPortDynamic(string host, uint port) + { + this.BoundHost = host; + this.BoundPort = port; + } + + /// + /// Starts local port forwarding. + /// + public override void Start() + { + this.InternalStart(); + } + + /// + /// Stops local port forwarding. + /// + public override void Stop() + { + base.Stop(); + + this.InternalStop(); + } + + partial void InternalStart(); + + partial void InternalStop(); + + partial void ExecuteThread(Action action); + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + this.InternalStop(); + + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._listenerTaskCompleted != null) + { + this._listenerTaskCompleted.Dispose(); + this._listenerTaskCompleted = null; + } + } + + // Note disposing has been done. + _isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~ForwardedPortDynamic() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/ForwardedPortLocal.NET.cs b/Renci.SshNet/ForwardedPortLocal.NET.cs new file mode 100644 index 0000000..d24ff46 --- /dev/null +++ b/Renci.SshNet/ForwardedPortLocal.NET.cs @@ -0,0 +1,119 @@ +using System; +using System.Linq; +using System.Net.Sockets; +using System.Net; +using System.Threading; +using Renci.SshNet.Channels; + +namespace Renci.SshNet +{ + /// + /// Provides functionality for local port forwarding + /// + public partial class ForwardedPortLocal + { + private TcpListener _listener; + private object _listenerLocker = new object(); + + partial void InternalStart() + { + // If port already started don't start it again + if (this.IsStarted) + return; + + IPAddress addr = this.BoundHost.GetIPAddress(); + var ep = new IPEndPoint(addr, (int)this.BoundPort); + + this._listener = new TcpListener(ep); + this._listener.Start(); + + this.Session.ErrorOccured += Session_ErrorOccured; + this.Session.Disconnected += Session_Disconnected; + + this._listenerTaskCompleted = new ManualResetEvent(false); + this.ExecuteThread(() => + { + try + { + while (true) + { + lock (this._listenerLocker) + { + if (this._listener == null) + break; + } + + var socket = this._listener.AcceptSocket(); + + this.ExecuteThread(() => + { + try + { + IPEndPoint originatorEndPoint = socket.RemoteEndPoint as IPEndPoint; + + this.RaiseRequestReceived(originatorEndPoint.Address.ToString(), (uint)originatorEndPoint.Port); + + var channel = this.Session.CreateChannel(); + + channel.Open(this.Host, this.Port, socket); + + channel.Bind(); + + channel.Close(); + } + catch (Exception exp) + { + this.RaiseExceptionEvent(exp); + } + }); + } + } + catch (SocketException exp) + { + if (!(exp.SocketErrorCode == SocketError.Interrupted)) + { + this.RaiseExceptionEvent(exp); + } + } + catch (Exception exp) + { + this.RaiseExceptionEvent(exp); + } + finally + { + this._listenerTaskCompleted.Set(); + } + }); + + this.IsStarted = true; + } + + partial void InternalStop() + { + // If port not started you cant stop it + if (!this.IsStarted) + return; + + lock (this._listenerLocker) + { + this._listener.Stop(); + this._listener = null; + } + this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout); + this._listenerTaskCompleted.Dispose(); + this._listenerTaskCompleted = null; + + this.IsStarted = false; + } + + private void Session_ErrorOccured(object sender, Common.ExceptionEventArgs e) + { + this._listener.Stop(); + } + + private void Session_Disconnected(object sender, EventArgs e) + { + this._listener.Stop(); + } + } +} diff --git a/Renci.SshNet/ForwardedPortLocal.NET40.cs b/Renci.SshNet/ForwardedPortLocal.NET40.cs index 150d38e..1618901 100644 --- a/Renci.SshNet/ForwardedPortLocal.NET40.cs +++ b/Renci.SshNet/ForwardedPortLocal.NET40.cs @@ -1,9 +1,6 @@ -using System.Threading.Tasks; -using System; -using System.Net.Sockets; -using System.Net; +using System; using System.Threading; -using Renci.SshNet.Channels; +using System.Threading.Tasks; namespace Renci.SshNet { @@ -12,84 +9,9 @@ namespace Renci.SshNet /// public partial class ForwardedPortLocal { - private TcpListener _listener; - partial void ExecuteThread(Action action) { - Task.Factory.StartNew(action); - } - - partial void InternalStart() - { - // If port already started don't start it again - if (this.IsStarted) - return; - - var ep = new IPEndPoint(Dns.GetHostAddresses(this.BoundHost)[0], (int)this.BoundPort); - - this._listener = new TcpListener(ep); - this._listener.Start(); - - this._listenerTaskCompleted = new ManualResetEvent(false); - this.ExecuteThread(() => - { - try - { - while (true) - { - var socket = this._listener.AcceptSocket(); - - this.ExecuteThread(() => - { - try - { - IPEndPoint originatorEndPoint = socket.RemoteEndPoint as IPEndPoint; - - this.RaiseRequestReceived(originatorEndPoint.Address.ToString(), (uint)originatorEndPoint.Port); - - var channel = this.Session.CreateChannel(); - - channel.Bind(this.Host, this.Port, socket); - } - catch (Exception exp) - { - this.RaiseExceptionEvent(exp); - } - }); - } - } - catch (SocketException exp) - { - if (!(exp.SocketErrorCode == SocketError.Interrupted)) - { - this.RaiseExceptionEvent(exp); - } - } - catch (Exception exp) - { - this.RaiseExceptionEvent(exp); - } - finally - { - this._listenerTaskCompleted.Set(); - } - }); - - this.IsStarted = true; - } - - partial void InternalStop() - { - // If port not started you cant stop it - if (!this.IsStarted) - return; - - this._listener.Stop(); - this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout); - this._listenerTaskCompleted.Dispose(); - this._listenerTaskCompleted = null; - - this.IsStarted = false; + ThreadPool.QueueUserWorkItem((o) => { action(); }); } } } diff --git a/Renci.SshNet/ForwardedPortLocal.cs b/Renci.SshNet/ForwardedPortLocal.cs index 599f64e..1c724e9 100644 --- a/Renci.SshNet/ForwardedPortLocal.cs +++ b/Renci.SshNet/ForwardedPortLocal.cs @@ -10,6 +10,74 @@ public partial class ForwardedPortLocal : ForwardedPort, IDisposable { private EventWaitHandle _listenerTaskCompleted; + /// + /// Gets the bound host. + /// + public string BoundHost { get; protected set; } + + /// + /// Gets the bound port. + /// + public uint BoundPort { get; protected set; } + + /// + /// Gets the forwarded host. + /// + public string Host { get; protected set; } + + /// + /// Gets the forwarded port. + /// + public uint Port { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// The bound port. + /// The host. + /// The port. + /// + /// + /// + public ForwardedPortLocal(uint boundPort, string host, uint port) + : this(string.Empty, boundPort, host, port) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The bound host. + /// The bound port. + /// The host. + /// The port. + public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint port) + { + if (boundHost == null) + throw new ArgumentNullException("boundHost"); + + if (host == null) + throw new ArgumentNullException("host"); + + if (!boundHost.IsValidHost()) + throw new ArgumentException("boundHost"); + + if (!boundPort.IsValidPort()) + throw new ArgumentOutOfRangeException("boundPort"); + + if (!host.IsValidHost()) + throw new ArgumentException("host"); + + if (!port.IsValidPort()) + throw new ArgumentOutOfRangeException("port"); + + this.BoundHost = boundHost; + this.BoundPort = boundPort; + this.Host = host; + this.Port = port; + } + + /// /// Starts local port forwarding. /// @@ -57,6 +125,8 @@ protected virtual void Dispose(bool disposing) // Check to see if Dispose has already been called. if (!this._isDisposed) { + this.InternalStop(); + // If disposing equals true, dispose all managed // and unmanaged ResourceMessages. if (disposing) diff --git a/Renci.SshNet/ForwardedPortRemote.NET.cs b/Renci.SshNet/ForwardedPortRemote.NET.cs new file mode 100644 index 0000000..3627d8b --- /dev/null +++ b/Renci.SshNet/ForwardedPortRemote.NET.cs @@ -0,0 +1,37 @@ +using System; +using System.Net; + +namespace Renci.SshNet +{ + /// + /// Provides functionality for remote port forwarding + /// + public partial class ForwardedPortRemote + { + /// + /// Initializes a new instance of the class. + /// + /// The bound port. + /// The host. + /// The port. + /// + /// + /// + public ForwardedPortRemote(uint boundPort, string host, uint port) + : this(string.Empty, boundPort, host, port) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The bound host. + /// The bound port. + /// The host. + /// The port. + public ForwardedPortRemote(string boundHost, uint boundPort, string host, uint port) + : this(Dns.GetHostEntry(boundHost).AddressList[0], boundPort, Dns.GetHostEntry(host).AddressList[0], port) + { + } + } +} diff --git a/Renci.SshNet/ForwardedPortRemote.NET40.cs b/Renci.SshNet/ForwardedPortRemote.NET40.cs index 257cc72..7b3a9ed 100644 --- a/Renci.SshNet/ForwardedPortRemote.NET40.cs +++ b/Renci.SshNet/ForwardedPortRemote.NET40.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; namespace Renci.SshNet @@ -10,7 +11,7 @@ public partial class ForwardedPortRemote { partial void ExecuteThread(Action action) { - Task.Factory.StartNew(action); + ThreadPool.QueueUserWorkItem((o) => { action(); }); } } } diff --git a/Renci.SshNet/ForwardedPortRemote.cs b/Renci.SshNet/ForwardedPortRemote.cs index a9310b0..67128a3 100644 --- a/Renci.SshNet/ForwardedPortRemote.cs +++ b/Renci.SshNet/ForwardedPortRemote.cs @@ -5,6 +5,7 @@ using Renci.SshNet.Common; using System.Diagnostics; using System.Globalization; +using System.Net; namespace Renci.SshNet { @@ -17,6 +18,77 @@ public partial class ForwardedPortRemote : ForwardedPort, IDisposable private EventWaitHandle _globalRequestResponse = new AutoResetEvent(false); + /// + /// Gets the bound host. + /// + public IPAddress BoundHostAddress { get; protected set; } + + /// + /// Gets the bound host. + /// + public string BoundHost + { + get + { + return this.BoundHostAddress.ToString(); + } + } + + /// + /// Gets the bound port. + /// + public uint BoundPort { get; protected set; } + + /// + /// Gets the forwarded host. + /// + public IPAddress HostAddress { get; protected set; } + + /// + /// Gets the forwarded host. + /// + public string Host + { + get + { + return this.HostAddress.ToString(); + } + } + + /// + /// Gets the forwarded port. + /// + public uint Port { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// The bound host address. + /// The bound port. + /// The host address. + /// The port. + /// boundHost + /// boundPort + public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress hostAddress, uint port) + { + if (boundHostAddress == null) + throw new ArgumentNullException("boundHost"); + + if (hostAddress == null) + throw new ArgumentNullException("host"); + + if (!boundPort.IsValidPort()) + throw new ArgumentOutOfRangeException("boundPort"); + + if (!port.IsValidPort()) + throw new ArgumentOutOfRangeException("port"); + + this.BoundHostAddress = boundHostAddress; + this.BoundPort = boundPort; + this.HostAddress = hostAddress; + this.Port = port; + } + /// /// Starts remote port forwarding. /// @@ -92,7 +164,7 @@ private void Session_ChannelOpening(object sender, MessageEventArgs(e.Message.LocalChannelNumber, e.Message.InitialWindowSize, e.Message.MaximumPacketSize); - channel.Bind(this.Host, this.Port); + channel.Bind(this.HostAddress, this.Port); } catch (Exception exp) { diff --git a/Renci.SshNet/HashInfo.cs b/Renci.SshNet/HashInfo.cs new file mode 100644 index 0000000..0ef66e2 --- /dev/null +++ b/Renci.SshNet/HashInfo.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Renci.SshNet.Security.Cryptography; +using System.Security.Cryptography; + +namespace Renci.SshNet +{ + /// + /// Holds information about key size and cipher to use + /// + public class HashInfo + { + /// + /// Gets the size of the key. + /// + /// + /// The size of the key. + /// + public int KeySize { get; private set; } + + /// + /// Gets the cipher. + /// + public Func HashAlgorithm { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// Size of the key. + /// The cipher. + public HashInfo(int keySize, Func hash) + { + this.KeySize = keySize; + this.HashAlgorithm = (key) => (hash(key.Take(this.KeySize / 8).ToArray())); + } + } +} diff --git a/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.NET40.cs b/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.NET40.cs new file mode 100644 index 0000000..bae885e --- /dev/null +++ b/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.NET40.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Common; +using System.Threading.Tasks; + +namespace Renci.SshNet +{ + public partial class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod + { + /// is null. + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem((o) => { action(); }); + } + } +} diff --git a/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs b/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs new file mode 100644 index 0000000..1df7631 --- /dev/null +++ b/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Common; + +namespace Renci.SshNet +{ + /// + /// Provides functionality to perform keyboard interactive authentication. + /// + public partial class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod, IDisposable + { + private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; + + private Session _session; + + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + + private Exception _exception; + + private RequestMessage _requestMessage; + + /// + /// Gets authentication method name + /// + public override string Name + { + get { return this._requestMessage.MethodName; } + } + + /// + /// Occurs when server prompts for more authentication information. + /// + public event EventHandler AuthenticationPrompt; + + /// + /// Initializes a new instance of the class. + /// + /// The username. + /// is whitespace or null. + public KeyboardInteractiveAuthenticationMethod(string username) + : base(username) + { + this._requestMessage = new RequestMessageKeyboardInteractive(ServiceName.Connection, username); + } + + /// + /// Authenticates the specified session. + /// + /// The session to authenticate. + /// Result of authentication process. + public override AuthenticationResult Authenticate(Session session) + { + this._session = session; + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; + session.MessageReceived += Session_MessageReceived; + + session.RegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); + + session.SendMessage(this._requestMessage); + + session.WaitHandle(this._authenticationCompleted); + + session.UnRegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); + + + session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; + session.MessageReceived -= Session_MessageReceived; + + + if (this._exception != null) + { + throw this._exception; + } + + return this._authenticationResult; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this._authenticationResult = AuthenticationResult.Success; + + this._authenticationCompleted.Set(); + } + + private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + { + if (e.Message.PartialSuccess) + this._authenticationResult = AuthenticationResult.PartialSuccess; + else + this._authenticationResult = AuthenticationResult.Failure; + + // Copy allowed authentication methods + this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); + + this._authenticationCompleted.Set(); + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + var informationRequestMessage = e.Message as InformationRequestMessage; + if (informationRequestMessage != null) + { + var eventArgs = new AuthenticationPromptEventArgs(this.Username, informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts); + + this.ExecuteThread(() => + { + try + { + if (this.AuthenticationPrompt != null) + { + this.AuthenticationPrompt(this, eventArgs); + } + + var informationResponse = new InformationResponseMessage(); + + foreach (var response in from r in eventArgs.Prompts orderby r.Id ascending select r.Response) + { + informationResponse.Responses.Add(response); + } + + // Send information response message + this._session.SendMessage(informationResponse); + } + catch (Exception exp) + { + this._exception = exp; + this._authenticationCompleted.Set(); + } + }); + } + } + + partial void ExecuteThread(Action action); + + #region IDisposable Members + + private bool isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this.isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + if (this._authenticationCompleted != null) + { + this._authenticationCompleted.Dispose(); + this._authenticationCompleted = null; + } + } + + // Note disposing has been done. + isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~KeyboardInteractiveAuthenticationMethod() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + } +} diff --git a/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs b/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs index fef39d9..f988be9 100644 --- a/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs +++ b/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs @@ -2,9 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages; using Renci.SshNet.Common; namespace Renci.SshNet @@ -12,37 +9,40 @@ namespace Renci.SshNet /// /// Provides connection information when keyboard interactive authentication method is used /// - public partial class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable + /// + /// + /// + public class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable { - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - - private Exception _exception; + /// + /// Occurs when server prompts for more authentication information. + /// + /// + /// + /// + public event EventHandler AuthenticationPrompt; - private RequestMessage _requestMessage; + // TODO: DOCS Add exception documentation for this class. /// - /// Gets connection name + /// Initializes a new instance of the class. /// - public override string Name + /// The host. + /// The username. + public KeyboardInteractiveConnectionInfo(string host, string username) + : this(host, ConnectionInfo.DEFAULT_PORT, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) { - get - { - return this._requestMessage.MethodName; - } - } - /// - /// Occurs when server prompts for more authentication information. - /// - public event EventHandler AuthenticationPrompt; + } /// /// Initializes a new instance of the class. /// /// The host. + /// The port. /// The username. - public KeyboardInteractiveConnectionInfo(string host, string username) - : this(host, 22, username) + public KeyboardInteractiveConnectionInfo(string host, int port, string username) + : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) { } @@ -53,94 +53,100 @@ public KeyboardInteractiveConnectionInfo(string host, string username) /// Connection host. /// Connection port. /// Connection username. - public KeyboardInteractiveConnectionInfo(string host, int port, string username) - : base(host, port, username) + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) { - this._requestMessage = new RequestMessageKeyboardInteractive(ServiceName.Connection, username); } /// - /// Called when connection needs to be authenticated. + /// Initializes a new instance of the class. /// - protected override void OnAuthenticate() + /// Connection host. + /// Connection port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) { - this.Session.RegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); - - this.Session.SendMessage(this._requestMessage); - - this.WaitHandle(this._authenticationCompleted); - - this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); + } - if (this._exception != null) - { - throw this._exception; - } + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) + { } /// - /// Handles the UserAuthenticationSuccessMessageReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs e) + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) { - base.Session_UserAuthenticationSuccessMessageReceived(sender, e); - this._authenticationCompleted.Set(); } /// - /// Handles the UserAuthenticationFailureReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) + : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) { - base.Session_UserAuthenticationFailureReceived(sender, e); - this._authenticationCompleted.Set(); } /// - /// Handles the MessageReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_MessageReceived(object sender, MessageEventArgs e) + /// Connection host. + /// Connection port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) + : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new KeyboardInteractiveAuthenticationMethod(username)) { - var informationRequestMessage = e.Message as InformationRequestMessage; - if (informationRequestMessage != null) + foreach (var authenticationMethod in this.AuthenticationMethods.OfType()) { - var eventArgs = new AuthenticationPromptEventArgs(this.Username, informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts); - - this.ExecuteThread(() => - { - try - { - if (this.AuthenticationPrompt != null) - { - this.AuthenticationPrompt(this, eventArgs); - } - - var informationResponse = new InformationResponseMessage(); + authenticationMethod.AuthenticationPrompt += AuthenticationMethod_AuthenticationPrompt; + } - foreach (var response in from r in eventArgs.Prompts orderby r.Id ascending select r.Response) - { - informationResponse.Responses.Add(response); - } + } - // Send information response message - this.SendMessage(informationResponse); - } - catch (Exception exp) - { - this._exception = exp; - this._authenticationCompleted.Set(); - } - }); + private void AuthenticationMethod_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e) + { + if (this.AuthenticationPrompt != null) + { + this.AuthenticationPrompt(sender, e); } } - partial void ExecuteThread(Action action); #region IDisposable Members @@ -170,10 +176,12 @@ protected virtual void Dispose(bool disposing) if (disposing) { // Dispose managed resources. - if (this._authenticationCompleted != null) + if (this.AuthenticationMethods != null) { - this._authenticationCompleted.Dispose(); - this._authenticationCompleted = null; + foreach (var authenticationMethods in this.AuthenticationMethods.OfType()) + { + authenticationMethods.Dispose(); + } } } @@ -184,7 +192,7 @@ protected virtual void Dispose(bool disposing) /// /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// ~KeyboardInteractiveConnectionInfo() { diff --git a/Renci.SshNet/MessageEventArgs.cs b/Renci.SshNet/MessageEventArgs.cs index fe8352e..fddf7ad 100644 --- a/Renci.SshNet/MessageEventArgs.cs +++ b/Renci.SshNet/MessageEventArgs.cs @@ -17,8 +17,12 @@ public class MessageEventArgs : EventArgs /// Initializes a new instance of the class. /// /// The message. + /// is null. public MessageEventArgs(T message) { + if (message == null) + throw new ArgumentNullException("message"); + this.Message = message; } } diff --git a/Renci.SshNet/Messages/Authentication/BannerMessage.cs b/Renci.SshNet/Messages/Authentication/BannerMessage.cs index 17d0cfe..d408457 100644 --- a/Renci.SshNet/Messages/Authentication/BannerMessage.cs +++ b/Renci.SshNet/Messages/Authentication/BannerMessage.cs @@ -32,7 +32,7 @@ protected override void LoadData() /// protected override void SaveData() { - this.Write(this.Message, Encoding.UTF8); + this.Write(this.Message); this.Write(this.Language); } } diff --git a/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs b/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs index f0ea0fd..7bbe024 100644 --- a/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs +++ b/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs @@ -27,7 +27,7 @@ internal class PublicKeyMessage : Message /// protected override void LoadData() { - this.PublicKeyAlgorithmName = this.ReadString(); + this.PublicKeyAlgorithmName = this.ReadAsciiString(); this.PublicKeyData = this.ReadBinaryString(); } @@ -36,7 +36,7 @@ protected override void LoadData() /// protected override void SaveData() { - this.Write(this.PublicKeyAlgorithmName); + this.WriteAscii(this.PublicKeyAlgorithmName); this.WriteBinaryString(this.PublicKeyData); } } diff --git a/Renci.SshNet/Messages/Authentication/RequestMessage.cs b/Renci.SshNet/Messages/Authentication/RequestMessage.cs index f27fafe..df5220f 100644 --- a/Renci.SshNet/Messages/Authentication/RequestMessage.cs +++ b/Renci.SshNet/Messages/Authentication/RequestMessage.cs @@ -54,19 +54,19 @@ protected override void LoadData() /// protected override void SaveData() { - this.Write(this.Username, Encoding.UTF8); + this.Write(this.Username); switch (this.ServiceName) { case ServiceName.UserAuthentication: - this.Write("ssh-userauth", Encoding.UTF8); + this.WriteAscii("ssh-userauth"); break; case ServiceName.Connection: - this.Write("ssh-connection", Encoding.UTF8); + this.WriteAscii("ssh-connection"); break; default: throw new NotSupportedException("Not supported service name"); } - this.Write(this.MethodName); + this.WriteAscii(this.MethodName); } } } diff --git a/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs b/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs index 731d66e..c7357da 100644 --- a/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs +++ b/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs @@ -83,10 +83,10 @@ protected override void SaveData() { base.SaveData(); - this.Write(this.PublicKeyAlgorithm); + this.WriteAscii(this.PublicKeyAlgorithm); this.WriteBinaryString(this.PublicHostKey); this.Write(this.ClientHostName); - this.Write(this.ClientUsername, Encoding.UTF8); + this.Write(this.ClientUsername); if (this.Signature != null) this.WriteBinaryString(this.Signature); diff --git a/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs b/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs index 51f28c7..8807d1d 100644 --- a/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs +++ b/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs @@ -52,7 +52,7 @@ protected override void SaveData() this.Write(this.Language); - this.Write(this.SubMethods, Encoding.UTF8); + this.Write(this.SubMethods); } } } diff --git a/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs b/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs index f21b741..f275803 100644 --- a/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs +++ b/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs @@ -24,12 +24,12 @@ public override string MethodName /// /// Gets authentication password. /// - public string Password { get; private set; } + public byte[] Password { get; private set; } /// /// Gets new authentication password. /// - public string NewPassword { get; private set; } + public byte[] NewPassword { get; private set; } /// /// Initializes a new instance of the class. @@ -37,10 +37,10 @@ public override string MethodName /// Name of the service. /// Authentication username. /// Authentication password. - public RequestMessagePassword(ServiceName serviceName, string username, string password) + public RequestMessagePassword(ServiceName serviceName, string username, byte[] password) : base(serviceName, username) { - this.Password = password ?? string.Empty; + this.Password = password; } /// @@ -50,10 +50,10 @@ public RequestMessagePassword(ServiceName serviceName, string username, string p /// Authentication username. /// Authentication password. /// New authentication password. - public RequestMessagePassword(ServiceName serviceName, string username, string password, string newPassword) + public RequestMessagePassword(ServiceName serviceName, string username, byte[] password, byte[] newPassword) : this(serviceName, username, password) { - this.NewPassword = newPassword ?? string.Empty; + this.NewPassword = newPassword; } /// @@ -63,13 +63,15 @@ protected override void SaveData() { base.SaveData(); - this.Write(!string.IsNullOrEmpty(this.NewPassword)); + this.Write(this.NewPassword != null); - this.Write(this.Password, Encoding.UTF8); + this.Write((uint)this.Password.Length); + this.Write(this.Password); - if (!string.IsNullOrEmpty(this.NewPassword)) + if (this.NewPassword != null) { - this.Write(this.NewPassword, Encoding.UTF8); + this.Write((uint)this.NewPassword.Length); + this.Write(this.NewPassword); } } } diff --git a/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs b/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs index 94aa7bf..2442a22 100644 --- a/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs +++ b/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs @@ -85,7 +85,7 @@ protected override void SaveData() { this.Write(true); } - this.Write(this.PublicKeyAlgorithmName); + this.WriteAscii(this.PublicKeyAlgorithmName); this.WriteBinaryString(this.PublicKeyData); if (this.Signature != null) this.WriteBinaryString(this.Signature); diff --git a/Renci.SshNet/Messages/Connection/ChannelExtendedDataMessage.cs b/Renci.SshNet/Messages/Connection/ChannelExtendedDataMessage.cs index 5cb2131..38a03ae 100644 --- a/Renci.SshNet/Messages/Connection/ChannelExtendedDataMessage.cs +++ b/Renci.SshNet/Messages/Connection/ChannelExtendedDataMessage.cs @@ -28,9 +28,11 @@ public ChannelExtendedDataMessage() /// Initializes a new instance of the class. /// /// The local channel number. - public ChannelExtendedDataMessage(uint localChannelNumber) + public ChannelExtendedDataMessage(uint localChannelNumber, uint dataTypeCode, byte[] data) { this.LocalChannelNumber = localChannelNumber; + this.DataTypeCode = dataTypeCode; + this.Data = data; } /// diff --git a/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs b/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs index a10c302..aebeda7 100644 --- a/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs +++ b/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs @@ -72,7 +72,7 @@ public ChannelOpenMessage(uint channelNumber, uint initialWindowSize, uint maxim /// protected override void LoadData() { - var channelName = this.ReadString(); + var channelName = this.ReadAsciiString(); this.LocalChannelNumber = this.ReadUInt32(); this.InitialWindowSize = this.ReadUInt32(); this.MaximumPacketSize = this.ReadUInt32(); @@ -108,7 +108,7 @@ protected override void LoadData() /// protected override void SaveData() { - this.Write(this.ChannelType); + this.WriteAscii(this.ChannelType); this.Write(this.LocalChannelNumber); this.Write(this.InitialWindowSize); this.Write(this.MaximumPacketSize); diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs new file mode 100644 index 0000000..cf9e3ab --- /dev/null +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Messages.Connection +{ + /// + /// Represents "break" type channel request information + /// + internal class BreakRequestInfo : RequestInfo + { + /// + /// Channel request name + /// + public const string NAME = "break"; + + /// + /// Gets the name of the request. + /// + /// + /// The name of the request. + /// + public override string RequestName + { + get { return BreakRequestInfo.NAME; } + } + + /// + /// Gets break length in milliseconds. + /// + public UInt32 BreakLength { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public BreakRequestInfo() + { + this.WantReply = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// Length of the break. + public BreakRequestInfo(UInt32 breakLength) + : this() + { + this.BreakLength = breakLength; + } + + /// + /// Called when type specific data need to be loaded. + /// + protected override void LoadData() + { + base.LoadData(); + + this.BreakLength = this.ReadUInt32(); + } + + /// + /// Called when type specific data need to be saved. + /// + protected override void SaveData() + { + base.SaveData(); + + this.Write(this.BreakLength); + } + } +} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs index 34feca0..d602f9c 100644 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs @@ -49,7 +49,7 @@ protected override void LoadData() { base.LoadData(); - this.RequestName = this.ReadString(); + this.RequestName = this.ReadAsciiString(); this.RequestData = this.ReadBytes(); } @@ -60,7 +60,7 @@ protected override void SaveData() { base.SaveData(); - this.Write(this.RequestName); + this.WriteAscii(this.RequestName); this.Write(this.RequestData); } } diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs index 1d0b72b..86c4b54 100644 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs @@ -26,8 +26,19 @@ public override string RequestName /// /// Gets command to execute. /// + /// + /// The command. + /// public string Command { get; private set; } + /// + /// Gets the encoding. + /// + /// + /// The encoding. + /// + public Encoding Encoding { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -40,10 +51,15 @@ public ExecRequestInfo() /// Initializes a new instance of the class. /// /// The command. - public ExecRequestInfo(string command) + /// is null. + public ExecRequestInfo(string command, Encoding encoding) : this() { + if (command == null) + throw new System.ArgumentNullException("command"); + this.Command = command; + this.Encoding = encoding; } /// @@ -63,7 +79,7 @@ protected override void SaveData() { base.SaveData(); - this.Write(this.Command, Encoding.UTF8); + this.Write(this.Command, this.Encoding); } } } diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs index a64e0ef..8be8535 100644 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs @@ -79,7 +79,7 @@ protected override void LoadData() { base.LoadData(); - this.SignalName = this.ReadString(); + this.SignalName = this.ReadAsciiString(); this.CoreDumped = this.ReadBoolean(); this.ErrorMessage = this.ReadString(); this.Language = this.ReadString(); @@ -92,10 +92,10 @@ protected override void SaveData() { base.SaveData(); - this.Write(this.SignalName); + this.WriteAscii(this.SignalName); this.Write(this.CoreDumped); this.Write(this.ErrorMessage); - this.Write(this.Language, Encoding.UTF8); + this.Write(this.Language); } } diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs index be70305..5bc34cf 100644 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs @@ -1,4 +1,7 @@ -namespace Renci.SshNet.Messages.Connection +using System; +using Renci.SshNet.Common; +using System.Collections.Generic; +namespace Renci.SshNet.Messages.Connection { /// /// Represents "pty-req" type channel request information @@ -67,7 +70,7 @@ public override string RequestName /// /// The terminal mode. /// - public string TerminalMode { get; set; } + public IDictionary TerminalModeValues { get; set; } /// /// Initializes a new instance of the class. @@ -85,8 +88,8 @@ public PseudoTerminalRequestInfo() /// The rows. /// The width. /// The height. - /// The terminal mode. - public PseudoTerminalRequestInfo(string environmentVariable, uint columns, uint rows, uint width, uint height, string terminalMode) + /// The terminal mode values. + public PseudoTerminalRequestInfo(string environmentVariable, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues) : this() { this.EnvironmentVariable = environmentVariable; @@ -94,22 +97,7 @@ public PseudoTerminalRequestInfo(string environmentVariable, uint columns, uint this.Rows = rows; this.PixelWidth = width; this.PixelHeight = height; - this.TerminalMode = terminalMode; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.EnvironmentVariable = this.ReadString(); - this.Columns = this.ReadUInt32(); - this.Rows = this.ReadUInt32(); - this.PixelWidth = this.ReadUInt32(); - this.PixelHeight = this.ReadUInt32(); - this.TerminalMode = this.ReadString(); + this.TerminalModeValues = terminalModeValues; } /// @@ -124,8 +112,22 @@ protected override void SaveData() this.Write(this.Rows); this.Write(this.Rows); this.Write(this.PixelHeight); - this.Write(this.TerminalMode); + if (this.TerminalModeValues != null) + { + this.Write((uint)this.TerminalModeValues.Count * (1 + 4) + 1); + + foreach (var item in this.TerminalModeValues) + { + this.Write((byte)item.Key); + this.Write(item.Value); + } + this.Write((byte)0); + } + else + { + this.Write((uint)0); + } } } } diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs index eb50dce..2e65001 100644 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs @@ -54,7 +54,7 @@ protected override void LoadData() { base.LoadData(); - this.SignalName = this.ReadString(); + this.SignalName = this.ReadAsciiString(); } /// @@ -64,7 +64,7 @@ protected override void SaveData() { base.SaveData(); - this.Write(this.SignalName); + this.WriteAscii(this.SignalName); } } } diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs index 81e5d6d..ea31542 100644 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs @@ -54,7 +54,7 @@ protected override void LoadData() { base.LoadData(); - this.SubsystemName = this.ReadString(); + this.SubsystemName = this.ReadAsciiString(); } /// @@ -64,7 +64,7 @@ protected override void SaveData() { base.SaveData(); - this.Write(this.SubsystemName); + this.WriteAscii(this.SubsystemName); } } } diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs index 3ccb2e9..12cfc84 100644 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs +++ b/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs @@ -85,7 +85,7 @@ protected override void LoadData() base.LoadData(); this.IsSingleConnection = this.ReadBoolean(); - this.AuthenticationProtocol = this.ReadString(); + this.AuthenticationProtocol = this.ReadAsciiString(); this.AuthenticationCookie = this.ReadBinaryString(); this.ScreenNumber = this.ReadUInt32(); } @@ -98,7 +98,7 @@ protected override void SaveData() base.SaveData(); this.Write(this.IsSingleConnection); - this.Write(this.AuthenticationProtocol); + this.WriteAscii(this.AuthenticationProtocol); this.WriteBinaryString(this.AuthenticationCookie); this.Write(this.ScreenNumber); } diff --git a/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs b/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs index c9a6891..4f60c36 100644 --- a/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs +++ b/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs @@ -73,22 +73,25 @@ public GlobalRequestMessage(GlobalRequestName requestName, bool wantReply, strin /// protected override void LoadData() { - var requestName = this.ReadString(); + var requestName = this.ReadAsciiString(); + + this.WantReply = this.ReadBoolean(); + switch (requestName) { case "tcpip-forward": this.RequestName = GlobalRequestName.TcpIpForward; + this.AddressToBind = this.ReadString(); + this.PortToBind = this.ReadUInt32(); break; case "cancel-tcpip-forward": this.RequestName = GlobalRequestName.CancelTcpIpForward; + this.AddressToBind = this.ReadString(); + this.PortToBind = this.ReadUInt32(); break; default: break; } - - this.WantReply = this.ReadBoolean(); - this.AddressToBind = this.ReadString(); - this.PortToBind = this.ReadUInt32(); } /// @@ -99,10 +102,10 @@ protected override void SaveData() switch (this.RequestName) { case GlobalRequestName.TcpIpForward: - this.Write("tcpip-forward"); + this.WriteAscii("tcpip-forward"); break; case GlobalRequestName.CancelTcpIpForward: - this.Write("cancel-tcpip-forward"); + this.WriteAscii("cancel-tcpip-forward"); break; default: break; diff --git a/Renci.SshNet/Messages/Message.cs b/Renci.SshNet/Messages/Message.cs index 0614676..4fe37f1 100644 --- a/Renci.SshNet/Messages/Message.cs +++ b/Renci.SshNet/Messages/Message.cs @@ -10,25 +10,6 @@ namespace Renci.SshNet.Messages /// public abstract class Message : SshData { - /// - /// Loads the specified data. - /// - /// SSH message type - /// Message data. - /// SSH message object - internal static T Load(byte[] data) where T : Message, new() - { - T message = new T(); - - message.LoadBytes(data); - - message.ResetReader(); - - message.LoadData(); - - return message; - } - /// /// Gets the index that represents zero in current data type. /// @@ -46,7 +27,7 @@ protected override int ZeroReaderIndex /// /// Gets data bytes array /// - /// + /// Byte array representation of the message public override byte[] GetBytes() { var messageAttribute = this.GetType().GetCustomAttributes(typeof(MessageAttribute), true).SingleOrDefault() as MessageAttribute; diff --git a/Renci.SshNet/Messages/Transport/DisconnectMessage.cs b/Renci.SshNet/Messages/Transport/DisconnectMessage.cs index 22ea569..bfc7bdb 100644 --- a/Renci.SshNet/Messages/Transport/DisconnectMessage.cs +++ b/Renci.SshNet/Messages/Transport/DisconnectMessage.cs @@ -6,7 +6,7 @@ namespace Renci.SshNet.Messages.Transport /// Represents SSH_MSG_DISCONNECT message. /// [Message("SSH_MSG_DISCONNECT", 1)] - public class DisconnectMessage : Message,IKeyExchangedAllowed + public class DisconnectMessage : Message, IKeyExchangedAllowed { /// /// Gets disconnect reason code. @@ -58,7 +58,7 @@ protected override void LoadData() protected override void SaveData() { this.Write((uint)this.ReasonCode); - this.Write(this.Description, Encoding.UTF8); + this.Write(this.Description); this.Write(this.Language ?? "en"); } } diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs index 3d00cc3..8ecdf82 100644 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs +++ b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs @@ -10,7 +10,7 @@ namespace Renci.SshNet.Messages.Transport /// Represents SSH_MSG_KEX_DH_GEX_INIT message. /// [Message("SSH_MSG_KEX_DH_GEX_INIT", 32)] - internal class KeyExchangeDhGroupExchangeInit : Message,IKeyExchangedAllowed + internal class KeyExchangeDhGroupExchangeInit : Message, IKeyExchangedAllowed { /// /// Gets the E value. diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs index 19725db..7c29004 100644 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs +++ b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs @@ -9,7 +9,7 @@ namespace Renci.SshNet.Messages.Transport /// Represents SSH_MSG_KEX_DH_GEX_REQUEST message. /// [Message("SSH_MSG_KEX_DH_GEX_REQUEST", 34)] - internal class KeyExchangeDhGroupExchangeRequest : Message,IKeyExchangedAllowed + internal class KeyExchangeDhGroupExchangeRequest : Message, IKeyExchangedAllowed { /// /// Gets or sets the minimal size in bits of an acceptable group. diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs index b19f109..30e88c0 100644 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs +++ b/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs @@ -6,7 +6,7 @@ namespace Renci.SshNet.Messages.Transport /// Represents SSH_MSG_KEXDH_INIT message. /// [Message("SSH_MSG_KEXDH_INIT", 30)] - internal class KeyExchangeDhInitMessage : Message,IKeyExchangedAllowed + internal class KeyExchangeDhInitMessage : Message, IKeyExchangedAllowed { /// /// Gets the E value. diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs new file mode 100644 index 0000000..9753bbe --- /dev/null +++ b/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs @@ -0,0 +1,50 @@ +using Renci.SshNet.Common; +using System.Linq; +using System.Collections.Generic; + +namespace Renci.SshNet.Messages.Transport +{ + /// + /// Represents SSH_MSG_KEXECDH_INIT message. + /// + [Message("SSH_MSG_KEXECDH_INIT", 30)] + internal class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed + { + /// + /// Gets the client's ephemeral contribution to the ECDH exchange, encoded as an octet string + /// + public byte[] QC { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The client exchange value. + public KeyExchangeEcdhInitMessage(BigInteger d, BigInteger Q) + { + var a = d.ToByteArray().Reverse(); + var b = Q.ToByteArray().Reverse(); + var data = new List(); + data.Add(0x04); + data.AddRange(d.ToByteArray().Reverse()); + data.AddRange(Q.ToByteArray().Reverse()); + this.QC = data.ToArray(); + } + + /// + /// Called when type specific data need to be loaded. + /// + protected override void LoadData() + { + this.ResetReader(); + this.QC = this.ReadBinaryString(); + } + + /// + /// Called when type specific data need to be saved. + /// + protected override void SaveData() + { + this.WriteBinaryString(this.QC); + } + } +} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs new file mode 100644 index 0000000..a7d1d3a --- /dev/null +++ b/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs @@ -0,0 +1,50 @@ +using System; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Messages.Transport +{ + /// + /// Represents SSH_MSG_KEXECDH_REPLY message. + /// + [Message("SSH_MSG_KEXECDH_REPLY", 31)] + public class KeyExchangeEcdhReplyMessage : Message + { + /// + /// Gets a string encoding an X.509v3 certificate containing the server's ECDSA public host key + /// + /// The host key. + public byte[] KS { get; private set; } + + /// + /// Gets the the server's ephemeral contribution to the ECDH exchange, encoded as an octet string. + /// + public byte[] QS { get; private set; } + + /// + /// Gets the an octet string containing the server's signature of the newly established exchange hash value. + /// + /// The signature. + public byte[] Signature { get; private set; } + + /// + /// Called when type specific data need to be loaded. + /// + protected override void LoadData() + { + this.ResetReader(); + this.KS = this.ReadBinaryString(); + this.QS = this.ReadBinaryString(); + this.Signature = this.ReadBinaryString(); + } + + /// + /// Called when type specific data need to be saved. + /// + protected override void SaveData() + { + this.WriteBinaryString(this.KS); + this.WriteBinaryString(this.QS); + this.WriteBinaryString(this.Signature); + } + } +} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs index 7e8556c..312b3b1 100644 --- a/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs +++ b/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs @@ -8,7 +8,7 @@ namespace Renci.SshNet.Messages.Transport /// Represents SSH_MSG_KEXINIT message. /// [Message("SSH_MSG_KEXINIT", 20)] - public class KeyExchangeInitMessage : Message,IKeyExchangedAllowed + public class KeyExchangeInitMessage : Message, IKeyExchangedAllowed { private static RNGCryptoServiceProvider _randomizer = new System.Security.Cryptography.RNGCryptoServiceProvider(); diff --git a/Renci.SshNet/Messages/Transport/NewKeysMessage.cs b/Renci.SshNet/Messages/Transport/NewKeysMessage.cs index d5b6512..2d678c4 100644 --- a/Renci.SshNet/Messages/Transport/NewKeysMessage.cs +++ b/Renci.SshNet/Messages/Transport/NewKeysMessage.cs @@ -4,7 +4,7 @@ /// Represents SSH_MSG_KEXINIT message. /// [Message("SSH_MSG_NEWKEYS", 21)] - public class NewKeysMessage : Message,IKeyExchangedAllowed + public class NewKeysMessage : Message, IKeyExchangedAllowed { /// /// Called when type specific data need to be loaded. diff --git a/Renci.SshNet/Messages/Transport/ServiceAcceptMessage.cs b/Renci.SshNet/Messages/Transport/ServiceAcceptMessage.cs index d6bc98b..06e6ce6 100644 --- a/Renci.SshNet/Messages/Transport/ServiceAcceptMessage.cs +++ b/Renci.SshNet/Messages/Transport/ServiceAcceptMessage.cs @@ -21,7 +21,7 @@ public class ServiceAcceptMessage : Message /// protected override void LoadData() { - var serviceName = this.ReadString(); + var serviceName = this.ReadAsciiString(); switch (serviceName) { case "ssh-userauth": diff --git a/Renci.SshNet/Messages/Transport/ServiceRequestMessage.cs b/Renci.SshNet/Messages/Transport/ServiceRequestMessage.cs index 3a50bb6..a3687dc 100644 --- a/Renci.SshNet/Messages/Transport/ServiceRequestMessage.cs +++ b/Renci.SshNet/Messages/Transport/ServiceRequestMessage.cs @@ -41,10 +41,10 @@ protected override void SaveData() switch (this.ServiceName) { case ServiceName.UserAuthentication: - this.Write("ssh-userauth"); + this.WriteAscii("ssh-userauth"); break; case ServiceName.Connection: - this.Write("ssh-connection"); + this.WriteAscii("ssh-connection"); break; default: throw new NotSupportedException("Not supported service name"); diff --git a/Renci.SshNet/NetConfClient.cs b/Renci.SshNet/NetConfClient.cs new file mode 100644 index 0000000..e859b7a --- /dev/null +++ b/Renci.SshNet/NetConfClient.cs @@ -0,0 +1,221 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using Renci.SshNet.Sftp; +using System.Text; +using Renci.SshNet.Common; +using System.Globalization; +using System.Threading; +using Renci.SshNet.NetConf; +using System.Xml; +using System.Diagnostics.CodeAnalysis; + +namespace Renci.SshNet +{ + // TODO: Please help with documentation here, as I don't know the details, specially for the methods not documented. + /// + /// Contains operation for working with NetConf server. + /// + public partial class NetConfClient : BaseClient + { + /// + /// Holds SftpSession instance that used to communicate to the SFTP server + /// + private NetConfSession _netConfSession; + + private bool _disposeConnectionInfo; + + /// + /// Gets or sets the operation timeout. + /// + /// The operation timeout. + public TimeSpan OperationTimeout { get; set; } + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// is null. + public NetConfClient(ConnectionInfo connectionInfo) + : base(connectionInfo) + { + this.AutomaticMessageIdHandling = true; + this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid, or is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public NetConfClient(string host, int port, string username, string password) + : this(new PasswordConnectionInfo(host, port, username, password)) + { + this._disposeConnectionInfo = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid, or is null or contains whitespace characters. + public NetConfClient(string host, string username, string password) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid, -or- is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public NetConfClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles)) + { + this._disposeConnectionInfo = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid, -or- is null or contains whitespace characters. + public NetConfClient(string host, string username, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) + { + } + + #endregion + + /// + /// Gets NetConf server capabilities. + /// + /// Client is not connected. + public XmlDocument ServerCapabilities + { + get + { + return this._netConfSession.ServerCapabilities; + } + } + + /// + /// Gets NetConf client capabilities. + /// + /// Client is not connected. + public XmlDocument ClientCapabilities + { + get + { + return this._netConfSession.ClientCapabilities; + } + } + + /// + /// Gets or sets a value indicating whether [automatic message id handling]. + /// + /// + /// true if [automatic message id handling]; otherwise, false. + /// + public bool AutomaticMessageIdHandling { get; set; } + + /// + /// Sends the receive RPC. + /// + /// The RPC. + /// Reply message to RPC request + /// Client is not connected. + public XmlDocument SendReceiveRpc(XmlDocument rpc) + { + return this._netConfSession.SendReceiveRpc(rpc, this.AutomaticMessageIdHandling); + } + + /// + /// Sends the receive RPC. + /// + /// The XML. + /// Reply message to RPC request + public XmlDocument SendReceiveRpc(string xml) + { + var rpc = new XmlDocument(); + rpc.LoadXml(xml); + return SendReceiveRpc(rpc); + } + + /// + /// Sends the close RPC. + /// + /// Reply message to closing RPC request + /// Client is not connected. + public XmlDocument SendCloseRpc() + { + XmlDocument rpc = new XmlDocument(); + + rpc.LoadXml(""); + + return this._netConfSession.SendReceiveRpc(rpc, this.AutomaticMessageIdHandling); + } + + /// + /// Called when client is connected to the server. + /// + protected override void OnConnected() + { + base.OnConnected(); + + this._netConfSession = new NetConfSession(this.Session, this.OperationTimeout); + + this._netConfSession.Connect(); + } + + /// + /// Called when client is disconnecting from the server. + /// + protected override void OnDisconnecting() + { + base.OnDisconnecting(); + + this._netConfSession.Disconnect(); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected override void Dispose(bool disposing) + { + if (this._netConfSession != null) + { + this._netConfSession.Dispose(); + this._netConfSession = null; + } + + base.Dispose(disposing); + + if (this._disposeConnectionInfo) + ((IDisposable)this.ConnectionInfo).Dispose(); + + } + } +} diff --git a/Renci.SshNet/Netconf/NetConfSession.cs b/Renci.SshNet/Netconf/NetConfSession.cs new file mode 100644 index 0000000..9e3fd7c --- /dev/null +++ b/Renci.SshNet/Netconf/NetConfSession.cs @@ -0,0 +1,212 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using System.Diagnostics; +using System.Collections.Generic; +using System.Globalization; +using Renci.SshNet.Sftp.Responses; +using Renci.SshNet.Sftp.Requests; +using Renci.SshNet.Sftp; +using Renci.SshNet.Messages.Connection; +using System.Xml; +using System.Text.RegularExpressions; + +namespace Renci.SshNet.NetConf +{ + internal class NetConfSession : SubsystemSession + { + private StringBuilder _data = new StringBuilder(); + + private bool _usingFramingProtocol = false; + + private const string _prompt = "]]>]]>"; + + private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(false); + + private EventWaitHandle _rpcReplyReceived = new AutoResetEvent(false); + + private StringBuilder _rpcReply = new StringBuilder(); + + private int _messageId = 0; + + /// + /// Gets NetConf server capabilities. + /// + public XmlDocument ServerCapabilities { get; private set; } + + /// + /// Gets NetConf client capabilities. + /// + public XmlDocument ClientCapabilities { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The session. + /// The operation timeout. + public NetConfSession(Session session, TimeSpan operationTimeout) + : base(session, "netconf", operationTimeout, Encoding.UTF8) + { + ClientCapabilities = new XmlDocument(); + ClientCapabilities.LoadXml("" + + "" + + "" + + "" + + "urn:ietf:params:netconf:base:1.0" + + "" + + "" + + ""); + + } + + public XmlDocument SendReceiveRpc(XmlDocument rpc, bool automaticMessageIdHandling) + { + this._data.Clear(); + + XmlNamespaceManager ns = null; + if (automaticMessageIdHandling) + { + _messageId++; + ns = new XmlNamespaceManager(rpc.NameTable); + ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0"); + rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value = _messageId.ToString(); + } + _rpcReply = new StringBuilder(); + _rpcReplyReceived.Reset(); + var reply = new XmlDocument(); + if (_usingFramingProtocol) + { + StringBuilder command = new StringBuilder(rpc.InnerXml.Length + 10); + command.AppendFormat("\n#{0}\n", rpc.InnerXml.Length); + command.Append(rpc.InnerXml); + command.Append("\n##\n"); + this.SendData(Encoding.UTF8.GetBytes(command.ToString())); + + this.WaitHandle(this._rpcReplyReceived, this._operationTimeout); + reply.LoadXml(_rpcReply.ToString()); + } + else + { + this.SendData(Encoding.UTF8.GetBytes(rpc.InnerXml + _prompt)); + this.WaitHandle(this._rpcReplyReceived, this._operationTimeout); + reply.LoadXml(_rpcReply.ToString()); + } + if (automaticMessageIdHandling) + { + //string reply_id = rpc.SelectSingleNode("/nc:rpc-reply/@message-id", ns).Value; + string reply_id = rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value; + if (reply_id != _messageId.ToString()) + { + throw new NetConfServerException("The rpc message id does not match the rpc-reply message id."); + } + } + return reply; + } + + protected override void OnChannelOpen() + { + this._data.Clear(); + + string message = string.Format("{0}{1}", this.ClientCapabilities.InnerXml, _prompt); + + this.SendData(Encoding.UTF8.GetBytes(message)); + + this.WaitHandle(this._serverCapabilitiesConfirmed, this._operationTimeout); + } + + protected override void OnDataReceived(uint dataTypeCode, byte[] data) + { + string chunk = Encoding.UTF8.GetString(data); + + if (this.ServerCapabilities == null) // This must be server capabilities, old protocol + { + this._data.Append(chunk); + + if (!chunk.Contains(_prompt)) + { + return; + } + try + { + chunk = this._data.ToString(); + this._data.Clear(); + + this.ServerCapabilities = new XmlDocument(); + this.ServerCapabilities.LoadXml(chunk.Replace(_prompt, "")); + } + catch (XmlException e) + { + throw new NetConfServerException("Server capabilities received are not well formed XML", e); + } + + XmlNamespaceManager ns = new XmlNamespaceManager(this.ServerCapabilities.NameTable); + + ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0"); + + this._usingFramingProtocol = (this.ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[text()='urn:ietf:params:netconf:base:1.1']", ns) != null); + + this._serverCapabilitiesConfirmed.Set(); + } + else if (this._usingFramingProtocol) + { + int position = 0; + + for (; ; ) + { + Match match = Regex.Match(chunk.Substring(position), @"\n#(?\d+)\n"); + if (!match.Success) + { + break; + } + int fractionLength = Convert.ToInt32(match.Groups["length"].Value); + this._rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength); + position += match.Index + match.Length + fractionLength; + } + if (Regex.IsMatch(chunk.Substring(position), @"\n##\n")) + { + this._rpcReplyReceived.Set(); + } + } + else // Old protocol + { + this._data.Append(chunk); + + if (!chunk.Contains(_prompt)) + { + return; + //throw new NetConfServerException("Server XML message does not end with the prompt " + _prompt); + } + + chunk = this._data.ToString(); + this._data.Clear(); + + this._rpcReply.Append(chunk.Replace(_prompt, "")); + this._rpcReplyReceived.Set(); + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + if (this._serverCapabilitiesConfirmed != null) + { + this._serverCapabilitiesConfirmed.Dispose(); + this._serverCapabilitiesConfirmed = null; + } + + if (this._rpcReplyReceived != null) + { + this._rpcReplyReceived.Dispose(); + this._rpcReplyReceived = null; + } + } + } + } +} diff --git a/Renci.SshNet/NoneAuthenticationMethod.cs b/Renci.SshNet/NoneAuthenticationMethod.cs new file mode 100644 index 0000000..2c9b858 --- /dev/null +++ b/Renci.SshNet/NoneAuthenticationMethod.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Messages; + +namespace Renci.SshNet +{ + /// + /// Provides functionality for "none" authentication method + /// + public class NoneAuthenticationMethod : AuthenticationMethod, IDisposable + { + private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; + + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + + /// + /// Gets connection name + /// + public override string Name + { + get { return "none"; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The username. + /// is whitespace or null. + public NoneAuthenticationMethod(string username) + : base(username) + { + + } + + /// + /// Authenticates the specified session. + /// + /// The session. + /// + /// Result of authentication process. + /// + /// is null. + public override AuthenticationResult Authenticate(Session session) + { + if (session == null) + throw new ArgumentNullException("session"); + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; + + session.SendMessage(new RequestMessageNone(ServiceName.Connection, this.Username)); + + session.WaitHandle(this._authenticationCompleted); + + session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; + + return this._authenticationResult; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this._authenticationResult = AuthenticationResult.Success; + + this._authenticationCompleted.Set(); + } + + private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + { + if (e.Message.PartialSuccess) + this._authenticationResult = AuthenticationResult.PartialSuccess; + else + this._authenticationResult = AuthenticationResult.Failure; + + // Copy allowed authentication methods + this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); + + this._authenticationCompleted.Set(); + } + + #region IDisposable Members + + private bool isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this.isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + if (this._authenticationCompleted != null) + { + this._authenticationCompleted.Dispose(); + this._authenticationCompleted = null; + } + } + + // Note disposing has been done. + isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~NoneAuthenticationMethod() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + } +} diff --git a/Renci.SshNet/NoneConnectionInfo.cs b/Renci.SshNet/NoneConnectionInfo.cs index 0b8b5bc..efd8420 100644 --- a/Renci.SshNet/NoneConnectionInfo.cs +++ b/Renci.SshNet/NoneConnectionInfo.cs @@ -30,25 +30,114 @@ public override string Name return "none"; } } + + /// + /// Initializes a new instance of the class. + /// + /// The host. + /// The username. + public NoneConnectionInfo(string host, string username) + : this(host, 22, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) + { + + } + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + /// The host. + /// The port. + /// The username. + public NoneConnectionInfo(string host, int port, string username) + : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) + { + + } + + /// + /// Initializes a new instance of the class. /// /// Connection host. + /// Connection port. /// Connection username. - public NoneConnectionInfo(string host, string username) - : this(host, 22, username) + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public NoneConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public NoneConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) { + } + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public NoneConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, 22, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) + { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public NoneConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + public NoneConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) + : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) + { + } + + /// + /// Initializes a new instance of the class. /// /// Connection host. /// Connection port. /// Connection username. - public NoneConnectionInfo(string host, int port, string username) - : base(host, port, username) + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + public NoneConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) + : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) { } diff --git a/Renci.SshNet/PasswordAuthenticationMethod.NET40.cs b/Renci.SshNet/PasswordAuthenticationMethod.NET40.cs new file mode 100644 index 0000000..c5b3d5f --- /dev/null +++ b/Renci.SshNet/PasswordAuthenticationMethod.NET40.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Messages; +using System.Threading.Tasks; + +namespace Renci.SshNet +{ + public partial class PasswordAuthenticationMethod : AuthenticationMethod + { + /// is null. + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem((o) => { action(); }); + } + } +} diff --git a/Renci.SshNet/PasswordAuthenticationMethod.cs b/Renci.SshNet/PasswordAuthenticationMethod.cs new file mode 100644 index 0000000..5c77057 --- /dev/null +++ b/Renci.SshNet/PasswordAuthenticationMethod.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Messages; + +namespace Renci.SshNet +{ + /// + /// Provides functionality to perform password authentication. + /// + public partial class PasswordAuthenticationMethod : AuthenticationMethod, IDisposable + { + private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; + + private Session _session; + + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + + private Exception _exception; + + private RequestMessage _requestMessage; + + private byte[] _password; + + /// + /// Gets authentication method name + /// + public override string Name + { + get { return this._requestMessage.MethodName; } + } + + /// + /// Occurs when user's password has expired and needs to be changed. + /// + public event EventHandler PasswordExpired; + + /// + /// Initializes a new instance of the class. + /// + /// The username. + /// The password. + /// is whitespace or null. + /// is null. + public PasswordAuthenticationMethod(string username, string password) + : this(username, Encoding.UTF8.GetBytes(password)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The username. + /// The password. + /// is whitespace or null. + /// is null. + public PasswordAuthenticationMethod(string username, byte[] password) + : base(username) + { + if (password == null) + throw new ArgumentNullException("password"); + + this._password = password; + + this._requestMessage = new RequestMessagePassword(ServiceName.Connection, this.Username, this._password); + } + + /// + /// Authenticates the specified session. + /// + /// The session to authenticate. + /// + /// Result of authentication process. + /// + /// is null. + public override AuthenticationResult Authenticate(Session session) + { + if (session == null) + throw new ArgumentNullException("session"); + + this._session = session; + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; + session.MessageReceived += Session_MessageReceived; + + session.RegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ"); + + session.SendMessage(this._requestMessage); + + session.WaitHandle(this._authenticationCompleted); + + session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; + session.MessageReceived -= Session_MessageReceived; + + + if (this._exception != null) + { + throw this._exception; + } + + return this._authenticationResult; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this._authenticationResult = AuthenticationResult.Success; + + this._authenticationCompleted.Set(); + } + + private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + { + if (e.Message.PartialSuccess) + this._authenticationResult = AuthenticationResult.PartialSuccess; + else + this._authenticationResult = AuthenticationResult.Failure; + + // Copy allowed authentication methods + this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); + + this._authenticationCompleted.Set(); + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + if (e.Message is PasswordChangeRequiredMessage) + { + this._session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ"); + + this.ExecuteThread(() => + { + try + { + var eventArgs = new AuthenticationPasswordChangeEventArgs(this.Username); + + // Raise an event to allow user to supply a new password + if (this.PasswordExpired != null) + { + this.PasswordExpired(this, eventArgs); + } + + // Send new authentication request with new password + this._session.SendMessage(new RequestMessagePassword(ServiceName.Connection, this.Username, this._password, eventArgs.NewPassword)); + } + catch (Exception exp) + { + this._exception = exp; + this._authenticationCompleted.Set(); + } + }); + } + } + + partial void ExecuteThread(Action action); + + #region IDisposable Members + + private bool isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this.isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + if (this._authenticationCompleted != null) + { + this._authenticationCompleted.Dispose(); + this._authenticationCompleted = null; + } + } + + // Note disposing has been done. + isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~PasswordAuthenticationMethod() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + } +} diff --git a/Renci.SshNet/PasswordConnectionInfo.cs b/Renci.SshNet/PasswordConnectionInfo.cs index e174825..15dc40c 100644 --- a/Renci.SshNet/PasswordConnectionInfo.cs +++ b/Renci.SshNet/PasswordConnectionInfo.cs @@ -2,50 +2,42 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading; -using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Common; -using Renci.SshNet.Messages; namespace Renci.SshNet { /// /// Provides connection information when password authentication method is used /// - public partial class PasswordConnectionInfo : ConnectionInfo, IDisposable + /// + /// + /// + /// + /// + public class PasswordConnectionInfo : ConnectionInfo, IDisposable { - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - - private Exception _exception; - - private RequestMessage _requestMessage; - - private string _password; - - /// - /// Gets connection name - /// - public override string Name - { - get - { - return this._requestMessage.MethodName; - } - } - /// /// Occurs when user's password has expired and needs to be changed. /// + /// + /// + /// public event EventHandler PasswordExpired; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Connection host. /// Connection username. /// Connection password. + /// + /// + /// + /// is null. + /// is invalid, or is null or contains whitespace characters. + /// is not within and . public PasswordConnectionInfo(string host, string username, string password) - : this(host, 22, username, password) + : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password)) { } @@ -59,94 +51,219 @@ public PasswordConnectionInfo(string host, string username, string password) /// Connection password. /// is null. /// is invalid, or is null or contains whitespace characters. - /// is not within and . + /// is not within and . public PasswordConnectionInfo(string host, int port, string username, string password) - : base(host, port, username) + : this(host, port, username, Encoding.UTF8.GetBytes(password), ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) { - if (password == null) - throw new ArgumentNullException("password"); + } - this._password = password; - this._requestMessage = new RequestMessagePassword(ServiceName.Connection, this.Username, password); + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// The port. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public PasswordConnectionInfo(string host, int port, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, port, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, string.Empty, string.Empty) + { } /// - /// Called when connection needs to be authenticated. + /// Initializes a new instance of the class. /// - protected override void OnAuthenticate() + /// Connection host. + /// The port. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public PasswordConnectionInfo(string host, int port, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, port, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) { - this.Session.RegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ"); + } - this.SendMessage(this._requestMessage); + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public PasswordConnectionInfo(string host, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, string.Empty, string.Empty) + { + } - this.WaitHandle(this._authenticationCompleted); + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public PasswordConnectionInfo(string host, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) + { + } - if (this._exception != null) - { - throw this._exception; - } + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + public PasswordConnectionInfo(string host, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) + : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) + { } /// - /// Handles the UserAuthenticationSuccessMessageReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs e) + /// Connection host. + /// Connection username. + /// Connection password. + public PasswordConnectionInfo(string host, string username, byte[] password) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password) { - base.Session_UserAuthenticationSuccessMessageReceived(sender, e); - this._authenticationCompleted.Set(); + } /// - /// Handles the UserAuthenticationFailureReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + /// Connection host. + /// Connection port. + /// Connection username. + /// Connection password. + /// is null. + /// is invalid, or is null or contains whitespace characters. + /// is not within and . + public PasswordConnectionInfo(string host, int port, string username, byte[] password) + : this(host, port, username, password, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) { - base.Session_UserAuthenticationFailureReceived(sender, e); - this._authenticationCompleted.Set(); } /// - /// Handles the MessageReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_MessageReceived(object sender, MessageEventArgs e) + /// Connection host. + /// The port. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public PasswordConnectionInfo(string host, int port, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, port, username, password, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) { - base.Session_MessageReceived(sender, e); + } - if (e.Message is PasswordChangeRequiredMessage) - { - this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ"); + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// The port. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public PasswordConnectionInfo(string host, int port, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, port, username, password, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) + { + } - this.ExecuteThread(() => - { - try - { - var eventArgs = new AuthenticationPasswordChangeEventArgs(this.Username); + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + public PasswordConnectionInfo(string host, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) + { + } - // Raise an event to allow user to supply a new password - if (this.PasswordExpired != null) - { - this.PasswordExpired(this, eventArgs); - } + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + public PasswordConnectionInfo(string host, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) + { + } - // Send new authentication request with new password - this.SendMessage(new RequestMessagePassword(ServiceName.Connection, this.Username, this._password, eventArgs.NewPassword)); - } - catch (Exception exp) - { - this._exception = exp; - this._authenticationCompleted.Set(); - } - }); + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + public PasswordConnectionInfo(string host, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// The port. + /// Connection username. + /// Connection password. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + public PasswordConnectionInfo(string host, int port, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) + : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PasswordAuthenticationMethod(username, password)) + { + foreach (var authenticationMethod in this.AuthenticationMethods.OfType()) + { + authenticationMethod.PasswordExpired += AuthenticationMethod_PasswordExpired; } } - partial void ExecuteThread(Action action); + private void AuthenticationMethod_PasswordExpired(object sender, AuthenticationPasswordChangeEventArgs e) + { + if (this.PasswordExpired != null) + { + this.PasswordExpired(sender, e); + } + } #region IDisposable Members @@ -176,10 +293,12 @@ protected virtual void Dispose(bool disposing) if (disposing) { // Dispose managed resources. - if (this._authenticationCompleted != null) + if (this.AuthenticationMethods != null) { - this._authenticationCompleted.Dispose(); - this._authenticationCompleted = null; + foreach (var authenticationMethods in this.AuthenticationMethods.OfType()) + { + authenticationMethods.Dispose(); + } } } diff --git a/Renci.SshNet/PrivateKeyAuthenticationMethod.cs b/Renci.SshNet/PrivateKeyAuthenticationMethod.cs new file mode 100644 index 0000000..589409e --- /dev/null +++ b/Renci.SshNet/PrivateKeyAuthenticationMethod.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Collections.ObjectModel; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Messages; +using Renci.SshNet.Common; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Provides functionality to perform private key authentication. + /// + public class PrivateKeyAuthenticationMethod : AuthenticationMethod, IDisposable + { + private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; + + private EventWaitHandle _authenticationCompleted = new ManualResetEvent(false); + + private bool _isSignatureRequired; + + /// + /// Gets authentication method name + /// + public override string Name + { + get { return "publickey"; } + } + + /// + /// Gets the key files used for authentication. + /// + public ICollection KeyFiles { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The username. + /// The key files. + /// is whitespace or null. + public PrivateKeyAuthenticationMethod(string username, params PrivateKeyFile[] keyFiles) + : base(username) + { + if (keyFiles == null) + throw new ArgumentNullException("keyFiles"); + + this.KeyFiles = new Collection(keyFiles); + } + + /// + /// Authenticates the specified session. + /// + /// The session to authenticate. + /// + /// Result of authentication process. + /// + public override AuthenticationResult Authenticate(Session session) + { + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; + session.MessageReceived += Session_MessageReceived; + + session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK"); + + foreach (var keyFile in this.KeyFiles) + { + this._authenticationCompleted.Reset(); + this._isSignatureRequired = false; + + var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data); + + if (this.KeyFiles.Count < 2) + { + // If only one key file provided then send signature for very first request + var signatureData = new SignatureData(message, session.SessionId).GetBytes(); + + message.Signature = keyFile.HostKey.Sign(signatureData); + } + + // Send public key authentication request + session.SendMessage(message); + + session.WaitHandle(this._authenticationCompleted); + + if (this._isSignatureRequired) + { + this._authenticationCompleted.Reset(); + + var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data); + + var signatureData = new SignatureData(message, session.SessionId).GetBytes(); + + signatureMessage.Signature = keyFile.HostKey.Sign(signatureData); + + // Send public key authentication request with signature + session.SendMessage(signatureMessage); + } + + session.WaitHandle(this._authenticationCompleted); + + if (this._authenticationResult == AuthenticationResult.Success) + { + break; + } + } + + session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; + session.MessageReceived -= Session_MessageReceived; + + session.UnRegisterMessage("SSH_MSG_USERAUTH_PK_OK"); + + return this._authenticationResult; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this._authenticationResult = AuthenticationResult.Success; + + this._authenticationCompleted.Set(); + } + + private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + { + if (e.Message.PartialSuccess) + this._authenticationResult = AuthenticationResult.PartialSuccess; + else + this._authenticationResult = AuthenticationResult.Failure; + + // Copy allowed authentication methods + this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); + + this._authenticationCompleted.Set(); + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + var publicKeyMessage = e.Message as PublicKeyMessage; + if (publicKeyMessage != null) + { + this._isSignatureRequired = true; + this._authenticationCompleted.Set(); + } + } + + #region IDisposable Members + + private bool isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this.isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + if (this._authenticationCompleted != null) + { + this._authenticationCompleted.Dispose(); + this._authenticationCompleted = null; + } + } + + // Note disposing has been done. + isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~PrivateKeyAuthenticationMethod() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + private class SignatureData : SshData + { + private RequestMessagePublicKey _message; + + private byte[] _sessionId; + + public SignatureData(RequestMessagePublicKey message, byte[] sessionId) + { + this._message = message; + this._sessionId = sessionId; + } + + protected override void LoadData() + { + throw new System.NotImplementedException(); + } + + protected override void SaveData() + { + this.WriteBinaryString(this._sessionId); + this.Write((byte)50); + this.Write(this._message.Username); + this.WriteAscii("ssh-connection"); + this.WriteAscii("publickey"); + this.Write((byte)1); + this.WriteAscii(this._message.PublicKeyAlgorithmName); + this.WriteBinaryString(this._message.PublicKeyData); + } + } + + } +} diff --git a/Renci.SshNet/PrivateKeyConnectionInfo.cs b/Renci.SshNet/PrivateKeyConnectionInfo.cs index 0a1d727..008e974 100644 --- a/Renci.SshNet/PrivateKeyConnectionInfo.cs +++ b/Renci.SshNet/PrivateKeyConnectionInfo.cs @@ -3,33 +3,17 @@ using System.Linq; using System.Text; using System.Collections.ObjectModel; -using System.Threading; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; namespace Renci.SshNet { /// /// Provides connection information when private key authentication method is used /// + /// + /// + /// public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable { - private EventWaitHandle _publicKeyRequestMessageResponseWaitHandle = new ManualResetEvent(false); - - private bool _isSignatureRequired; - - /// - /// Gets connection name - /// - public override string Name - { - get - { - return "publickey"; - } - } - /// /// Gets the key files used for authentication. /// @@ -41,8 +25,12 @@ public override string Name /// Connection host. /// Connection username. /// Connection key files. + /// + /// + /// + /// public PrivateKeyConnectionInfo(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, 22, username, keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) { } @@ -55,139 +43,107 @@ public PrivateKeyConnectionInfo(string host, string username, params PrivateKeyF /// Connection username. /// Connection key files. public PrivateKeyConnectionInfo(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : base(host, port, username) + : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) { - this.KeyFiles = new Collection(keyFiles); } /// - /// Called when connection needs to be authenticated. + /// Initializes a new instance of the class. /// - protected override void OnAuthenticate() + /// Connection host. + /// The port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The key files. + public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles) + : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) { - if (this.KeyFiles == null) - return; - - this.Session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK"); - - foreach (var keyFile in this.KeyFiles) - { - this._publicKeyRequestMessageResponseWaitHandle.Reset(); - this._isSignatureRequired = false; - - var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data); - - if (this.KeyFiles.Count < 2) - { - // If only one key file provided then send signature for very first request - var signatureData = new SignatureData(message, this.Session.SessionId).GetBytes(); - - message.Signature = keyFile.HostKey.Sign(signatureData); - } - - // Send public key authentication request - this.SendMessage(message); - - this.WaitHandle(this._publicKeyRequestMessageResponseWaitHandle); - - if (this._isSignatureRequired) - { - this._publicKeyRequestMessageResponseWaitHandle.Reset(); - - var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data); - - var signatureData = new SignatureData(message, this.Session.SessionId).GetBytes(); - - signatureMessage.Signature = keyFile.HostKey.Sign(signatureData); - - // Send public key authentication request with signature - this.SendMessage(signatureMessage); - } - - this.WaitHandle(this._publicKeyRequestMessageResponseWaitHandle); - - if (this.IsAuthenticated) - { - break; - } - } - - this.Session.UnRegisterMessage("SSH_MSG_USERAUTH_PK_OK"); } /// - /// Handles the UserAuthenticationSuccessMessageReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs e) + /// Connection host. + /// The port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The key files. + public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles) + : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) { - base.Session_UserAuthenticationSuccessMessageReceived(sender, e); - - this._publicKeyRequestMessageResponseWaitHandle.Set(); } /// - /// Handles the UserAuthenticationFailureReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The key files. + public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) { - base.Session_UserAuthenticationFailureReceived(sender, e); - this._publicKeyRequestMessageResponseWaitHandle.Set(); } /// - /// Handles the MessageReceived event of the session. + /// Initializes a new instance of the class. /// - /// The source of the event. - /// The event data. - protected override void Session_MessageReceived(object sender, MessageEventArgs e) + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The key files. + public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) { - base.Session_MessageReceived(sender, e); - - var publicKeyMessage = e.Message as PublicKeyMessage; - if (publicKeyMessage != null) - { - this._isSignatureRequired = true; - this._publicKeyRequestMessageResponseWaitHandle.Set(); - } } - private class SignatureData : SshData + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + /// The key files. + public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, keyFiles) { - private RequestMessagePublicKey _message; - - private byte[] _sessionId; - - public SignatureData(RequestMessagePublicKey message, byte[] sessionId) - { - this._message = message; - this._sessionId = sessionId; - } - - protected override void LoadData() - { - throw new System.NotImplementedException(); - } + } - protected override void SaveData() - { - this.WriteBinaryString(this._sessionId); - this.Write((byte)50); - this.Write(this._message.Username); - this.Write("ssh-connection"); - this.Write("publickey"); - this.Write((byte)1); - this.Write(this._message.PublicKeyAlgorithmName); - this.WriteBinaryString(this._message.PublicKeyData); - } + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// The port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + /// The key files. + public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles) + : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(username, keyFiles)) + { + this.KeyFiles = new Collection(keyFiles); } #region IDisposable Members - private bool _isDisposed = false; + private bool isDisposed = false; /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. @@ -206,28 +162,30 @@ public void Dispose() protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. - if (!this._isDisposed) + if (!this.isDisposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. - if (this._publicKeyRequestMessageResponseWaitHandle != null) + if (this.AuthenticationMethods != null) { - this._publicKeyRequestMessageResponseWaitHandle.Dispose(); - this._publicKeyRequestMessageResponseWaitHandle = null; + foreach (var authenticationMethods in this.AuthenticationMethods.OfType()) + { + authenticationMethods.Dispose(); + } } } // Note disposing has been done. - _isDisposed = true; + isDisposed = true; } } /// /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. + /// is reclaimed by garbage collection. /// ~PrivateKeyConnectionInfo() { @@ -238,6 +196,5 @@ protected virtual void Dispose(bool disposing) } #endregion - } } diff --git a/Renci.SshNet/PrivateKeyFile.cs b/Renci.SshNet/PrivateKeyFile.cs index 3668c46..d18f7dc 100644 --- a/Renci.SshNet/PrivateKeyFile.cs +++ b/Renci.SshNet/PrivateKeyFile.cs @@ -13,34 +13,30 @@ using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Security.Cryptography.Ciphers.Paddings; +using System.Diagnostics.CodeAnalysis; namespace Renci.SshNet { /// - /// old private key information/ + /// Represents private key information /// - public class PrivateKeyFile + /// + /// + /// + public class PrivateKeyFile : IDisposable { #if SILVERLIGHT - private static Regex _privateKeyRegex = new Regex(@"^-----BEGIN (?\w+) PRIVATE KEY-----\r?\n(Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)?(?([a-zA-Z0-9/+=]{1,64}\r?\n)+)-----END \k PRIVATE KEY-----.*", RegexOptions.Multiline); + private static Regex _privateKeyRegex = new Regex(@"^-+ *BEGIN (?\w+( \w+)*) PRIVATE KEY *-+\r?\n(Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k PRIVATE KEY *-+", RegexOptions.Multiline); #else - private static readonly Regex _privateKeyRegex = new Regex(@"^-----BEGIN (?\w+) PRIVATE KEY-----\r?\n(Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)?(?([a-zA-Z0-9/+=]{1,72}\r?\n)+)-----END \k PRIVATE KEY-----.*", RegexOptions.Compiled | RegexOptions.Multiline); + private static Regex _privateKeyRegex = new Regex(@"^-+ *BEGIN (?\w+( \w+)*) PRIVATE KEY *-+\r?\n(Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k PRIVATE KEY *-+", RegexOptions.Compiled | RegexOptions.Multiline); #endif + private Key _key; + /// /// Gets the host key. /// - public HostAlgorithm HostKey; - - public static bool IsValid(string privateKey) - { - using (var sr = new StreamReader(privateKey)) - { - return _privateKeyRegex.Match(sr.ReadToEnd()).Success; - } - } - - + public HostAlgorithm HostKey { get; private set; } /// /// Initializes a new instance of the class. @@ -60,9 +56,9 @@ public PrivateKeyFile(Stream privateKey) public PrivateKeyFile(string fileName) { if (string.IsNullOrEmpty(fileName)) - throw new ArgumentException("FileName not valid"); + throw new ArgumentNullException("fileName"); - using (var keyFile = File.Open(fileName, FileMode.Open,FileAccess.Read,FileShare.Read)) + using (var keyFile = File.Open(fileName, FileMode.Open)) { this.Open(keyFile, null); } @@ -78,14 +74,21 @@ public PrivateKeyFile(string fileName) public PrivateKeyFile(string fileName, string passPhrase) { if (string.IsNullOrEmpty(fileName)) - throw new ArgumentException("FileName not valid"); + throw new ArgumentNullException("fileName"); - using (var keyFile = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var keyFile = File.Open(fileName, FileMode.Open)) { this.Open(keyFile, passPhrase); } } + public static bool IsValid(string privateKey) + { + using (var sr = new StreamReader(privateKey)) + { + return _privateKeyRegex.Match(sr.ReadToEnd()).Success; + } + } /// /// Initializes a new instance of the class. @@ -103,6 +106,7 @@ public PrivateKeyFile(Stream privateKey, string passPhrase) /// /// The private key. /// The pass phrase. + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "this._key disposed in Dispose(bool) method.")] private void Open(Stream privateKey, string passPhrase) { if (privateKey == null) @@ -128,7 +132,7 @@ private void Open(Stream privateKey, string passPhrase) var binaryData = System.Convert.FromBase64String(data); - byte[] decryptedData; + byte[] decryptedData = null; if (!string.IsNullOrEmpty(cipherName) && !string.IsNullOrEmpty(salt)) { @@ -143,24 +147,23 @@ private void Open(Stream privateKey, string passPhrase) switch (cipherName) { case "DES-EDE3-CBC": - cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); + cipher = new CipherInfo(192, (key, iv) => { return new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); break; case "DES-EDE3-CFB": - cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), new PKCS7Padding())); + cipher = new CipherInfo(192, (key, iv) => { return new TripleDesCipher(key, new CfbCipherMode(iv), new PKCS7Padding()); }); break; case "DES-CBC": - cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); + cipher = new CipherInfo(64, (key, iv) => { return new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); + break; + case "AES-128-CBC": + cipher = new CipherInfo(128, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); + break; + case "AES-192-CBC": + cipher = new CipherInfo(192, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); + break; + case "AES-256-CBC": + cipher = new CipherInfo(256, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); break; - // TODO: Implement more private key ciphers - //case "AES-128-CBC": - // cipher = new CipherInfo(128, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); - // break; - //case "AES-192-CBC": - // cipher = new CipherInfo(192, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); - // break; - //case "AES-256-CBC": - // cipher = new CipherInfo(256, (key, iv) => { return new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()); }); - // break; default: throw new SshException(string.Format(CultureInfo.CurrentCulture, "Private key cipher \"{0}\" is not supported.", cipherName)); } @@ -175,16 +178,112 @@ private void Open(Stream privateKey, string passPhrase) switch (keyName) { case "RSA": - this.HostKey = new KeyHostAlgorithm("ssh-rsa", new RsaKey(decryptedData.ToArray())); + this._key = new RsaKey(decryptedData.ToArray()); + this.HostKey = new KeyHostAlgorithm("ssh-rsa", this._key); break; case "DSA": - this.HostKey = new KeyHostAlgorithm("ssh-dss", new DsaKey(decryptedData.ToArray())); + this._key = new DsaKey(decryptedData.ToArray()); + this.HostKey = new KeyHostAlgorithm("ssh-dss", this._key); + break; + case "SSH2 ENCRYPTED": + var reader = new SshDataReader(decryptedData); + var magicNumber = reader.ReadUInt32(); + if (magicNumber != 0x3f6ff9eb) + { + throw new SshException("Invalid SSH2 private key."); + } + + var totalLength = reader.ReadUInt32(); // Read total bytes length including magic number + var keyType = reader.ReadString(); + var ssh2CipherName = reader.ReadString(); + var blobSize = (int)reader.ReadUInt32(); + + byte[] keyData = null; + if (ssh2CipherName == "none") + { + keyData = reader.ReadBytes(blobSize); + } + //else if (ssh2CipherName == "3des-cbc") + //{ + // var key = GetCipherKey(passPhrase, 192 / 8); + // var ssh2Сipher = new TripleDesCipher(key, null, null); + // keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize)); + //} + else + { + throw new SshException(string.Format("Cipher method '{0}' is not supported.", cipherName)); + } + + // TODO: Create two specific data types to avoid using SshDataReader class + + reader = new SshDataReader(keyData); + + var decryptedLength = reader.ReadUInt32(); + + if (decryptedLength + 4 != blobSize) + throw new SshException("Invalid passphrase."); + + if (keyType == "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}") + { + var exponent = reader.ReadBigIntWithBits();//e + var d = reader.ReadBigIntWithBits();//d + var modulus = reader.ReadBigIntWithBits();//n + var inverseQ = reader.ReadBigIntWithBits();//u + var q = reader.ReadBigIntWithBits();//p + var p = reader.ReadBigIntWithBits();//q + this._key = new RsaKey(modulus, exponent, d, p, q, inverseQ); + this.HostKey = new KeyHostAlgorithm("ssh-rsa", this._key); + } + else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}") + { + var zero = reader.ReadUInt32(); + if (zero != 0) + { + throw new SshException("Invalid private key"); + } + var p = reader.ReadBigIntWithBits(); + var g = reader.ReadBigIntWithBits(); + var q = reader.ReadBigIntWithBits(); + var y = reader.ReadBigIntWithBits(); + var x = reader.ReadBigIntWithBits(); + this._key = new DsaKey(p, q, g, y, x); + this.HostKey = new KeyHostAlgorithm("ssh-dss", this._key); + } + else + { + throw new NotSupportedException(string.Format("Key type '{0}' is not supported.", keyType)); + } break; default: throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Key '{0}' is not supported.", keyName)); } } + private static byte[] GetCipherKey(string passphrase, int length) + { + List cipherKey = new List(); + + using (var md5 = new MD5Hash()) + { + byte[] passwordBytes = Encoding.UTF8.GetBytes(passphrase); + + var hash = md5.ComputeHash(passwordBytes.ToArray()).AsEnumerable(); + + cipherKey.AddRange(hash); + + while (cipherKey.Count < length) + { + hash = passwordBytes.Concat(hash); + + hash = md5.ComputeHash(hash.ToArray()); + + cipherKey.AddRange(hash); + } + } + + return cipherKey.Take(length).ToArray(); + } + /// /// Decrypts encrypted private key file data. /// @@ -192,9 +291,10 @@ private void Open(Stream privateKey, string passPhrase) /// Encrypted data. /// Decryption pass phrase. /// Decryption binary salt. - /// - /// , , or is null. - public static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string passPhrase, byte[] binarySalt) + /// Decrypted byte array. + /// cipherInfo + /// , , or is null. + private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string passPhrase, byte[] binarySalt) { if (cipherInfo == null) throw new ArgumentNullException("cipherInfo"); @@ -211,7 +311,8 @@ public static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string { var passwordBytes = Encoding.UTF8.GetBytes(passPhrase); - var initVector = passwordBytes.Concat(binarySalt); + // Use 8 bytes binary salkt + var initVector = passwordBytes.Concat(binarySalt.Take(8)); var hash = md5.ComputeHash(initVector.ToArray()).AsEnumerable(); @@ -231,5 +332,107 @@ public static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string return cipher.Decrypt(cipherData); } + + #region IDisposable Members + + private bool _isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._key != null) + { + ((IDisposable)this._key).Dispose(); + this._key = null; + } + } + + // Note disposing has been done. + _isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~PrivateKeyFile() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + private class SshDataReader : SshData + { + public SshDataReader(byte[] data) + { + this.LoadBytes(data); + } + + public new UInt32 ReadUInt32() + { + return base.ReadUInt32(); + } + + public new string ReadString() + { + return base.ReadString(); + } + + public new byte[] ReadBytes(int length) + { + return base.ReadBytes(length); + } + + /// + /// Reads next mpint data type from internal buffer where length specified in bits. + /// + /// mpint read. + public BigInteger ReadBigIntWithBits() + { + var length = (int)base.ReadUInt32(); + + length = (int)(length + 7) / 8; + + var data = base.ReadBytes(length); + var bytesArray = new byte[data.Length + 1]; + Buffer.BlockCopy(data, 0, bytesArray, 1, data.Length); + + return new BigInteger(bytesArray.Reverse().ToArray()); + } + + protected override void LoadData() + { + } + + protected override void SaveData() + { + } + } } } diff --git a/Renci.SshNet/Properties/AssemblyInfo.cs b/Renci.SshNet/Properties/AssemblyInfo.cs index 92f8c16..60b05c0 100644 --- a/Renci.SshNet/Properties/AssemblyInfo.cs +++ b/Renci.SshNet/Properties/AssemblyInfo.cs @@ -8,10 +8,9 @@ // associated with an assembly. [assembly: AssemblyTitle("SshNet")] [assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Renci")] [assembly: AssemblyProduct("SSH.NET")] -[assembly: AssemblyCopyright("Copyright © Renci 2011")] +[assembly: AssemblyCopyright("Copyright © Renci 2013")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,7 +32,13 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("2013.4.7")] +[assembly: AssemblyFileVersion("2013.4.7")] +[assembly: AssemblyInformationalVersion("2013.4.7")] [assembly: CLSCompliant(false)] [assembly: InternalsVisibleTo("Renci.SshNet.Tests")] +#if DEBUG +[assembly: AssemblyConfiguration("Debug")] +#else +[assembly: AssemblyConfiguration("Release")] +#endif \ No newline at end of file diff --git a/Renci.SshNet/ProxyTypes.cs b/Renci.SshNet/ProxyTypes.cs new file mode 100644 index 0000000..30e37a3 --- /dev/null +++ b/Renci.SshNet/ProxyTypes.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet +{ + /// + /// Specifies the type of proxy client will use to connect to server. + /// + public enum ProxyTypes + { + /// No proxy server. + None, + /// A SOCKS4 proxy server. + Socks4, + /// A SOCKS5 proxy server. + Socks5, + /// A HTTP proxy server. + Http, + } +} diff --git a/Renci.SshNet/Renci.SshNet.csproj b/Renci.SshNet/Renci.SshNet.csproj index 35a509f..ac0f129 100644 --- a/Renci.SshNet/Renci.SshNet.csproj +++ b/Renci.SshNet/Renci.SshNet.csproj @@ -12,61 +12,39 @@ Renci.SshNet v4.0 512 - - - - - - - - true full false bin\Debug\ - TRACE;DEBUG;NOTIMEOUT + DEBUG;TRACE prompt - 4 + 0 bin\Debug\Renci.SshNet.xml - x86 pdbonly true bin\Release\ - - + TRACE prompt 4 bin\Release\Renci.SshNet.xml - x86 - - - true - bin\DebugNoTimeout\ - TRACE;DEBUG - full - AnyCPU - bin\Debug\Renci.SshClient.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - false - ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - false - bin\DebugNoTimeout\Renci.SshNet.xml - + + + Code + + + Code + @@ -77,6 +55,7 @@ + @@ -90,19 +69,36 @@ + + + Code + + + + Code - - Code - + Code + + Code + + + Code + + + Code + + + Code + Code @@ -110,6 +106,7 @@ + @@ -121,12 +118,74 @@ + + Code + + - + + Code + + + + + Code + + + + + + Code + + + Code + + + Code + + + + + + + Code + + + + Code + + + Code + + + + Code + + + Code + + + + Code + + + + + + Code + + + + Code + + + Code + + Code @@ -136,8 +195,6 @@ - - @@ -169,6 +226,7 @@ + @@ -201,7 +259,6 @@ - @@ -221,10 +278,8 @@ - - - - + + @@ -246,6 +301,7 @@ + Code @@ -319,13 +375,19 @@ - - - + + + + + + + Code + + @@ -342,7 +404,11 @@ - + + Code + + + @@ -358,6 +424,9 @@ + + Code + @@ -366,45 +435,27 @@ Code + Code + + Code + - - - - - - - - - - - - - - - - - - - - - - + - + - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 2bbd11f04cec10d27dfaa9cd6b9cd6d688d582d7 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 20 Jul 2014 22:17:09 +0200 Subject: [PATCH 011/134] ssh buffer up, timing --- Renci.SshNet/Sftp/SftpFileStream.cs | 6 +++--- Sshfs/Sshfs/MainForm.cs | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Renci.SshNet/Sftp/SftpFileStream.cs b/Renci.SshNet/Sftp/SftpFileStream.cs index f489200..c493d7a 100644 --- a/Renci.SshNet/Sftp/SftpFileStream.cs +++ b/Renci.SshNet/Sftp/SftpFileStream.cs @@ -189,13 +189,13 @@ public virtual byte[] Handle public TimeSpan Timeout { get; set; } internal SftpFileStream(SftpSession session, string path, FileMode mode) - : this(session, path, mode, FileAccess.ReadWrite, 4096, false) + : this(session, path, mode, FileAccess.ReadWrite, 65536, false) { // Nothing to do here. } internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access) - : this(session, path, mode, access, 4096, false) + : this(session, path, mode, access, 65536, false) { // Nothing to do here. } @@ -216,7 +216,7 @@ internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAcc { throw new ArgumentNullException("path"); } - if (bufferSize <= 0 || bufferSize > 16 * 1024) + if (bufferSize <= 0 || bufferSize > 64 * 1024) { throw new ArgumentOutOfRangeException("bufferSize"); } diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index 8c3b172..bbfb2f4 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -15,6 +15,7 @@ using Microsoft.Win32; using Renci.SshNet; using Sshfs.Properties; +using System.Threading; #endregion @@ -422,6 +423,7 @@ private void MainForm_Shown(object sender, EventArgs e) foreach (var drive in _drives.Where(d => d.Automount)) { MountDrive(drive); + Thread.Sleep(100);//pokus } if (_drives.Count != 0 && _drives[0].Automount) muButton.Enabled = false; From caa2dbb999cdac319d6926cf69082902dcfbf10f Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 20 Jul 2014 23:01:09 +0200 Subject: [PATCH 012/134] mounting tuning automount is mounting in sequence --- Sshfs/Sshfs/MainForm.cs | 7 ++++++- Sshfs/Sshfs/SftpDrive.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index bbfb2f4..dafd24a 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -402,6 +402,7 @@ private void muButton_Click(object sender, EventArgs e) else { drive.Unmount(); + muButton.Enabled = false; } } @@ -423,7 +424,11 @@ private void MainForm_Shown(object sender, EventArgs e) foreach (var drive in _drives.Where(d => d.Automount)) { MountDrive(drive); - Thread.Sleep(100);//pokus + //no parallel mounting on startup fix: + while (drive.Status == DriveStatus.Mounting) + { + Thread.Sleep(100); + } } if (_drives.Count != 0 && _drives[0].Automount) muButton.Enabled = false; diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index 251c805..0113ce0 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -204,7 +204,7 @@ public void Mount() Directory.GetLogicalDrives().All( drive => drive[0] != Letter)) { - Thread.Sleep(1000); + Thread.Sleep(200); } }, _mountCancel.Token); From 2235b8f64e00c85fe33be75f359be9da585364cb Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 21 Jul 2014 18:43:37 +0200 Subject: [PATCH 013/134] swap to .net 4.5 --- DokanNet/DokanNet.csproj | 8 ++++++-- Renci.SshNet/Renci.SshNet.csproj | 5 ++++- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 4 ++-- Sshfs/Sshfs/Properties/Resources.Designer.cs | 17 ++++++++++++++++- Sshfs/Sshfs/Properties/Settings.Designer.cs | 4 ++-- Sshfs/Sshfs/Sshfs.csproj | 14 ++++++++------ Sshfs/Sshfs/app.config | 6 +++--- 7 files changed, 41 insertions(+), 17 deletions(-) diff --git a/DokanNet/DokanNet.csproj b/DokanNet/DokanNet.csproj index 85ded30..5952eda 100644 --- a/DokanNet/DokanNet.csproj +++ b/DokanNet/DokanNet.csproj @@ -13,7 +13,7 @@ 3.5 - v4.0 + v4.5 @@ -52,6 +52,7 @@ AllRules.ruleset false x86 + false pdbonly @@ -63,6 +64,7 @@ 4 AllRules.ruleset AnyCPU + false @@ -83,6 +85,7 @@ ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false false + false bin\x86\Release\ @@ -100,6 +103,7 @@ false ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false + false true @@ -152,7 +156,7 @@ - + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {2F5F8C90-0BD1-424F-997C-7BC6280919D1} + Library + Properties + Renci.SshNet + Renci.SshNet + v4.0 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + TRACE;DEBUG + prompt + 4 + bin\Debug\Renci.SshNet.xml + + + none + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Renci.SshNet.xml + 1591 + + + true + + + ..\Renci.SshNet.snk + + + + + + + + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + Code + + + + + + + + Code + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + Code + + + + + + + Code + + + + + Code + + + + + + Code + + + Code + + + Code + + + + + + + + Code + + + + + Code + + + Code + + + + Code + + + Code + + + + Code + + + + + + Code + + + + Code + + + Code + + + + + Code + + + Code + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + Code + + + + + + + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + Code + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + + Code + + + Code + + + Code + + + Code + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + Code + + + + + + + Code + + + + + Code + + + Code + + + + + Code + + + + + Code + + + + + + + + + Renci.SshNet.snk + + + + + + + + \ No newline at end of file diff --git a/Renci.SshNet/Renci.SshNet.csproj.vspscc b/Renci.SshNet/Renci.SshNet.csproj.vspscc index b6d3289..feffdec 100644 --- a/Renci.SshNet/Renci.SshNet.csproj.vspscc +++ b/Renci.SshNet/Renci.SshNet.csproj.vspscc @@ -1,10 +1,10 @@ -"" -{ -"FILE_VERSION" = "9237" -"ENLISTMENT_CHOICE" = "NEVER" -"PROJECT_FILE_RELATIVE_PATH" = "" -"NUMBER_OF_EXCLUDED_FILES" = "0" -"ORIGINAL_PROJECT_FILE_PATH" = "" -"NUMBER_OF_NESTED_PROJECTS" = "0" -"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" -} +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Renci.SshNet/ScpClient.NET.cs b/Renci.SshNet/ScpClient.NET.cs index 6967894..294f515 100644 --- a/Renci.SshNet/ScpClient.NET.cs +++ b/Renci.SshNet/ScpClient.NET.cs @@ -1,312 +1,303 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Channels; -using System.IO; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Connection; -using System.Text.RegularExpressions; -using System.Threading; -using System.Diagnostics; - -namespace Renci.SshNet -{ - /// - /// Provides SCP client functionality. - /// - public partial class ScpClient - { - private static Regex _rootPath = new Regex(@"^(/|[A-Z][:])", RegexOptions.Compiled | RegexOptions.IgnoreCase); - - /// - /// Uploads the specified file to the remote host. - /// - /// The file system info. - /// The path. - /// fileSystemInfo - /// path - /// - /// or is null. - public void Upload(FileInfo fileInfo, string path) - { - if (fileInfo == null) - throw new ArgumentNullException("fileSystemInfo"); - - if (string.IsNullOrEmpty(path)) - throw new ArgumentException("path"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateChannel()) - { - channel.DataReceived += delegate(object sender, Common.ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -t \"{0}\"", path)); - this.CheckReturnCode(input); - - this.InternalUpload(channel, input, fileInfo, fileInfo.Name); - - channel.Close(); - } - } - - /// - /// Uploads the specified directory to the remote host. - /// - /// The directory info. - /// The path. - /// fileSystemInfo - /// path - public void Upload(DirectoryInfo directoryInfo, string path) - { - if (directoryInfo == null) - throw new ArgumentNullException("fileSystemInfo"); - - if (string.IsNullOrEmpty(path)) - throw new ArgumentException("path"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateChannel()) - { - channel.DataReceived += delegate(object sender, Common.ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -rt \"{0}\"", path)); - this.CheckReturnCode(input); - - this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc); - this.SendData(channel, string.Format("D0755 0 {0}\n", Path.GetFileName(path))); - this.CheckReturnCode(input); - - this.InternalUpload(channel, input, directoryInfo as DirectoryInfo, directoryInfo.Name); - - this.SendData(channel, "E\n"); - this.CheckReturnCode(input); - - channel.Close(); - } - } - - /// - /// Downloads the specified file from the remote host to local file. - /// - /// Remote host file name. - /// Local file information. - /// or is null. - public void Download(string filename, FileInfo fileInfo) - { - if (fileInfo == null) - throw new ArgumentNullException("fileInfo"); - - if (string.IsNullOrEmpty(filename)) - throw new ArgumentException("filename"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateChannel()) - { - channel.DataReceived += delegate(object sender, Common.ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -pf \"{0}\"", filename)); - this.SendConfirmation(channel); // Send reply - - this.InternalDownload(channel, input, fileInfo); - - channel.Close(); - } - } - - /// - /// Downloads the specified directory from the remote host to local directory. - /// - /// Remote host directory name. - /// Local directory information. - /// or is null. - public void Download(string directoryName, DirectoryInfo directoryInfo) - { - if (directoryInfo == null) - throw new ArgumentNullException("directoryInfo"); - - if (string.IsNullOrEmpty(directoryName)) - throw new ArgumentException("directoryName"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateChannel()) - { - channel.DataReceived += delegate(object sender, Common.ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -prf \"{0}\"", directoryName)); - this.SendConfirmation(channel); // Send reply - - this.InternalDownload(channel, input, directoryInfo); - - channel.Close(); - } - } - - private void InternalUpload(ChannelSession channel, Stream input, FileInfo fileInfo, string filename) - { - this.InternalSetTimestamp(channel, input, fileInfo.LastWriteTimeUtc, fileInfo.LastAccessTimeUtc); - using (var source = fileInfo.OpenRead()) - { - this.InternalUpload(channel, input, source, filename); - } - } - - private void InternalUpload(ChannelSession channel, Stream input, DirectoryInfo directoryInfo, string directoryName) - { - // Upload files - var files = directoryInfo.GetFiles(); - foreach (var file in files) - { - this.InternalUpload(channel, input, file, file.Name); - } - - // Upload directories - var directories = directoryInfo.GetDirectories(); - foreach (var directory in directories) - { - this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc); - this.SendData(channel, string.Format("D0755 0 {0}\n", directory.Name)); - this.CheckReturnCode(input); - - this.InternalUpload(channel, input, directory, directory.Name); - - this.SendData(channel, "E\n"); - this.CheckReturnCode(input); - } - } - - private void InternalDownload(ChannelSession channel, Stream input, FileSystemInfo fileSystemInfo) - { - DateTime modifiedTime = DateTime.Now; - DateTime accessedTime = DateTime.Now; - - var startDirectoryFullName = fileSystemInfo.FullName; - var currentDirectoryFullName = startDirectoryFullName; - var directoryCounter = 0; - - while (true) - { - var message = ReadString(input); - - if (message == "E") - { - this.SendConfirmation(channel); // Send reply - - directoryCounter--; - - currentDirectoryFullName = new DirectoryInfo(currentDirectoryFullName).Parent.FullName; - - if (directoryCounter == 0) - break; - continue; - } - - var match = _directoryInfoRe.Match(message); - if (match.Success) - { - this.SendConfirmation(channel); // Send reply - - // Read directory - var mode = long.Parse(match.Result("${mode}")); - var filename = match.Result("${filename}"); - - DirectoryInfo newDirectoryInfo; - if (directoryCounter > 0) - { - newDirectoryInfo = Directory.CreateDirectory(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, filename)); - newDirectoryInfo.LastAccessTime = accessedTime; - newDirectoryInfo.LastWriteTime = modifiedTime; - } - else - { - // Dont create directory for first level - newDirectoryInfo = fileSystemInfo as DirectoryInfo; - } - - directoryCounter++; - - currentDirectoryFullName = newDirectoryInfo.FullName; - continue; - } - - match = _fileInfoRe.Match(message); - if (match.Success) - { - // Read file - this.SendConfirmation(channel); // Send reply - - var mode = match.Result("${mode}"); - var length = long.Parse(match.Result("${length}")); - var fileName = match.Result("${filename}"); - - var fileInfo = fileSystemInfo as FileInfo; - - if (fileInfo == null) - fileInfo = new FileInfo(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, fileName)); - - using (var output = fileInfo.OpenWrite()) - { - this.InternalDownload(channel, input, output, fileName, length); - } - - fileInfo.LastAccessTime = accessedTime; - fileInfo.LastWriteTime = modifiedTime; - - if (directoryCounter == 0) - break; - continue; - } - - match = _timestampRe.Match(message); - if (match.Success) - { - // Read timestamp - this.SendConfirmation(channel); // Send reply - - var mtime = long.Parse(match.Result("${mtime}")); - var atime = long.Parse(match.Result("${atime}")); - - var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - modifiedTime = zeroTime.AddSeconds(mtime); - accessedTime = zeroTime.AddSeconds(atime); - continue; - } - - this.SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message)); - } - } - - partial void SendData(ChannelSession channel, string command) - { - channel.SendData(System.Text.Encoding.Default.GetBytes(command)); - } - } -} +using System; +using Renci.SshNet.Channels; +using System.IO; +using Renci.SshNet.Common; +using System.Text.RegularExpressions; + +namespace Renci.SshNet +{ + /// + /// Provides SCP client functionality. + /// + public partial class ScpClient + { + private static readonly Regex _directoryInfoRe = new Regex(@"D(?\d{4}) (?\d+) (?.+)"); + private static readonly Regex _timestampRe = new Regex(@"T(?\d+) 0 (?\d+) 0"); + + /// + /// Uploads the specified file to the remote host. + /// + /// The file system info. + /// The path. + /// is null. + /// is null or empty. + public void Upload(FileInfo fileInfo, string path) + { + if (fileInfo == null) + throw new ArgumentNullException("fileInfo"); + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("path"); + + using (var input = new PipeStream()) + using (var channel = this.Session.CreateClientChannel()) + { + channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) + { + input.Write(e.Data, 0, e.Data.Length); + input.Flush(); + }; + + channel.Open(); + + // Send channel command request + channel.SendExecRequest(string.Format("scp -t \"{0}\"", path)); + this.CheckReturnCode(input); + + this.InternalUpload(channel, input, fileInfo, fileInfo.Name); + + channel.Close(); + } + } + + /// + /// Uploads the specified directory to the remote host. + /// + /// The directory info. + /// The path. + /// fileSystemInfo + /// is null or empty. + public void Upload(DirectoryInfo directoryInfo, string path) + { + if (directoryInfo == null) + throw new ArgumentNullException("directoryInfo"); + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("path"); + + using (var input = new PipeStream()) + using (var channel = this.Session.CreateClientChannel()) + { + channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) + { + input.Write(e.Data, 0, e.Data.Length); + input.Flush(); + }; + + channel.Open(); + + // Send channel command request + channel.SendExecRequest(string.Format("scp -rt \"{0}\"", path)); + this.CheckReturnCode(input); + + this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc); + this.SendData(channel, string.Format("D0755 0 {0}\n", Path.GetFileName(path))); + this.CheckReturnCode(input); + + this.InternalUpload(channel, input, directoryInfo); + + this.SendData(channel, "E\n"); + this.CheckReturnCode(input); + + channel.Close(); + } + } + + /// + /// Downloads the specified file from the remote host to local file. + /// + /// Remote host file name. + /// Local file information. + /// is null. + /// is null or empty. + public void Download(string filename, FileInfo fileInfo) + { + if (string.IsNullOrEmpty(filename)) + throw new ArgumentException("filename"); + if (fileInfo == null) + throw new ArgumentNullException("fileInfo"); + + using (var input = new PipeStream()) + using (var channel = this.Session.CreateClientChannel()) + { + channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) + { + input.Write(e.Data, 0, e.Data.Length); + input.Flush(); + }; + + channel.Open(); + + // Send channel command request + channel.SendExecRequest(string.Format("scp -pf \"{0}\"", filename)); + this.SendConfirmation(channel); // Send reply + + this.InternalDownload(channel, input, fileInfo); + + channel.Close(); + } + } + + /// + /// Downloads the specified directory from the remote host to local directory. + /// + /// Remote host directory name. + /// Local directory information. + /// is null or empty. + /// is null. + public void Download(string directoryName, DirectoryInfo directoryInfo) + { + if (string.IsNullOrEmpty(directoryName)) + throw new ArgumentException("directoryName"); + if (directoryInfo == null) + throw new ArgumentNullException("directoryInfo"); + + using (var input = new PipeStream()) + using (var channel = this.Session.CreateClientChannel()) + { + channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) + { + input.Write(e.Data, 0, e.Data.Length); + input.Flush(); + }; + + channel.Open(); + + // Send channel command request + channel.SendExecRequest(string.Format("scp -prf \"{0}\"", directoryName)); + this.SendConfirmation(channel); // Send reply + + this.InternalDownload(channel, input, directoryInfo); + + channel.Close(); + } + } + + private void InternalUpload(ChannelSession channel, Stream input, FileInfo fileInfo, string filename) + { + this.InternalSetTimestamp(channel, input, fileInfo.LastWriteTimeUtc, fileInfo.LastAccessTimeUtc); + using (var source = fileInfo.OpenRead()) + { + this.InternalUpload(channel, input, source, filename); + } + } + + private void InternalUpload(ChannelSession channel, Stream input, DirectoryInfo directoryInfo) + { + // Upload files + var files = directoryInfo.GetFiles(); + foreach (var file in files) + { + this.InternalUpload(channel, input, file, file.Name); + } + + // Upload directories + var directories = directoryInfo.GetDirectories(); + foreach (var directory in directories) + { + this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc); + this.SendData(channel, string.Format("D0755 0 {0}\n", directory.Name)); + this.CheckReturnCode(input); + + this.InternalUpload(channel, input, directory); + + this.SendData(channel, "E\n"); + this.CheckReturnCode(input); + } + } + + private void InternalDownload(ChannelSession channel, Stream input, FileSystemInfo fileSystemInfo) + { + DateTime modifiedTime = DateTime.Now; + DateTime accessedTime = DateTime.Now; + + var startDirectoryFullName = fileSystemInfo.FullName; + var currentDirectoryFullName = startDirectoryFullName; + var directoryCounter = 0; + + while (true) + { + var message = ReadString(input); + + if (message == "E") + { + this.SendConfirmation(channel); // Send reply + + directoryCounter--; + + currentDirectoryFullName = new DirectoryInfo(currentDirectoryFullName).Parent.FullName; + + if (directoryCounter == 0) + break; + continue; + } + + var match = _directoryInfoRe.Match(message); + if (match.Success) + { + this.SendConfirmation(channel); // Send reply + + // Read directory + var mode = long.Parse(match.Result("${mode}")); + var filename = match.Result("${filename}"); + + DirectoryInfo newDirectoryInfo; + if (directoryCounter > 0) + { + newDirectoryInfo = Directory.CreateDirectory(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, filename)); + newDirectoryInfo.LastAccessTime = accessedTime; + newDirectoryInfo.LastWriteTime = modifiedTime; + } + else + { + // Dont create directory for first level + newDirectoryInfo = fileSystemInfo as DirectoryInfo; + } + + directoryCounter++; + + currentDirectoryFullName = newDirectoryInfo.FullName; + continue; + } + + match = _fileInfoRe.Match(message); + if (match.Success) + { + // Read file + this.SendConfirmation(channel); // Send reply + + var mode = match.Result("${mode}"); + var length = long.Parse(match.Result("${length}")); + var fileName = match.Result("${filename}"); + + var fileInfo = fileSystemInfo as FileInfo; + + if (fileInfo == null) + fileInfo = new FileInfo(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, fileName)); + + using (var output = fileInfo.OpenWrite()) + { + this.InternalDownload(channel, input, output, fileName, length); + } + + fileInfo.LastAccessTime = accessedTime; + fileInfo.LastWriteTime = modifiedTime; + + if (directoryCounter == 0) + break; + continue; + } + + match = _timestampRe.Match(message); + if (match.Success) + { + // Read timestamp + this.SendConfirmation(channel); // Send reply + + var mtime = long.Parse(match.Result("${mtime}")); + var atime = long.Parse(match.Result("${atime}")); + + var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + modifiedTime = zeroTime.AddSeconds(mtime); + accessedTime = zeroTime.AddSeconds(atime); + continue; + } + + this.SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message)); + } + } + + partial void SendData(ChannelSession channel, string command) + { + channel.SendData(System.Text.Encoding.Default.GetBytes(command)); + } + } +} diff --git a/Renci.SshNet/ScpClient.cs b/Renci.SshNet/ScpClient.cs index 341f61a..b13a584 100644 --- a/Renci.SshNet/ScpClient.cs +++ b/Renci.SshNet/ScpClient.cs @@ -1,421 +1,408 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Channels; -using System.IO; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Connection; -using System.Text.RegularExpressions; -using System.Threading; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Renci.SshNet -{ - /// - /// Provides SCP client functionality. - /// - public partial class ScpClient : BaseClient - { - private static Regex _fileInfoRe = new Regex(@"C(?\d{4}) (?\d+) (?.+)"); - - private static Regex _directoryInfoRe = new Regex(@"D(?\d{4}) (?\d+) (?.+)"); - - private static Regex _timestampRe = new Regex(@"T(?\d+) 0 (?\d+) 0"); - - private static char[] _byteToChar; - - private bool _disposeConnectionInfo; - - /// - /// Gets or sets the operation timeout. - /// - /// The operation timeout. - public TimeSpan OperationTimeout { get; set; } - - /// - /// Gets or sets the size of the buffer. - /// - /// The size of the buffer. - public uint BufferSize { get; set; } - - /// - /// Occurs when downloading file. - /// - public event EventHandler Downloading; - - /// - /// Occurs when uploading file. - /// - public event EventHandler Uploading; - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// is null. - public ScpClient(ConnectionInfo connectionInfo) - : base(connectionInfo) - { - this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); - this.BufferSize = 1024 * 16; - - if (_byteToChar == null) - { - _byteToChar = new char[128]; - var ch = '\0'; - for (int i = 0; i < 128; i++) - { - _byteToChar[i] = ch++; - } - } - - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public ScpClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password)) - { - this._disposeConnectionInfo = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - public ScpClient(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public ScpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles)) - { - this._disposeConnectionInfo = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - public ScpClient(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) - { - } - - #endregion - - /// - /// Uploads the specified stream to the remote host. - /// - /// Stream to upload. - /// Remote host file name. - public void Upload(Stream source, string path) - { - using (var input = new PipeStream()) - using (var channel = this.Session.CreateChannel()) - { - channel.DataReceived += delegate(object sender, Common.ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - var pathParts = path.Split('\\', '/'); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -rt \"{0}\"", pathParts[0])); - this.CheckReturnCode(input); - - // Prepare directory structure - for (int i = 0; i < pathParts.Length - 1; i++) - { - this.InternalSetTimestamp(channel, input, DateTime.UtcNow, DateTime.UtcNow); - this.SendData(channel, string.Format("D0755 0 {0}\n", pathParts[i])); - this.CheckReturnCode(input); - } - - this.InternalUpload(channel, input, source, pathParts.Last()); - - // Finish directory structure - for (int i = 0; i < pathParts.Length - 1; i++) - { - this.SendData(channel, "E\n"); - this.CheckReturnCode(input); - } - - channel.Close(); - } - } - - /// - /// Downloads the specified file from the remote host to the stream. - /// - /// Remote host file name. - /// The stream where to download remote file. - /// is null or contains whitespace characters. - /// is null. - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - public void Download(string filename, Stream destination) - { - if (filename.IsNullOrWhiteSpace()) - throw new ArgumentException("filename"); - - if (destination == null) - throw new ArgumentNullException("destination"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateChannel()) - { - channel.DataReceived += delegate(object sender, Common.ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -f \"{0}\"", filename)); - this.SendConfirmation(channel); // Send reply - - var message = ReadString(input); - var match = _fileInfoRe.Match(message); - - if (match.Success) - { - // Read file - this.SendConfirmation(channel); // Send reply - - var mode = match.Result("${mode}"); - var length = long.Parse(match.Result("${length}")); - var fileName = match.Result("${filename}"); - - this.InternalDownload(channel, input, destination, fileName, length); - } - else - { - this.SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message)); - } - - channel.Close(); - } - } - - private void InternalSetTimestamp(ChannelSession channel, Stream input, DateTime lastWriteTime, DateTime lastAccessime) - { - var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - var modificationSeconds = (long)(lastWriteTime - zeroTime).TotalSeconds; - var accessSeconds = (long)(lastAccessime - zeroTime).TotalSeconds; - this.SendData(channel, string.Format("T{0} 0 {1} 0\n", modificationSeconds, accessSeconds)); - this.CheckReturnCode(input); - } - - private void InternalUpload(ChannelSession channel, Stream input, Stream source, string filename) - { - var length = source.Length; - - this.SendData(channel, string.Format("C0644 {0} {1}\n", length, Path.GetFileName(filename))); - - var buffer = new byte[this.BufferSize]; - - var read = source.Read(buffer, 0, buffer.Length); - - long totalRead = 0; - - while (read > 0) - { - this.SendData(channel, buffer, read); - - totalRead += read; - - this.RaiseUploadingEvent(filename, length, totalRead); - - read = source.Read(buffer, 0, buffer.Length); - } - - this.SendConfirmation(channel); - this.CheckReturnCode(input); - } - - private void InternalDownload(ChannelSession channel, Stream input, Stream output, string filename, long length) - { - var buffer = new byte[Math.Min(length, this.BufferSize)]; - var needToRead = length; - - do - { - var read = input.Read(buffer, 0, (int)Math.Min(needToRead, this.BufferSize)); - - output.Write(buffer, 0, read); - - this.RaiseDownloadingEvent(filename, length, length - needToRead); - - needToRead -= read; - } - while (needToRead > 0); - - output.Flush(); - - // Raise one more time when file downloaded - this.RaiseDownloadingEvent(filename, length, length - needToRead); - - // Send confirmation byte after last data byte was read - this.SendConfirmation(channel); - - this.CheckReturnCode(input); - } - - private void RaiseDownloadingEvent(string filename, long size, long downloaded) - { - if (this.Downloading != null) - { - this.Downloading(this, new ScpDownloadEventArgs(filename, size, downloaded)); - } - } - - private void RaiseUploadingEvent(string filename, long size, long uploaded) - { - if (this.Uploading != null) - { - this.Uploading(this, new ScpUploadEventArgs(filename, size, uploaded)); - } - } - - private void SendConfirmation(ChannelSession channel) - { - this.SendData(channel, new byte[] { 0 }); - } - - private void SendConfirmation(ChannelSession channel, byte errorCode, string message) - { - this.SendData(channel, new byte[] { errorCode }); - this.SendData(channel, string.Format("{0}\n", message)); - } - - /// - /// Checks the return code. - /// - /// The output stream. - private void CheckReturnCode(Stream input) - { - var b = ReadByte(input); - - if (b > 0) - { - var errorText = ReadString(input); - - throw new ScpException(errorText); - } - } - - partial void SendData(ChannelSession channel, string command); - - private void SendData(ChannelSession channel, byte[] buffer, int length) - { - if (length == buffer.Length) - { - channel.SendData(buffer); - } - else - { - channel.SendData(buffer.Take(length).ToArray()); - } - } - - private void SendData(ChannelSession channel, byte[] buffer) - { - channel.SendData(buffer); - } - - private static int ReadByte(Stream stream) - { - var b = stream.ReadByte(); - - while (b < 0) - { - Thread.Sleep(100); - b = stream.ReadByte(); - } - - return b; - } - - private static string ReadString(Stream stream) - { - var hasError = false; - - StringBuilder sb = new StringBuilder(); - - var b = ReadByte(stream); - - if (b == 1 || b == 2) - { - hasError = true; - b = ReadByte(stream); - } - - var ch = _byteToChar[b]; - - while (ch != '\n') - { - sb.Append(ch); - - b = ReadByte(stream); - - ch = _byteToChar[b]; - } - - if (hasError) - throw new ScpException(sb.ToString()); - - return sb.ToString(); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._disposeConnectionInfo) - ((IDisposable)this.ConnectionInfo).Dispose(); - } - } -} +using System; +using System.Linq; +using System.Text; +using Renci.SshNet.Channels; +using System.IO; +using Renci.SshNet.Common; +using System.Text.RegularExpressions; +using System.Threading; +using System.Diagnostics.CodeAnalysis; + +namespace Renci.SshNet +{ + /// + /// Provides SCP client functionality. + /// + public partial class ScpClient : BaseClient + { + private static readonly Regex _fileInfoRe = new Regex(@"C(?\d{4}) (?\d+) (?.+)"); + private static char[] _byteToChar; + + /// + /// Gets or sets the operation timeout. + /// + /// + /// The timeout to wait until an operation completes. The default value is negative + /// one (-1) milliseconds, which indicates an infinite time-out period. + /// + public TimeSpan OperationTimeout { get; set; } + + /// + /// Gets or sets the size of the buffer. + /// + /// + /// The size of the buffer. The default buffer size is 16384 bytes. + /// + public uint BufferSize { get; set; } + + /// + /// Occurs when downloading file. + /// + public event EventHandler Downloading; + + /// + /// Occurs when uploading file. + /// + public event EventHandler Uploading; + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// is null. + public ScpClient(ConnectionInfo connectionInfo) + : this(connectionInfo, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid, or is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public ScpClient(string host, int port, string username, string password) + : this(new PasswordConnectionInfo(host, port, username, password), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid, or is null or contains whitespace characters. + public ScpClient(string host, string username, string password) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid, -or- is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public ScpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid, -or- is null or contains whitespace characters. + public ScpClient(string host, string username, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// Specified whether this instance owns the connection info. + /// is null. + /// + /// If is true, then the + /// connection info will be disposed when this instance is disposed. + /// + private ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo) + : base(connectionInfo, ownsConnectionInfo) + { + this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); + this.BufferSize = 1024 * 16; + + if (_byteToChar == null) + { + _byteToChar = new char[128]; + var ch = '\0'; + for (int i = 0; i < 128; i++) + { + _byteToChar[i] = ch++; + } + } + } + + #endregion + + /// + /// Uploads the specified stream to the remote host. + /// + /// Stream to upload. + /// Remote host file name. + public void Upload(Stream source, string path) + { + using (var input = new PipeStream()) + using (var channel = this.Session.CreateClientChannel()) + { + channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) + { + input.Write(e.Data, 0, e.Data.Length); + input.Flush(); + }; + + channel.Open(); + + int pathEnd = path.LastIndexOfAny(new[] { '\\', '/' }); + if (pathEnd != -1) + { + // split the path from the file + string pathOnly = path.Substring(0, pathEnd); + string fileOnly = path.Substring(pathEnd + 1); + // Send channel command request + channel.SendExecRequest(string.Format("scp -t \"{0}\"", pathOnly)); + this.CheckReturnCode(input); + + path = fileOnly; + } + + this.InternalUpload(channel, input, source, path); + + channel.Close(); + } + } + + /// + /// Downloads the specified file from the remote host to the stream. + /// + /// Remote host file name. + /// The stream where to download remote file. + /// is null or contains whitespace characters. + /// is null. + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + public void Download(string filename, Stream destination) + { + if (filename.IsNullOrWhiteSpace()) + throw new ArgumentException("filename"); + + if (destination == null) + throw new ArgumentNullException("destination"); + + using (var input = new PipeStream()) + using (var channel = this.Session.CreateClientChannel()) + { + channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) + { + input.Write(e.Data, 0, e.Data.Length); + input.Flush(); + }; + + channel.Open(); + + // Send channel command request + channel.SendExecRequest(string.Format("scp -f \"{0}\"", filename)); + this.SendConfirmation(channel); // Send reply + + var message = ReadString(input); + var match = _fileInfoRe.Match(message); + + if (match.Success) + { + // Read file + this.SendConfirmation(channel); // Send reply + + var mode = match.Result("${mode}"); + var length = long.Parse(match.Result("${length}")); + var fileName = match.Result("${filename}"); + + this.InternalDownload(channel, input, destination, fileName, length); + } + else + { + this.SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message)); + } + + channel.Close(); + } + } + + private void InternalSetTimestamp(ChannelSession channel, Stream input, DateTime lastWriteTime, DateTime lastAccessime) + { + var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + var modificationSeconds = (long)(lastWriteTime - zeroTime).TotalSeconds; + var accessSeconds = (long)(lastAccessime - zeroTime).TotalSeconds; + this.SendData(channel, string.Format("T{0} 0 {1} 0\n", modificationSeconds, accessSeconds)); + this.CheckReturnCode(input); + } + + private void InternalUpload(ChannelSession channel, Stream input, Stream source, string filename) + { + var length = source.Length; + + this.SendData(channel, string.Format("C0644 {0} {1}\n", length, Path.GetFileName(filename))); + + var buffer = new byte[this.BufferSize]; + + var read = source.Read(buffer, 0, buffer.Length); + + long totalRead = 0; + + while (read > 0) + { + this.SendData(channel, buffer, read); + + totalRead += read; + + this.RaiseUploadingEvent(filename, length, totalRead); + + read = source.Read(buffer, 0, buffer.Length); + } + + this.SendConfirmation(channel); + this.CheckReturnCode(input); + } + + private void InternalDownload(ChannelSession channel, Stream input, Stream output, string filename, long length) + { + var buffer = new byte[Math.Min(length, this.BufferSize)]; + var needToRead = length; + + do + { + var read = input.Read(buffer, 0, (int)Math.Min(needToRead, this.BufferSize)); + + output.Write(buffer, 0, read); + + this.RaiseDownloadingEvent(filename, length, length - needToRead); + + needToRead -= read; + } + while (needToRead > 0); + + output.Flush(); + + // Raise one more time when file downloaded + this.RaiseDownloadingEvent(filename, length, length - needToRead); + + // Send confirmation byte after last data byte was read + this.SendConfirmation(channel); + + this.CheckReturnCode(input); + } + + private void RaiseDownloadingEvent(string filename, long size, long downloaded) + { + if (this.Downloading != null) + { + this.Downloading(this, new ScpDownloadEventArgs(filename, size, downloaded)); + } + } + + private void RaiseUploadingEvent(string filename, long size, long uploaded) + { + if (this.Uploading != null) + { + this.Uploading(this, new ScpUploadEventArgs(filename, size, uploaded)); + } + } + + private void SendConfirmation(ChannelSession channel) + { + this.SendData(channel, new byte[] { 0 }); + } + + private void SendConfirmation(ChannelSession channel, byte errorCode, string message) + { + this.SendData(channel, new[] { errorCode }); + this.SendData(channel, string.Format("{0}\n", message)); + } + + /// + /// Checks the return code. + /// + /// The output stream. + private void CheckReturnCode(Stream input) + { + var b = ReadByte(input); + + if (b > 0) + { + var errorText = ReadString(input); + + throw new ScpException(errorText); + } + } + + partial void SendData(ChannelSession channel, string command); + + private void SendData(ChannelSession channel, byte[] buffer, int length) + { + if (length == buffer.Length) + { + channel.SendData(buffer); + } + else + { + channel.SendData(buffer.Take(length).ToArray()); + } + } + + private void SendData(ChannelSession channel, byte[] buffer) + { + channel.SendData(buffer); + } + + private static int ReadByte(Stream stream) + { + var b = stream.ReadByte(); + + while (b < 0) + { + Thread.Sleep(100); + b = stream.ReadByte(); + } + + return b; + } + + private static string ReadString(Stream stream) + { + var hasError = false; + + var sb = new StringBuilder(); + + var b = ReadByte(stream); + + if (b == 1 || b == 2) + { + hasError = true; + b = ReadByte(stream); + } + + var ch = _byteToChar[b]; + + while (ch != '\n') + { + sb.Append(ch); + + b = ReadByte(stream); + + ch = _byteToChar[b]; + } + + if (hasError) + throw new ScpException(sb.ToString()); + + return sb.ToString(); + } + } +} diff --git a/Renci.SshNet/Security/Algorithm.cs b/Renci.SshNet/Security/Algorithm.cs index 180c9d0..184a824 100644 --- a/Renci.SshNet/Security/Algorithm.cs +++ b/Renci.SshNet/Security/Algorithm.cs @@ -1,13 +1,13 @@ -namespace Renci.SshNet.Security -{ - /// - /// Represents the abstract base class from which all implementations of algorithms must inherit. - /// - public abstract class Algorithm - { - /// - /// Gets algorithm name. - /// - public abstract string Name { get; } - } -} +namespace Renci.SshNet.Security +{ + /// + /// Represents the abstract base class from which all implementations of algorithms must inherit. + /// + public abstract class Algorithm + { + /// + /// Gets algorithm name. + /// + public abstract string Name { get; } + } +} diff --git a/Renci.SshNet/Security/CertificateHostAlgorithm.cs b/Renci.SshNet/Security/CertificateHostAlgorithm.cs index 57ff0f7..4ef8ba5 100644 --- a/Renci.SshNet/Security/CertificateHostAlgorithm.cs +++ b/Renci.SshNet/Security/CertificateHostAlgorithm.cs @@ -1,55 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security -{ - /// - /// Implements certificate support for host algorithm. - /// - public class CertificateHostAlgorithm : HostAlgorithm - { - /// - /// Gets the host key data. - /// - public override byte[] Data - { - get { throw new NotImplementedException(); } - } - - /// - /// Initializes a new instance of the class. - /// - /// The host key name. - public CertificateHostAlgorithm(string name) - : base(name) - { - - } - - /// - /// Signs the specified data. - /// - /// The data. - /// Signed data. - /// - public override byte[] Sign(byte[] data) - { - throw new NotImplementedException(); - } - - /// - /// Verifies the signature. - /// - /// The data. - /// The signature. - /// True if signature was successfully verified; otherwise false. - /// - public override bool VerifySignature(byte[] data, byte[] signature) - { - throw new NotImplementedException(); - } - - } -} +using System; + +namespace Renci.SshNet.Security +{ + /// + /// Implements certificate support for host algorithm. + /// + public class CertificateHostAlgorithm : HostAlgorithm + { + /// + /// Gets the host key data. + /// + public override byte[] Data + { + get { throw new NotImplementedException(); } + } + + /// + /// Initializes a new instance of the class. + /// + /// The host key name. + public CertificateHostAlgorithm(string name) + : base(name) + { + } + + /// + /// Signs the specified data. + /// + /// The data. + /// Signed data. + /// + public override byte[] Sign(byte[] data) + { + throw new NotImplementedException(); + } + + /// + /// Verifies the signature. + /// + /// The data. + /// The signature. + /// True if signature was successfully verified; otherwise false. + /// + public override bool VerifySignature(byte[] data, byte[] signature) + { + throw new NotImplementedException(); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs b/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs index d7a67d4..e5e70be 100644 --- a/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs +++ b/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs @@ -1,18 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for asymmetric cipher implementations. - /// - public abstract class AsymmetricCipher : Cipher - { - public override byte MinimumSize - { - get { return 0; } - } - } -} +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Base class for asymmetric cipher implementations. + /// + public abstract class AsymmetricCipher : Cipher + { + public override byte MinimumSize + { + get { return 0; } + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/Renci.SshNet/Security/Cryptography/BlockCipher.cs index 43b67df..5e0edb6 100644 --- a/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ b/Renci.SshNet/Security/Cryptography/BlockCipher.cs @@ -1,158 +1,148 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Security.Cryptography.Ciphers; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for block cipher implementations. - /// - public abstract class BlockCipher : SymmetricCipher - { - private CipherMode _mode; - - private CipherPadding _padding; - - /// - /// Gets the size of the block in bytes. - /// - /// - /// The size of the block in bytes. - /// - protected readonly byte _blockSize; - - /// - /// Gets the minimum data size. - /// - /// - /// The minimum data size. - /// - public override byte MinimumSize - { - get { return this.BlockSize; } - } - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - public byte BlockSize - { - get - { - return this._blockSize; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// Size of the block. - /// Cipher mode. - /// Cipher padding. - /// is null. - protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding padding) - : base(key) - { - this._blockSize = blockSize; - this._mode = mode; - this._padding = padding; - - if (this._mode != null) - this._mode.Init(this); - } - - /// - /// Encrypts the specified data. - /// - /// The data. - /// Encrypted data - public override byte[] Encrypt(byte[] data) - { - var output = new byte[data.Length]; - - if (data.Length % this._blockSize > 0) - { - if (this._padding == null) - { - throw new ArgumentException("data"); - } - else - { - data = this._padding.Pad(this._blockSize, data); - } - } - - var writtenBytes = 0; - - for (int i = 0; i < data.Length / this._blockSize; i++) - { - if (this._mode == null) - { - writtenBytes += this.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - } - else - { - writtenBytes += this._mode.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - } - } - - if (writtenBytes < data.Length) - { - throw new InvalidOperationException("Encryption error."); - } - - return output; - } - - /// - /// Decrypts the specified data. - /// - /// The data. - /// Decrypted data - public override byte[] Decrypt(byte[] data) - { - if (data.Length % this._blockSize > 0) - { - { - if (this._padding == null) - { - throw new ArgumentException("data"); - } - else - { - data = this._padding.Pad(this._blockSize, data); - } - } - } - - var output = new byte[data.Length]; - - var writtenBytes = 0; - for (int i = 0; i < data.Length / this._blockSize; i++) - { - if (this._mode == null) - { - writtenBytes += this.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - } - else - { - writtenBytes += this._mode.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - - } - } - - if (writtenBytes < data.Length) - { - throw new InvalidOperationException("Encryption error."); - } - - return output; - } - } -} +using System; +using Renci.SshNet.Security.Cryptography.Ciphers; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Base class for block cipher implementations. + /// + public abstract class BlockCipher : SymmetricCipher + { + private readonly CipherMode _mode; + + private readonly CipherPadding _padding; + + /// + /// Gets the size of the block in bytes. + /// + /// + /// The size of the block in bytes. + /// + protected readonly byte _blockSize; + + /// + /// Gets the minimum data size. + /// + /// + /// The minimum data size. + /// + public override byte MinimumSize + { + get { return this.BlockSize; } + } + + /// + /// Gets the size of the block. + /// + /// + /// The size of the block. + /// + public byte BlockSize + { + get + { + return this._blockSize; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// Size of the block. + /// Cipher mode. + /// Cipher padding. + /// is null. + protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding padding) + : base(key) + { + this._blockSize = blockSize; + this._mode = mode; + this._padding = padding; + + if (this._mode != null) + this._mode.Init(this); + } + + /// + /// Encrypts the specified data. + /// + /// The data. + /// Encrypted data + public override byte[] Encrypt(byte[] data) + { + var output = new byte[data.Length]; + + if (data.Length % this._blockSize > 0) + { + if (this._padding == null) + { + throw new ArgumentException("data"); + } + data = this._padding.Pad(this._blockSize, data); + } + + var writtenBytes = 0; + + for (int i = 0; i < data.Length / this._blockSize; i++) + { + if (this._mode == null) + { + writtenBytes += this.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); + } + else + { + writtenBytes += this._mode.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); + } + } + + if (writtenBytes < data.Length) + { + throw new InvalidOperationException("Encryption error."); + } + + return output; + } + + /// + /// Decrypts the specified data. + /// + /// The data. + /// Decrypted data + public override byte[] Decrypt(byte[] data) + { + if (data.Length % this._blockSize > 0) + { + { + if (this._padding == null) + { + throw new ArgumentException("data"); + } + data = this._padding.Pad(this._blockSize, data); + } + } + + var output = new byte[data.Length]; + + var writtenBytes = 0; + for (int i = 0; i < data.Length / this._blockSize; i++) + { + if (this._mode == null) + { + writtenBytes += this.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); + } + else + { + writtenBytes += this._mode.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); + } + } + + if (writtenBytes < data.Length) + { + throw new InvalidOperationException("Encryption error."); + } + + return output; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Cipher.cs b/Renci.SshNet/Security/Cryptography/Cipher.cs index ed41f0a..433e234 100644 --- a/Renci.SshNet/Security/Cryptography/Cipher.cs +++ b/Renci.SshNet/Security/Cryptography/Cipher.cs @@ -1,247 +1,244 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for cipher implementation. - /// - public abstract class Cipher - { - /// - /// Gets the minimum data size. - /// - /// - /// The minimum data size. - /// - public abstract byte MinimumSize { get; } - - /// - /// Encrypts the specified input. - /// - /// The input. - /// Encrypted data. - public abstract byte[] Encrypt(byte[] input); - - /// - /// Decrypts the specified input. - /// - /// The input. - /// Decrypted data. - public abstract byte[] Decrypt(byte[] input); - - #region Packing functions - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt32ToBigEndian(uint number, byte[] buffer) - { - buffer[0] = (byte)(number >> 24); - buffer[1] = (byte)(number >> 16); - buffer[2] = (byte)(number >> 8); - buffer[3] = (byte)(number); - } - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt32ToBigEndian(uint number, byte[] buffer, int offset) - { - buffer[offset] = (byte)(number >> 24); - buffer[offset + 1] = (byte)(number >> 16); - buffer[offset + 2] = (byte)(number >> 8); - buffer[offset + 3] = (byte)(number); - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static uint BigEndianToUInt32(byte[] buffer) - { - uint n = (uint)buffer[0] << 24; - n |= (uint)buffer[1] << 16; - n |= (uint)buffer[2] << 8; - n |= (uint)buffer[3]; - return n; - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static uint BigEndianToUInt32(byte[] buffer, int offset) - { - uint n = (uint)buffer[offset] << 24; - n |= (uint)buffer[offset + 1] << 16; - n |= (uint)buffer[offset + 2] << 8; - n |= (uint)buffer[offset + 3]; - return n; - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static ulong BigEndianToUInt64(byte[] buffer) - { - uint hi = BigEndianToUInt32(buffer); - uint lo = BigEndianToUInt32(buffer, 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static ulong BigEndianToUInt64(byte[] buffer, int offset) - { - uint hi = BigEndianToUInt32(buffer, offset); - uint lo = BigEndianToUInt32(buffer, offset + 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt64ToBigEndian(ulong number, byte[] buffer) - { - UInt32ToBigEndian((uint)(number >> 32), buffer); - UInt32ToBigEndian((uint)(number), buffer, 4); - } - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt64ToBigEndian(ulong number, byte[] buffer, int offset) - { - UInt32ToBigEndian((uint)(number >> 32), buffer, offset); - UInt32ToBigEndian((uint)(number), buffer, offset + 4); - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt32ToLittleEndian(uint number, byte[] buffer) - { - buffer[0] = (byte)(number); - buffer[1] = (byte)(number >> 8); - buffer[2] = (byte)(number >> 16); - buffer[3] = (byte)(number >> 24); - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt32ToLittleEndian(uint number, byte[] buffer, int offset) - { - buffer[offset] = (byte)(number); - buffer[offset + 1] = (byte)(number >> 8); - buffer[offset + 2] = (byte)(number >> 16); - buffer[offset + 3] = (byte)(number >> 24); - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static uint LittleEndianToUInt32(byte[] buffer) - { - uint n = (uint)buffer[0]; - n |= (uint)buffer[1] << 8; - n |= (uint)buffer[2] << 16; - n |= (uint)buffer[3] << 24; - return n; - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static uint LittleEndianToUInt32(byte[] buffer, int offset) - { - uint n = (uint)buffer[offset]; - n |= (uint)buffer[offset + 1] << 8; - n |= (uint)buffer[offset + 2] << 16; - n |= (uint)buffer[offset + 3] << 24; - return n; - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static ulong LittleEndianToUInt64(byte[] buffer) - { - uint lo = LittleEndianToUInt32(buffer); - uint hi = LittleEndianToUInt32(buffer, 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static ulong LittleEndianToUInt64(byte[] buffer, int offset) - { - uint lo = LittleEndianToUInt32(buffer, offset); - uint hi = LittleEndianToUInt32(buffer, offset + 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt64ToLittleEndian(ulong number, byte[] buffer) - { - UInt32ToLittleEndian((uint)(number), buffer); - UInt32ToLittleEndian((uint)(number >> 32), buffer, 4); - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt64ToLittleEndian(ulong number, byte[] buffer, int offset) - { - UInt32ToLittleEndian((uint)(number), buffer, offset); - UInt32ToLittleEndian((uint)(number >> 32), buffer, offset + 4); - } - - #endregion - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Base class for cipher implementation. + /// + public abstract class Cipher + { + /// + /// Gets the minimum data size. + /// + /// + /// The minimum data size. + /// + public abstract byte MinimumSize { get; } + + /// + /// Encrypts the specified input. + /// + /// The input. + /// Encrypted data. + public abstract byte[] Encrypt(byte[] input); + + /// + /// Decrypts the specified input. + /// + /// The input. + /// Decrypted data. + public abstract byte[] Decrypt(byte[] input); + + #region Packing functions + + /// + /// Populates buffer with big endian number representation. + /// + /// The number to convert. + /// The buffer. + protected static void UInt32ToBigEndian(uint number, byte[] buffer) + { + buffer[0] = (byte)(number >> 24); + buffer[1] = (byte)(number >> 16); + buffer[2] = (byte)(number >> 8); + buffer[3] = (byte)(number); + } + + /// + /// Populates buffer with big endian number representation. + /// + /// The number to convert. + /// The buffer. + /// The buffer offset. + protected static void UInt32ToBigEndian(uint number, byte[] buffer, int offset) + { + buffer[offset] = (byte)(number >> 24); + buffer[offset + 1] = (byte)(number >> 16); + buffer[offset + 2] = (byte)(number >> 8); + buffer[offset + 3] = (byte)(number); + } + + /// + /// Converts big endian bytes into number. + /// + /// The buffer. + /// Converted . + protected static uint BigEndianToUInt32(byte[] buffer) + { + uint n = (uint)buffer[0] << 24; + n |= (uint)buffer[1] << 16; + n |= (uint)buffer[2] << 8; + n |= (uint)buffer[3]; + return n; + } + + /// + /// Converts big endian bytes into number. + /// + /// The buffer. + /// The buffer offset. + /// Converted . + protected static uint BigEndianToUInt32(byte[] buffer, int offset) + { + uint n = (uint)buffer[offset] << 24; + n |= (uint)buffer[offset + 1] << 16; + n |= (uint)buffer[offset + 2] << 8; + n |= (uint)buffer[offset + 3]; + return n; + } + + /// + /// Converts big endian bytes into number. + /// + /// The buffer. + /// Converted . + protected static ulong BigEndianToUInt64(byte[] buffer) + { + uint hi = BigEndianToUInt32(buffer); + uint lo = BigEndianToUInt32(buffer, 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + /// + /// Converts big endian bytes into number. + /// + /// The buffer. + /// The buffer offset. + /// Converted . + protected static ulong BigEndianToUInt64(byte[] buffer, int offset) + { + uint hi = BigEndianToUInt32(buffer, offset); + uint lo = BigEndianToUInt32(buffer, offset + 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + /// + /// Populates buffer with big endian number representation. + /// + /// The number to convert. + /// The buffer. + protected static void UInt64ToBigEndian(ulong number, byte[] buffer) + { + UInt32ToBigEndian((uint)(number >> 32), buffer); + UInt32ToBigEndian((uint)(number), buffer, 4); + } + + /// + /// Populates buffer with big endian number representation. + /// + /// The number to convert. + /// The buffer. + /// The buffer offset. + protected static void UInt64ToBigEndian(ulong number, byte[] buffer, int offset) + { + UInt32ToBigEndian((uint)(number >> 32), buffer, offset); + UInt32ToBigEndian((uint)(number), buffer, offset + 4); + } + + /// + /// Populates buffer with little endian number representation. + /// + /// The number to convert. + /// The buffer. + protected static void UInt32ToLittleEndian(uint number, byte[] buffer) + { + buffer[0] = (byte)(number); + buffer[1] = (byte)(number >> 8); + buffer[2] = (byte)(number >> 16); + buffer[3] = (byte)(number >> 24); + } + + /// + /// Populates buffer with little endian number representation. + /// + /// The number to convert. + /// The buffer. + /// The buffer offset. + protected static void UInt32ToLittleEndian(uint number, byte[] buffer, int offset) + { + buffer[offset] = (byte)(number); + buffer[offset + 1] = (byte)(number >> 8); + buffer[offset + 2] = (byte)(number >> 16); + buffer[offset + 3] = (byte)(number >> 24); + } + + /// + /// Converts little endian bytes into number. + /// + /// The buffer. + /// Converted . + protected static uint LittleEndianToUInt32(byte[] buffer) + { + uint n = (uint)buffer[0]; + n |= (uint)buffer[1] << 8; + n |= (uint)buffer[2] << 16; + n |= (uint)buffer[3] << 24; + return n; + } + + /// + /// Converts little endian bytes into number. + /// + /// The buffer. + /// The buffer offset. + /// Converted . + protected static uint LittleEndianToUInt32(byte[] buffer, int offset) + { + uint n = (uint)buffer[offset]; + n |= (uint)buffer[offset + 1] << 8; + n |= (uint)buffer[offset + 2] << 16; + n |= (uint)buffer[offset + 3] << 24; + return n; + } + + /// + /// Converts little endian bytes into number. + /// + /// The buffer. + /// Converted . + protected static ulong LittleEndianToUInt64(byte[] buffer) + { + uint lo = LittleEndianToUInt32(buffer); + uint hi = LittleEndianToUInt32(buffer, 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + /// + /// Converts little endian bytes into number. + /// + /// The buffer. + /// The buffer offset. + /// Converted . + protected static ulong LittleEndianToUInt64(byte[] buffer, int offset) + { + uint lo = LittleEndianToUInt32(buffer, offset); + uint hi = LittleEndianToUInt32(buffer, offset + 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + /// + /// Populates buffer with little endian number representation. + /// + /// The number to convert. + /// The buffer. + protected static void UInt64ToLittleEndian(ulong number, byte[] buffer) + { + UInt32ToLittleEndian((uint)(number), buffer); + UInt32ToLittleEndian((uint)(number >> 32), buffer, 4); + } + + /// + /// Populates buffer with little endian number representation. + /// + /// The number to convert. + /// The buffer. + /// The buffer offset. + protected static void UInt64ToLittleEndian(ulong number, byte[] buffer, int offset) + { + UInt32ToLittleEndian((uint)(number), buffer, offset); + UInt32ToLittleEndian((uint)(number >> 32), buffer, offset + 4); + } + + #endregion + } +} diff --git a/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs b/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs index 4e267cc..921b12e 100644 --- a/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs +++ b/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs @@ -1,99 +1,90 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Implements digital signature where where asymmetric cipher is used, - /// - public abstract class CipherDigitalSignature : DigitalSignature - { - private AsymmetricCipher _cipher; - - private ObjectIdentifier _oid; - - /// - /// Initializes a new instance of the class. - /// - /// The object identifier. - /// The cipher. - public CipherDigitalSignature(ObjectIdentifier oid, AsymmetricCipher cipher) - { - if (cipher == null) - throw new ArgumentNullException("cipher"); - - this._cipher = cipher; - this._oid = oid; - } - - /// - /// Verifies the signature. - /// - /// The input. - /// The signature. - /// - /// True if signature was successfully verified; otherwise false. - /// - public override bool Verify(byte[] input, byte[] signature) - { - var encryptedSignature = this._cipher.Decrypt(signature); - - var hashData = this.Hash(input); - - var expected = DerEncode(hashData); - - if (expected.SequenceEqual(encryptedSignature)) - return true; - else - return false; - } - - /// - /// Creates the signature. - /// - /// The input. - /// - /// Signed input data. - /// - public override byte[] Sign(byte[] input) - { - // Calculate hash value - var hashData = this.Hash(input); - - // Calculate DER string - var derEncodedHash = DerEncode(hashData); - - return this._cipher.Encrypt(derEncodedHash).TrimLeadingZero().ToArray(); - } - - /// - /// Hashes the specified input. - /// - /// The input. - /// Hashed data. - protected abstract byte[] Hash(byte[] input); - - /// - /// Encodes hash using DER. - /// - /// The hash data. - /// DER Encoded byte array - protected byte[] DerEncode(byte[] hashData) - { - var data = new DerData(); - - var alg = new DerData(); - alg.Write(this._oid); - alg.WriteNull(); - - data.Write(alg); - data.Write(hashData); - - return data.Encode(); - } - } -} +using System; +using System.Linq; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Implements digital signature where where asymmetric cipher is used, + /// + public abstract class CipherDigitalSignature : DigitalSignature + { + private readonly AsymmetricCipher _cipher; + + private readonly ObjectIdentifier _oid; + + /// + /// Initializes a new instance of the class. + /// + /// The object identifier. + /// The cipher. + public CipherDigitalSignature(ObjectIdentifier oid, AsymmetricCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this._cipher = cipher; + this._oid = oid; + } + + /// + /// Verifies the signature. + /// + /// The input. + /// The signature. + /// + /// True if signature was successfully verified; otherwise false. + /// + public override bool Verify(byte[] input, byte[] signature) + { + var encryptedSignature = this._cipher.Decrypt(signature); + var hashData = this.Hash(input); + var expected = DerEncode(hashData); + return expected.SequenceEqual(encryptedSignature); + } + + /// + /// Creates the signature. + /// + /// The input. + /// + /// Signed input data. + /// + public override byte[] Sign(byte[] input) + { + // Calculate hash value + var hashData = this.Hash(input); + + // Calculate DER string + var derEncodedHash = DerEncode(hashData); + + return this._cipher.Encrypt(derEncodedHash).TrimLeadingZero().ToArray(); + } + + /// + /// Hashes the specified input. + /// + /// The input. + /// Hashed data. + protected abstract byte[] Hash(byte[] input); + + /// + /// Encodes hash using DER. + /// + /// The hash data. + /// DER Encoded byte array + protected byte[] DerEncode(byte[] hashData) + { + var data = new DerData(); + + var alg = new DerData(); + alg.Write(this._oid); + alg.WriteNull(); + + data.Write(alg); + data.Write(hashData); + + return data.Encode(); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs index a90e0a9..95b72cd 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs @@ -1,838 +1,835 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// AES cipher implementation. - /// - public sealed class AesCipher : BlockCipher - { - private const uint m1 = 0x80808080; - - private const uint m2 = 0x7f7f7f7f; - - private const uint m3 = 0x0000001b; - - private int _rounds; - - private uint[] _encryptionKey; - - private uint[] _decryptionKey; - - private uint C0, C1, C2, C3; - - #region Static Definition Tables - - private static readonly byte[] S = - { - 99, 124, 119, 123, 242, 107, 111, 197, - 48, 1, 103, 43, 254, 215, 171, 118, - 202, 130, 201, 125, 250, 89, 71, 240, - 173, 212, 162, 175, 156, 164, 114, 192, - 183, 253, 147, 38, 54, 63, 247, 204, - 52, 165, 229, 241, 113, 216, 49, 21, - 4, 199, 35, 195, 24, 150, 5, 154, - 7, 18, 128, 226, 235, 39, 178, 117, - 9, 131, 44, 26, 27, 110, 90, 160, - 82, 59, 214, 179, 41, 227, 47, 132, - 83, 209, 0, 237, 32, 252, 177, 91, - 106, 203, 190, 57, 74, 76, 88, 207, - 208, 239, 170, 251, 67, 77, 51, 133, - 69, 249, 2, 127, 80, 60, 159, 168, - 81, 163, 64, 143, 146, 157, 56, 245, - 188, 182, 218, 33, 16, 255, 243, 210, - 205, 12, 19, 236, 95, 151, 68, 23, - 196, 167, 126, 61, 100, 93, 25, 115, - 96, 129, 79, 220, 34, 42, 144, 136, - 70, 238, 184, 20, 222, 94, 11, 219, - 224, 50, 58, 10, 73, 6, 36, 92, - 194, 211, 172, 98, 145, 149, 228, 121, - 231, 200, 55, 109, 141, 213, 78, 169, - 108, 86, 244, 234, 101, 122, 174, 8, - 186, 120, 37, 46, 28, 166, 180, 198, - 232, 221, 116, 31, 75, 189, 139, 138, - 112, 62, 181, 102, 72, 3, 246, 14, - 97, 53, 87, 185, 134, 193, 29, 158, - 225, 248, 152, 17, 105, 217, 142, 148, - 155, 30, 135, 233, 206, 85, 40, 223, - 140, 161, 137, 13, 191, 230, 66, 104, - 65, 153, 45, 15, 176, 84, 187, 22, - }; - - // The inverse S-box - private static readonly byte[] Si = - { - 82, 9, 106, 213, 48, 54, 165, 56, - 191, 64, 163, 158, 129, 243, 215, 251, - 124, 227, 57, 130, 155, 47, 255, 135, - 52, 142, 67, 68, 196, 222, 233, 203, - 84, 123, 148, 50, 166, 194, 35, 61, - 238, 76, 149, 11, 66, 250, 195, 78, - 8, 46, 161, 102, 40, 217, 36, 178, - 118, 91, 162, 73, 109, 139, 209, 37, - 114, 248, 246, 100, 134, 104, 152, 22, - 212, 164, 92, 204, 93, 101, 182, 146, - 108, 112, 72, 80, 253, 237, 185, 218, - 94, 21, 70, 87, 167, 141, 157, 132, - 144, 216, 171, 0, 140, 188, 211, 10, - 247, 228, 88, 5, 184, 179, 69, 6, - 208, 44, 30, 143, 202, 63, 15, 2, - 193, 175, 189, 3, 1, 19, 138, 107, - 58, 145, 17, 65, 79, 103, 220, 234, - 151, 242, 207, 206, 240, 180, 230, 115, - 150, 172, 116, 34, 231, 173, 53, 133, - 226, 249, 55, 232, 28, 117, 223, 110, - 71, 241, 26, 113, 29, 41, 197, 137, - 111, 183, 98, 14, 170, 24, 190, 27, - 252, 86, 62, 75, 198, 210, 121, 32, - 154, 219, 192, 254, 120, 205, 90, 244, - 31, 221, 168, 51, 136, 7, 199, 49, - 177, 18, 16, 89, 39, 128, 236, 95, - 96, 81, 127, 169, 25, 181, 74, 13, - 45, 229, 122, 159, 147, 201, 156, 239, - 160, 224, 59, 77, 174, 42, 245, 176, - 200, 235, 187, 60, 131, 83, 153, 97, - 23, 43, 4, 126, 186, 119, 214, 38, - 225, 105, 20, 99, 85, 33, 12, 125, - }; - - // vector used in calculating key schedule (powers of x in GF(256)) - private static readonly byte[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 - }; - - #endregion - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize == 256 || keySize == 192 || keySize == 128)) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - /// or is null. - /// or is too short. - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException("inputBuffer"); - - if (outputBuffer == null) - throw new ArgumentNullException("outputBuffer"); - - if ((inputOffset + (32 / 2)) > inputBuffer.Length) - { - throw new IndexOutOfRangeException("input buffer too short"); - } - - if ((outputOffset + (32 / 2)) > outputBuffer.Length) - { - throw new IndexOutOfRangeException("output buffer too short"); - } - - if (this._encryptionKey == null) - { - this._encryptionKey = this.GenerateWorkingKey(true, this.Key); - } - - this.UnPackBlock(inputBuffer, inputOffset); - - this.EncryptBlock(this._encryptionKey); - - this.PackBlock(outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - /// or is null. - /// or is too short. - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException("inputBuffer"); - - if (outputBuffer == null) - throw new ArgumentNullException("outputBuffer"); - - if ((inputOffset + (32 / 2)) > inputBuffer.Length) - { - throw new IndexOutOfRangeException("input buffer too short"); - } - - if ((outputOffset + (32 / 2)) > outputBuffer.Length) - { - throw new IndexOutOfRangeException("output buffer too short"); - } - - if (this._decryptionKey == null) - { - this._decryptionKey = this.GenerateWorkingKey(false, this.Key); - } - - this.UnPackBlock(inputBuffer, inputOffset); - - this.DecryptBlock(this._decryptionKey); - - this.PackBlock(outputBuffer, outputOffset); - - return this.BlockSize; - } - - private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) - { - int KC = key.Length / 4; // key length in words - - if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length)) - throw new ArgumentException("Key length not 128/192/256 bits."); - - _rounds = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes - uint[] W = new uint[(_rounds + 1) * 4]; // 4 words in a block - - // - // copy the key into the round key array - // - - int t = 0; - - for (int i = 0; i < key.Length; t++) - { - W[(t >> 2) * 4 + (t & 3)] = LittleEndianToUInt32(key, i); - i += 4; - } - - // - // while not enough round key material calculated - // calculate new values - // - int k = (_rounds + 1) << 2; - for (int i = KC; (i < k); i++) - { - uint temp = W[((i - 1) >> 2) * 4 + ((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) * 4 + (i & 3)] = W[((i - KC) >> 2) * 4 + ((i - KC) & 3)] ^ temp; - } - - if (!isEncryption) - { - for (int j = 1; j < _rounds; j++) - { - for (int i = 0; i < 4; i++) - { - W[j * 4 + i] = InvMcol(W[j * 4 + i]); - } - } - } - - return W; - } - - private uint Shift(uint r, int shift) - { - return (r >> shift) | (r << (32 - shift)); - } - - private uint FFmulX(uint x) - { - return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); - } - - private uint InvMcol(uint x) - { - uint f2 = FFmulX(x); - uint f4 = FFmulX(f2); - uint f8 = FFmulX(f4); - uint f9 = x ^ f8; - - return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); - } - - private uint SubWord(uint x) - { - return (uint)S[x & 255] - | (((uint)S[(x >> 8) & 255]) << 8) - | (((uint)S[(x >> 16) & 255]) << 16) - | (((uint)S[(x >> 24) & 255]) << 24); - } - - private void UnPackBlock(byte[] bytes, int off) - { - C0 = LittleEndianToUInt32(bytes, off); - C1 = LittleEndianToUInt32(bytes, off + 4); - C2 = LittleEndianToUInt32(bytes, off + 8); - C3 = LittleEndianToUInt32(bytes, off + 12); - } - - private void PackBlock(byte[] bytes, int off) - { - UInt32ToLittleEndian(C0, bytes, off); - UInt32ToLittleEndian(C1, bytes, off + 4); - UInt32ToLittleEndian(C2, bytes, off + 8); - UInt32ToLittleEndian(C3, bytes, off + 12); - } - - private void EncryptBlock(uint[] KW) - { - int r; - uint r0, r1, r2, r3; - - C0 ^= KW[0 * 4 + 0]; - C1 ^= KW[0 * 4 + 1]; - C2 ^= KW[0 * 4 + 2]; - C3 ^= KW[0 * 4 + 3]; - - for (r = 1; r < _rounds - 1; ) - { - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 3]; - C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[r * 4 + 0]; - C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[r * 4 + 1]; - C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[r * 4 + 2]; - C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[r++ * 4 + 3]; - } - - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 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 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[r * 4 + 0]; - C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[r * 4 + 1]; - C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[r * 4 + 2]; - C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[r * 4 + 3]; - } - - private void DecryptBlock(uint[] KW) - { - int r; - uint r0, r1, r2, r3; - - C0 ^= KW[_rounds * 4 + 0]; - C1 ^= KW[_rounds * 4 + 1]; - C2 ^= KW[_rounds * 4 + 2]; - C3 ^= KW[_rounds * 4 + 3]; - - for (r = _rounds - 1; r > 1; ) - { - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r-- * 4 + 3]; - C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[r * 4 + 0]; - C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[r * 4 + 1]; - C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[r * 4 + 2]; - C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[r-- * 4 + 3]; - } - - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r * 4 + 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 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[0 * 4 + 0]; - C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[0 * 4 + 1]; - C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[0 * 4 + 2]; - C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[0 * 4 + 3]; - } - } -} +using System; +using System.Globalization; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// AES cipher implementation. + /// + public sealed class AesCipher : BlockCipher + { + private const uint m1 = 0x80808080; + + private const uint m2 = 0x7f7f7f7f; + + private const uint m3 = 0x0000001b; + + private int _rounds; + + private uint[] _encryptionKey; + + private uint[] _decryptionKey; + + private uint C0, C1, C2, C3; + + #region Static Definition Tables + + private static readonly byte[] S = + { + 99, 124, 119, 123, 242, 107, 111, 197, + 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, + 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, + 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, + 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, + 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, + 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, + 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, + 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, + 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, + 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, + 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, + 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, + 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, + 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, + 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, + 65, 153, 45, 15, 176, 84, 187, 22 + }; + + // The inverse S-box + private static readonly byte[] Si = + { + 82, 9, 106, 213, 48, 54, 165, 56, + 191, 64, 163, 158, 129, 243, 215, 251, + 124, 227, 57, 130, 155, 47, 255, 135, + 52, 142, 67, 68, 196, 222, 233, 203, + 84, 123, 148, 50, 166, 194, 35, 61, + 238, 76, 149, 11, 66, 250, 195, 78, + 8, 46, 161, 102, 40, 217, 36, 178, + 118, 91, 162, 73, 109, 139, 209, 37, + 114, 248, 246, 100, 134, 104, 152, 22, + 212, 164, 92, 204, 93, 101, 182, 146, + 108, 112, 72, 80, 253, 237, 185, 218, + 94, 21, 70, 87, 167, 141, 157, 132, + 144, 216, 171, 0, 140, 188, 211, 10, + 247, 228, 88, 5, 184, 179, 69, 6, + 208, 44, 30, 143, 202, 63, 15, 2, + 193, 175, 189, 3, 1, 19, 138, 107, + 58, 145, 17, 65, 79, 103, 220, 234, + 151, 242, 207, 206, 240, 180, 230, 115, + 150, 172, 116, 34, 231, 173, 53, 133, + 226, 249, 55, 232, 28, 117, 223, 110, + 71, 241, 26, 113, 29, 41, 197, 137, + 111, 183, 98, 14, 170, 24, 190, 27, + 252, 86, 62, 75, 198, 210, 121, 32, + 154, 219, 192, 254, 120, 205, 90, 244, + 31, 221, 168, 51, 136, 7, 199, 49, + 177, 18, 16, 89, 39, 128, 236, 95, + 96, 81, 127, 169, 25, 181, 74, 13, + 45, 229, 122, 159, 147, 201, 156, 239, + 160, 224, 59, 77, 174, 42, 245, 176, + 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, + 225, 105, 20, 99, 85, 33, 12, 125 + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly byte[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 + }; + + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The mode. + /// The padding. + /// is null. + /// Keysize is not valid for this algorithm. + public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 16, mode, padding) + { + var keySize = key.Length * 8; + + if (!(keySize == 256 || keySize == 192 || keySize == 128)) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + /// or is null. + /// or is too short. + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer == null) + throw new ArgumentNullException("inputBuffer"); + + if (outputBuffer == null) + throw new ArgumentNullException("outputBuffer"); + + if ((inputOffset + (32 / 2)) > inputBuffer.Length) + { + throw new IndexOutOfRangeException("input buffer too short"); + } + + if ((outputOffset + (32 / 2)) > outputBuffer.Length) + { + throw new IndexOutOfRangeException("output buffer too short"); + } + + if (this._encryptionKey == null) + { + this._encryptionKey = this.GenerateWorkingKey(true, this.Key); + } + + this.UnPackBlock(inputBuffer, inputOffset); + + this.EncryptBlock(this._encryptionKey); + + this.PackBlock(outputBuffer, outputOffset); + + return this.BlockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + /// or is null. + /// or is too short. + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer == null) + throw new ArgumentNullException("inputBuffer"); + + if (outputBuffer == null) + throw new ArgumentNullException("outputBuffer"); + + if ((inputOffset + (32 / 2)) > inputBuffer.Length) + { + throw new IndexOutOfRangeException("input buffer too short"); + } + + if ((outputOffset + (32 / 2)) > outputBuffer.Length) + { + throw new IndexOutOfRangeException("output buffer too short"); + } + + if (this._decryptionKey == null) + { + this._decryptionKey = this.GenerateWorkingKey(false, this.Key); + } + + this.UnPackBlock(inputBuffer, inputOffset); + + this.DecryptBlock(this._decryptionKey); + + this.PackBlock(outputBuffer, outputOffset); + + return this.BlockSize; + } + + private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) + { + int KC = key.Length / 4; // key length in words + + if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length)) + throw new ArgumentException("Key length not 128/192/256 bits."); + + _rounds = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + uint[] W = new uint[(_rounds + 1) * 4]; // 4 words in a block + + // + // copy the key into the round key array + // + + int t = 0; + + for (int i = 0; i < key.Length; t++) + { + W[(t >> 2) * 4 + (t & 3)] = LittleEndianToUInt32(key, i); + i += 4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (_rounds + 1) << 2; + for (int i = KC; (i < k); i++) + { + uint temp = W[((i - 1) >> 2) * 4 + ((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) * 4 + (i & 3)] = W[((i - KC) >> 2) * 4 + ((i - KC) & 3)] ^ temp; + } + + if (!isEncryption) + { + for (int j = 1; j < _rounds; j++) + { + for (int i = 0; i < 4; i++) + { + W[j * 4 + i] = InvMcol(W[j * 4 + i]); + } + } + } + + return W; + } + + private uint Shift(uint r, int shift) + { + return (r >> shift) | (r << (32 - shift)); + } + + private uint FFmulX(uint x) + { + return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); + } + + private uint InvMcol(uint x) + { + uint f2 = FFmulX(x); + uint f4 = FFmulX(f2); + uint f8 = FFmulX(f4); + uint f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + private uint SubWord(uint x) + { + return (uint)S[x & 255] + | (((uint)S[(x >> 8) & 255]) << 8) + | (((uint)S[(x >> 16) & 255]) << 16) + | (((uint)S[(x >> 24) & 255]) << 24); + } + + private void UnPackBlock(byte[] bytes, int off) + { + C0 = LittleEndianToUInt32(bytes, off); + C1 = LittleEndianToUInt32(bytes, off + 4); + C2 = LittleEndianToUInt32(bytes, off + 8); + C3 = LittleEndianToUInt32(bytes, off + 12); + } + + private void PackBlock(byte[] bytes, int off) + { + UInt32ToLittleEndian(C0, bytes, off); + UInt32ToLittleEndian(C1, bytes, off + 4); + UInt32ToLittleEndian(C2, bytes, off + 8); + UInt32ToLittleEndian(C3, bytes, off + 12); + } + + private void EncryptBlock(uint[] KW) + { + int r; + uint r0, r1, r2, r3; + + C0 ^= KW[0 * 4 + 0]; + C1 ^= KW[0 * 4 + 1]; + C2 ^= KW[0 * 4 + 2]; + C3 ^= KW[0 * 4 + 3]; + + for (r = 1; r < _rounds - 1; ) + { + r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; + r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; + r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; + r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 3]; + C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[r * 4 + 0]; + C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[r * 4 + 1]; + C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[r * 4 + 2]; + C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[r++ * 4 + 3]; + } + + r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; + r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; + r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; + r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 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 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[r * 4 + 0]; + C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[r * 4 + 1]; + C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[r * 4 + 2]; + C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[r * 4 + 3]; + } + + private void DecryptBlock(uint[] KW) + { + int r; + uint r0, r1, r2, r3; + + C0 ^= KW[_rounds * 4 + 0]; + C1 ^= KW[_rounds * 4 + 1]; + C2 ^= KW[_rounds * 4 + 2]; + C3 ^= KW[_rounds * 4 + 3]; + + for (r = _rounds - 1; r > 1; ) + { + r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; + r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; + r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; + r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r-- * 4 + 3]; + C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[r * 4 + 0]; + C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[r * 4 + 1]; + C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[r * 4 + 2]; + C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[r-- * 4 + 3]; + } + + r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; + r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; + r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; + r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r * 4 + 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 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[0 * 4 + 0]; + C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[0 * 4 + 1]; + C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[0 * 4 + 2]; + C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[0 * 4 + 3]; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs index 8349c0e..fb3c001 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs @@ -1,183 +1,175 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements ARCH4 cipher algorithm - /// - public sealed class Arc4Cipher : StreamCipher - { - private readonly static int STATE_LENGTH = 256; - - /// - /// Holds the state of the RC4 engine - /// - private byte[] _engineState; - - private int _x; - - private int _y; - - private byte[] _workingKey; - - /// - /// Gets the minimum data size. - /// - /// - /// The minimum data size. - /// - public override byte MinimumSize - { - get { return 0; } - } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// if set to true will disharged first 1536 bytes. - /// is null. - public Arc4Cipher(byte[] key, bool dischargeFirstBytes) - : base(key) - { - this._workingKey = key; - SetKey(this._workingKey); - // The first 1536 bytes of keystream - // generated by the cipher MUST be discarded, and the first byte of the - // first encrypted packet MUST be encrypted using the 1537th byte of - // keystream. - if (dischargeFirstBytes) - this.Encrypt(new byte[1536]); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - return this.ProcessBytes(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - return this.ProcessBytes(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - /// - /// Encrypts the specified input. - /// - /// The input. - /// - /// Encrypted data. - /// - /// - public override byte[] Encrypt(byte[] input) - { - var output = new byte[input.Length]; - this.ProcessBytes(input, 0, input.Length, output, 0); - return output; - } - - /// - /// Decrypts the specified input. - /// - /// The input. - /// - /// Decrypted data. - /// - /// - public override byte[] Decrypt(byte[] input) - { - var output = new byte[input.Length]; - this.ProcessBytes(input, 0, input.Length, output, 0); - return output; - } - - private int ProcessBytes(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + inputCount) > inputBuffer.Length) - { - throw new IndexOutOfRangeException("input buffer too short"); - } - - if ((outputOffset + inputCount) > outputBuffer.Length) - { - throw new IndexOutOfRangeException("output buffer too short"); - } - - for (int i = 0; i < inputCount; i++) - { - this._x = (this._x + 1) & 0xff; - this._y = (this._engineState[this._x] + this._y) & 0xff; - - // swap - byte tmp = this._engineState[this._x]; - this._engineState[this._x] = this._engineState[this._y]; - this._engineState[this._y] = tmp; - - // xor - outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ this._engineState[(this._engineState[this._x] + this._engineState[this._y]) & 0xff]); - } - return inputCount; - } - - private void Reset() - { - SetKey(this._workingKey); - } - - private void SetKey(byte[] keyBytes) - { - this._workingKey = keyBytes; - - this._x = 0; - this._y = 0; - - if (this._engineState == null) - { - this._engineState = new byte[STATE_LENGTH]; - } - - // reset the state of the engine - for (byte i = 0; i < STATE_LENGTH; i++) - { - this._engineState[i] = i; - } - - int i1 = 0; - int i2 = 0; - - for (int i = 0; i < STATE_LENGTH; i++) - { - i2 = ((keyBytes[i1] & 0xff) + this._engineState[i] + i2) & 0xff; - // do the byte-swap inline - byte tmp = this._engineState[i]; - this._engineState[i] = this._engineState[i2]; - this._engineState[i2] = tmp; - i1 = (i1 + 1) % keyBytes.Length; - } - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Implements ARCH4 cipher algorithm + /// + public sealed class Arc4Cipher : StreamCipher + { + private static readonly int STATE_LENGTH = 256; + + /// + /// Holds the state of the RC4 engine + /// + private byte[] _engineState; + + private int _x; + + private int _y; + + private byte[] _workingKey; + + /// + /// Gets the minimum data size. + /// + /// + /// The minimum data size. + /// + public override byte MinimumSize + { + get { return 0; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// if set to true will disharged first 1536 bytes. + /// is null. + public Arc4Cipher(byte[] key, bool dischargeFirstBytes) + : base(key) + { + this._workingKey = key; + SetKey(this._workingKey); + // The first 1536 bytes of keystream + // generated by the cipher MUST be discarded, and the first byte of the + // first encrypted packet MUST be encrypted using the 1537th byte of + // keystream. + if (dischargeFirstBytes) + this.Encrypt(new byte[1536]); + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + return this.ProcessBytes(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + return this.ProcessBytes(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + } + + /// + /// Encrypts the specified input. + /// + /// The input. + /// + /// Encrypted data. + /// + /// + public override byte[] Encrypt(byte[] input) + { + var output = new byte[input.Length]; + this.ProcessBytes(input, 0, input.Length, output, 0); + return output; + } + + /// + /// Decrypts the specified input. + /// + /// The input. + /// + /// Decrypted data. + /// + /// + public override byte[] Decrypt(byte[] input) + { + var output = new byte[input.Length]; + this.ProcessBytes(input, 0, input.Length, output, 0); + return output; + } + + private int ProcessBytes(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if ((inputOffset + inputCount) > inputBuffer.Length) + { + throw new IndexOutOfRangeException("input buffer too short"); + } + + if ((outputOffset + inputCount) > outputBuffer.Length) + { + throw new IndexOutOfRangeException("output buffer too short"); + } + + for (int i = 0; i < inputCount; i++) + { + this._x = (this._x + 1) & 0xff; + this._y = (this._engineState[this._x] + this._y) & 0xff; + + // swap + byte tmp = this._engineState[this._x]; + this._engineState[this._x] = this._engineState[this._y]; + this._engineState[this._y] = tmp; + + // xor + outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ this._engineState[(this._engineState[this._x] + this._engineState[this._y]) & 0xff]); + } + return inputCount; + } + + private void SetKey(byte[] keyBytes) + { + this._workingKey = keyBytes; + + this._x = 0; + this._y = 0; + + if (this._engineState == null) + { + this._engineState = new byte[STATE_LENGTH]; + } + + // reset the state of the engine + for (var i = 0; i < STATE_LENGTH; i++) + { + this._engineState[i] = (byte) i; + } + + int i1 = 0; + int i2 = 0; + + for (var i = 0; i < STATE_LENGTH; i++) + { + i2 = ((keyBytes[i1] & 0xff) + this._engineState[i] + i2) & 0xff; + // do the byte-swap inline + byte tmp = this._engineState[i]; + this._engineState[i] = this._engineState[i2]; + this._engineState[i2] = tmp; + i1 = (i1 + 1) % keyBytes.Length; + } + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs index 9f4e4ba..f78afec 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs @@ -1,519 +1,516 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Blowfish cipher implementation. - /// - public sealed class BlowfishCipher : BlockCipher - { - #region Static reference tables - - private readonly static uint[] 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 - }; - - #endregion - - private static readonly int _rounds = 16; - - private static readonly int _sboxSk = 256; - - private static readonly int _pSize = _rounds + 2; - - /// - /// The s-boxes - /// - private readonly uint[] _s0, _s1, _s2, _s3; - - /// - /// The p-array - /// - private readonly uint[] _p; - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public BlowfishCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 8, mode, padding) - { - var keySize = key.Length * 8; - - if (keySize < 1 || keySize > 448) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - this._s0 = new uint[_sboxSk]; - - this._s1 = new uint[_sboxSk]; - - this._s2 = new uint[_sboxSk]; - - this._s3 = new uint[_sboxSk]; - - this._p = new uint[_pSize]; - - this.SetKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - uint xl = BigEndianToUInt32(inputBuffer, inputOffset); - uint xr = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - xl ^= this._p[0]; - - for (int i = 1; i < _rounds; i += 2) - { - xr ^= F(xl) ^ this._p[i]; - xl ^= F(xr) ^ this._p[i + 1]; - } - - xr ^= this._p[_rounds + 1]; - - UInt32ToBigEndian(xr, outputBuffer, outputOffset); - UInt32ToBigEndian(xl, outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - uint xl = BigEndianToUInt32(inputBuffer, inputOffset); - uint xr = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - xl ^= this._p[_rounds + 1]; - - for (int i = _rounds; i > 0; i -= 2) - { - xr ^= F(xl) ^ this._p[i]; - xl ^= F(xr) ^ this._p[i - 1]; - } - - xr ^= this._p[0]; - - UInt32ToBigEndian(xr, outputBuffer, outputOffset); - UInt32ToBigEndian(xl, outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - private uint F(uint x) - { - return (((this._s0[x >> 24] + this._s1[(x >> 16) & 0xff]) ^ this._s2[(x >> 8) & 0xff]) + this._s3[x & 0xff]); - } - - 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...) - */ - Buffer.BlockCopy(KS0, 0, this._s0, 0, _sboxSk * sizeof(uint)); - Buffer.BlockCopy(KS1, 0, this._s1, 0, _sboxSk * sizeof(uint)); - Buffer.BlockCopy(KS2, 0, this._s2, 0, _sboxSk * sizeof(uint)); - Buffer.BlockCopy(KS3, 0, this._s3, 0, _sboxSk * sizeof(uint)); - - Buffer.BlockCopy(KP, 0, this._p, 0, _pSize * sizeof(uint)); - - /* - * (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 < _pSize; i++) - { - // Get the 32 bits of the key, in 4 * 8 bit chunks - uint data = 0x0000000; - for (int j = 0; j < 4; j++) - { - // create a 32 bit block - data = (data << 8) | (uint)key[keyIndex++]; - - // 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 - this._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, this._p); - ProcessTable(this._p[_pSize - 2], this._p[_pSize - 1], this._s0); - ProcessTable(this._s0[_sboxSk - 2], this._s0[_sboxSk - 1], this._s1); - ProcessTable(this._s1[_sboxSk - 2], this._s1[_sboxSk - 1], this._s2); - ProcessTable(this._s2[_sboxSk - 2], this._s2[_sboxSk - 1], this._s3); - } - - /// - /// apply the encryption cycle to each value pair in the table. - /// - /// The xl. - /// The xr. - /// The table. - private void ProcessTable(uint xl, uint xr, uint[] 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]; - } - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Blowfish cipher implementation. + /// + public sealed class BlowfishCipher : BlockCipher + { + #region Static reference tables + + private readonly static uint[] 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 + }; + + #endregion + + private const int _rounds = 16; + + private const int _sboxSk = 256; + + private const int _pSize = _rounds + 2; + + /// + /// The s-boxes + /// + private readonly uint[] _s0, _s1, _s2, _s3; + + /// + /// The p-array + /// + private readonly uint[] _p; + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The mode. + /// The padding. + /// is null. + /// Keysize is not valid for this algorithm. + public BlowfishCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 8, mode, padding) + { + var keySize = key.Length * 8; + + if (keySize < 1 || keySize > 448) + throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + + this._s0 = new uint[_sboxSk]; + + this._s1 = new uint[_sboxSk]; + + this._s2 = new uint[_sboxSk]; + + this._s3 = new uint[_sboxSk]; + + this._p = new uint[_pSize]; + + this.SetKey(key); + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputCount != this.BlockSize) + throw new ArgumentException("inputCount"); + + uint xl = BigEndianToUInt32(inputBuffer, inputOffset); + uint xr = BigEndianToUInt32(inputBuffer, inputOffset + 4); + + xl ^= this._p[0]; + + for (int i = 1; i < _rounds; i += 2) + { + xr ^= F(xl) ^ this._p[i]; + xl ^= F(xr) ^ this._p[i + 1]; + } + + xr ^= this._p[_rounds + 1]; + + UInt32ToBigEndian(xr, outputBuffer, outputOffset); + UInt32ToBigEndian(xl, outputBuffer, outputOffset + 4); + + return this.BlockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputCount != this.BlockSize) + throw new ArgumentException("inputCount"); + + uint xl = BigEndianToUInt32(inputBuffer, inputOffset); + uint xr = BigEndianToUInt32(inputBuffer, inputOffset + 4); + + xl ^= this._p[_rounds + 1]; + + for (int i = _rounds; i > 0; i -= 2) + { + xr ^= F(xl) ^ this._p[i]; + xl ^= F(xr) ^ this._p[i - 1]; + } + + xr ^= this._p[0]; + + UInt32ToBigEndian(xr, outputBuffer, outputOffset); + UInt32ToBigEndian(xl, outputBuffer, outputOffset + 4); + + return this.BlockSize; + } + + private uint F(uint x) + { + return (((this._s0[x >> 24] + this._s1[(x >> 16) & 0xff]) ^ this._s2[(x >> 8) & 0xff]) + this._s3[x & 0xff]); + } + + 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...) + */ + Buffer.BlockCopy(KS0, 0, this._s0, 0, _sboxSk * sizeof(uint)); + Buffer.BlockCopy(KS1, 0, this._s1, 0, _sboxSk * sizeof(uint)); + Buffer.BlockCopy(KS2, 0, this._s2, 0, _sboxSk * sizeof(uint)); + Buffer.BlockCopy(KS3, 0, this._s3, 0, _sboxSk * sizeof(uint)); + + Buffer.BlockCopy(KP, 0, this._p, 0, _pSize * sizeof(uint)); + + /* + * (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 < _pSize; i++) + { + // Get the 32 bits of the key, in 4 * 8 bit chunks + uint data = 0x0000000; + for (int j = 0; j < 4; j++) + { + // create a 32 bit block + data = (data << 8) | (uint)key[keyIndex++]; + + // 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 + this._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, this._p); + ProcessTable(this._p[_pSize - 2], this._p[_pSize - 1], this._s0); + ProcessTable(this._s0[_sboxSk - 2], this._s0[_sboxSk - 1], this._s1); + ProcessTable(this._s1[_sboxSk - 2], this._s1[_sboxSk - 1], this._s2); + ProcessTable(this._s2[_sboxSk - 2], this._s2[_sboxSk - 1], this._s3); + } + + /// + /// apply the encryption cycle to each value pair in the table. + /// + /// The xl. + /// The xr. + /// The table. + private void ProcessTable(uint xl, uint xr, uint[] 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]; + } + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs index 301f4a1..f9308e4 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs @@ -1,733 +1,726 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements CAST cipher algorithm - /// - public sealed class CastCipher : BlockCipher - { - internal static readonly int MAX_ROUNDS = 16; - - internal static readonly int RED_ROUNDS = 12; - - /// - /// The rotating round key - /// - private int[] _kr = new int[17]; - - /// - /// The masking round key - /// - private uint[] _km = new uint[17]; - - private int _rounds = MAX_ROUNDS; - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public CastCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 8, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize >= 40 && keySize <= 128 && keySize % 8 == 0)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - this.SetKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - // process the input block - // batch the units up into a 32 bit chunk and go for it - // the array is in bytes, the increment is 8x8 bits = 64 - - uint L0 = BigEndianToUInt32(inputBuffer, inputOffset); - uint R0 = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - uint[] result = new uint[2]; - this.CastEncipher(L0, R0, result); - - // now stuff them into the destination block - UInt32ToBigEndian(result[0], outputBuffer, outputOffset); - UInt32ToBigEndian(result[1], outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - // process the input block - // batch the units up into a 32 bit chunk and go for it - // the array is in bytes, the increment is 8x8 bits = 64 - uint L16 = BigEndianToUInt32(inputBuffer, inputOffset); - uint R16 = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - uint[] result = new uint[2]; - this.CastDecipher(L16, R16, result); - - // now stuff them into the destination block - UInt32ToBigEndian(result[0], outputBuffer, outputOffset); - UInt32ToBigEndian(result[1], outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - #region Static Definition Tables - - internal static readonly uint[] S1 = - { - 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, - 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, - 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, - 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, - 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, - 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, - 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, - 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, - 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, - 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, - 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, - 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, - 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, - 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, - 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, - 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, - 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, - 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, - 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, - 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, - 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, - 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, - 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, - 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, - 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, - 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, - 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, - 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, - 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, - 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, - 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, - 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf - }, - S2 = - { - 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, - 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, - 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, - 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, - 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, - 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, - 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, - 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, - 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, - 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, - 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, - 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, - 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, - 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, - 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, - 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, - 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, - 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, - 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, - 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, - 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, - 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, - 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, - 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, - 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, - 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, - 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, - 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, - 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, - 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, - 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, - 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1 - }, - S3 = - { - 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, - 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, - 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, - 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, - 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, - 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, - 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, - 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, - 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, - 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, - 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, - 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, - 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, - 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, - 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, - 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, - 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, - 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, - 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, - 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, - 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, - 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, - 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, - 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, - 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, - 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, - 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, - 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, - 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, - 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, - 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, - 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783 - }, - S4 = - { - 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, - 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, - 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, - 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, - 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, - 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, - 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, - 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, - 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, - 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, - 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, - 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, - 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, - 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, - 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, - 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, - 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, - 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, - 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, - 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, - 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, - 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, - 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, - 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, - 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, - 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, - 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, - 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, - 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, - 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, - 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, - 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2 - }, - S5 = - { - 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, - 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, - 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, - 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, - 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, - 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, - 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, - 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, - 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, - 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, - 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, - 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, - 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, - 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, - 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, - 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, - 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, - 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, - 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, - 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, - 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, - 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, - 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, - 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, - 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, - 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, - 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, - 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, - 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, - 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, - 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, - 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4 - }, - S6 = - { - 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, - 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, - 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, - 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, - 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, - 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, - 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, - 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, - 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, - 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, - 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, - 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, - 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, - 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, - 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, - 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, - 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, - 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, - 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, - 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, - 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, - 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, - 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, - 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, - 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, - 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, - 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, - 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, - 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, - 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, - 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, - 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f - }, - S7 = - { - 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, - 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, - 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, - 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, - 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, - 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, - 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, - 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, - 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, - 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, - 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, - 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, - 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, - 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, - 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, - 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, - 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, - 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, - 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, - 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, - 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, - 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, - 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, - 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, - 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, - 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, - 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, - 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, - 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, - 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, - 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, - 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3 - }, - S8 = - { - 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, - 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, - 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, - 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, - 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, - 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, - 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, - 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, - 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, - 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, - 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, - 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, - 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, - 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, - 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, - 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, - 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, - 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, - 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, - 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, - 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, - 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, - 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, - 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, - 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, - 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, - 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, - 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, - 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, - 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, - 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, - 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e - }; - - #endregion - - /// - /// Sets the subkeys using the same nomenclatureas described in RFC2144. - /// - /// The key. - private void SetKey(byte[] key) - { - /* - * Determine the key size here, if required - * - * if keysize <= 80bits, use 12 rounds instead of 16 - * if keysize < 128bits, pad with 0 - * - * Typical key sizes => 40, 64, 80, 128 - */ - - if (key.Length < 11) - { - this._rounds = RED_ROUNDS; - } - - int[] z = new int[16]; - int[] x = new int[16]; - - uint z03, z47, z8B, zCF; - uint x03, x47, x8B, xCF; - - /* copy the key into x */ - for (int i = 0; i < key.Length; i++) - { - x[i] = (int)(key[i] & 0xff); - } - - /* - * This will look different because the selection of - * bytes from the input key I've already chosen the - * correct int. - */ - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._km[1] = S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]; - this._km[2] = S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]; - this._km[3] = S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]; - this._km[4] = S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]; - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._km[5] = S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]; - this._km[6] = S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]; - this._km[7] = S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]; - this._km[8] = S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]; - - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._km[9] = S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]; - this._km[10] = S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]; - this._km[11] = S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]; - this._km[12] = S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]; - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._km[13] = S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]; - this._km[14] = S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]; - this._km[15] = S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]; - this._km[16] = S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]; - - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._kr[1] = (int)((S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]) & 0x1f); - this._kr[2] = (int)((S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]) & 0x1f); - this._kr[3] = (int)((S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]) & 0x1f); - this._kr[4] = (int)((S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]) & 0x1f); - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._kr[5] = (int)((S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]) & 0x1f); - this._kr[6] = (int)((S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]) & 0x1f); - this._kr[7] = (int)((S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]) & 0x1f); - this._kr[8] = (int)((S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]) & 0x1f); - - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._kr[9] = (int)((S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]) & 0x1f); - this._kr[10] = (int)((S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]) & 0x1f); - this._kr[11] = (int)((S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]) & 0x1f); - this._kr[12] = (int)((S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]) & 0x1f); - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._kr[13] = (int)((S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]) & 0x1f); - this._kr[14] = (int)((S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]) & 0x1f); - this._kr[15] = (int)((S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]) & 0x1f); - this._kr[16] = (int)((S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]) & 0x1f); - } - - /// - /// The first of the three processing functions for the encryption and decryption. - /// - /// The input to be processed. - /// The mask to be used from Km[n]. - /// The rotation value to be used. - /// - private static uint F1(uint D, uint Kmi, int Kri) - { - uint I = Kmi + D; - I = I << Kri | (I >> (32 - Kri)); - return ((S1[(I >> 24) & 0xff] ^ S2[(I >> 16) & 0xff]) - S3[(I >> 8) & 0xff]) + S4[I & 0xff]; - } - - /// - /// The second of the three processing functions for the encryption and decryption. - /// - /// The input to be processed. - /// The mask to be used from Km[n]. - /// The rotation value to be used. - /// - private static uint F2(uint D, uint Kmi, int Kri) - { - uint I = Kmi ^ D; - I = I << Kri | (I >> (32 - Kri)); - return ((S1[(I >> 24) & 0xff] - S2[(I >> 16) & 0xff]) + S3[(I >> 8) & 0xff]) ^ S4[I & 0xff]; - } - - /// - /// The third of the three processing functions for the encryption and decryption. - /// - /// The input to be processed. - /// The mask to be used from Km[n]. - /// The rotation value to be used. - /// - private static uint F3(uint D, uint Kmi, int Kri) - { - uint I = Kmi - D; - I = I << Kri | (I >> (32 - Kri)); - return ((S1[(I >> 24) & 0xff] + S2[(I >> 16) & 0xff]) ^ S3[(I >> 8) & 0xff]) - S4[I & 0xff]; - } - - /// - /// Does the 16 rounds to encrypt the block. - /// - /// The LH-32bits of the plaintext block. - /// The RH-32bits of the plaintext block. - /// The result. - private void CastEncipher(uint L0, uint R0, uint[] result) - { - uint Lp = L0; // the previous value, equiv to L[i-1] - uint Rp = R0; // equivalent to R[i-1] - - /* - * numbering consistent with paper to make - * checking and validating easier - */ - uint Li = L0, Ri = R0; - - for (int i = 1; i <= this._rounds; i++) - { - Lp = Li; - Rp = Ri; - - Li = Rp; - switch (i) - { - case 1: - case 4: - case 7: - case 10: - case 13: - case 16: - Ri = Lp ^ F1(Rp, this._km[i], this._kr[i]); - break; - case 2: - case 5: - case 8: - case 11: - case 14: - Ri = Lp ^ F2(Rp, this._km[i], this._kr[i]); - break; - case 3: - case 6: - case 9: - case 12: - case 15: - Ri = Lp ^ F3(Rp, this._km[i], this._kr[i]); - break; - } - } - - result[0] = Ri; - result[1] = Li; - - return; - } - - private void CastDecipher(uint L16, uint R16, uint[] result) - { - uint Lp = L16; // the previous value, equiv to L[i-1] - uint Rp = R16; // equivalent to R[i-1] - - /* - * numbering consistent with paper to make - * checking and validating easier - */ - uint Li = L16, Ri = R16; - - for (int i = this._rounds; i > 0; i--) - { - Lp = Li; - Rp = Ri; - - Li = Rp; - switch (i) - { - case 1: - case 4: - case 7: - case 10: - case 13: - case 16: - Ri = Lp ^ F1(Rp, this._km[i], this._kr[i]); - break; - case 2: - case 5: - case 8: - case 11: - case 14: - Ri = Lp ^ F2(Rp, this._km[i], this._kr[i]); - break; - case 3: - case 6: - case 9: - case 12: - case 15: - Ri = Lp ^ F3(Rp, this._km[i], this._kr[i]); - break; - } - } - - result[0] = Ri; - result[1] = Li; - - return; - } - - private static void Bits32ToInts(uint inData, int[] b, int offset) - { - b[offset + 3] = (int)(inData & 0xff); - b[offset + 2] = (int)((inData >> 8) & 0xff); - b[offset + 1] = (int)((inData >> 16) & 0xff); - b[offset] = (int)((inData >> 24) & 0xff); - } - - private static uint IntsTo32bits(int[] b, int i) - { - return (uint)(((b[i] & 0xff) << 24) | - ((b[i + 1] & 0xff) << 16) | - ((b[i + 2] & 0xff) << 8) | - ((b[i + 3] & 0xff))); - } - - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Implements CAST cipher algorithm + /// + public sealed class CastCipher : BlockCipher + { + internal static readonly int MAX_ROUNDS = 16; + + internal static readonly int RED_ROUNDS = 12; + + /// + /// The rotating round key + /// + private readonly int[] _kr = new int[17]; + + /// + /// The masking round key + /// + private readonly uint[] _km = new uint[17]; + + private int _rounds = MAX_ROUNDS; + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The mode. + /// The padding. + /// is null. + /// Keysize is not valid for this algorithm. + public CastCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 8, mode, padding) + { + var keySize = key.Length * 8; + + if (!(keySize >= 40 && keySize <= 128 && keySize % 8 == 0)) + throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + + this.SetKey(key); + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + + uint L0 = BigEndianToUInt32(inputBuffer, inputOffset); + uint R0 = BigEndianToUInt32(inputBuffer, inputOffset + 4); + + uint[] result = new uint[2]; + this.CastEncipher(L0, R0, result); + + // now stuff them into the destination block + UInt32ToBigEndian(result[0], outputBuffer, outputOffset); + UInt32ToBigEndian(result[1], outputBuffer, outputOffset + 4); + + return this.BlockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + // process the input block + // batch the units up into a 32 bit chunk and go for it + // the array is in bytes, the increment is 8x8 bits = 64 + uint L16 = BigEndianToUInt32(inputBuffer, inputOffset); + uint R16 = BigEndianToUInt32(inputBuffer, inputOffset + 4); + + uint[] result = new uint[2]; + this.CastDecipher(L16, R16, result); + + // now stuff them into the destination block + UInt32ToBigEndian(result[0], outputBuffer, outputOffset); + UInt32ToBigEndian(result[1], outputBuffer, outputOffset + 4); + + return this.BlockSize; + } + + #region Static Definition Tables + + internal static readonly uint[] S1 = + { + 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, + 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, + 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, + 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, + 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, + 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, + 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, + 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, + 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, + 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, + 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, + 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, + 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, + 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, + 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, + 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, + 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, + 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, + 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, + 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, + 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, + 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, + 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, + 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, + 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, + 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, + 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, + 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, + 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, + 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, + 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, + 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf + }, + S2 = + { + 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, + 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, + 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, + 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, + 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, + 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, + 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, + 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, + 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, + 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, + 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, + 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, + 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, + 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, + 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, + 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, + 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, + 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, + 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, + 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, + 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, + 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, + 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, + 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, + 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, + 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, + 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, + 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, + 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, + 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, + 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, + 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1 + }, + S3 = + { + 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, + 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, + 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, + 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, + 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, + 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, + 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, + 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, + 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, + 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, + 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, + 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, + 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, + 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, + 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, + 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, + 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, + 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, + 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, + 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, + 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, + 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, + 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, + 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, + 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, + 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, + 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, + 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, + 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, + 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, + 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, + 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783 + }, + S4 = + { + 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, + 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, + 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, + 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, + 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, + 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, + 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, + 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, + 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, + 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, + 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, + 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, + 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, + 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, + 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, + 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, + 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, + 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, + 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, + 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, + 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, + 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, + 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, + 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, + 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, + 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, + 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, + 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, + 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, + 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, + 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, + 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2 + }, + S5 = + { + 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, + 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, + 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, + 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, + 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, + 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, + 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, + 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, + 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, + 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, + 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, + 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, + 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, + 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, + 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, + 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, + 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, + 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, + 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, + 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, + 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, + 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, + 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, + 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, + 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, + 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, + 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, + 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, + 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, + 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, + 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, + 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4 + }, + S6 = + { + 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, + 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, + 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, + 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, + 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, + 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, + 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, + 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, + 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, + 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, + 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, + 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, + 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, + 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, + 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, + 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, + 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, + 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, + 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, + 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, + 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, + 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, + 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, + 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, + 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, + 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, + 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, + 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, + 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, + 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, + 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, + 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f + }, + S7 = + { + 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, + 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, + 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, + 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, + 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, + 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, + 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, + 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, + 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, + 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, + 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, + 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, + 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, + 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, + 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, + 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, + 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, + 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, + 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, + 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, + 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, + 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, + 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, + 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, + 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, + 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, + 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, + 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, + 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, + 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, + 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, + 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3 + }, + S8 = + { + 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, + 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, + 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, + 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, + 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, + 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, + 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, + 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, + 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, + 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, + 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, + 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, + 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, + 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, + 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, + 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, + 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, + 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, + 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, + 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, + 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, + 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, + 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, + 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, + 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, + 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, + 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, + 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, + 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, + 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, + 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, + 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e + }; + + #endregion + + /// + /// Sets the subkeys using the same nomenclatureas described in RFC2144. + /// + /// The key. + private void SetKey(byte[] key) + { + /* + * Determine the key size here, if required + * + * if keysize <= 80bits, use 12 rounds instead of 16 + * if keysize < 128bits, pad with 0 + * + * Typical key sizes => 40, 64, 80, 128 + */ + + if (key.Length < 11) + { + this._rounds = RED_ROUNDS; + } + + int[] z = new int[16]; + int[] x = new int[16]; + + uint z03, z47, z8B, zCF; + uint x03, x47, x8B, xCF; + + /* copy the key into x */ + for (int i = 0; i < key.Length; i++) + { + x[i] = (int)(key[i] & 0xff); + } + + /* + * This will look different because the selection of + * bytes from the input key I've already chosen the + * correct int. + */ + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + + z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; + + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + this._km[1] = S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]; + this._km[2] = S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]; + this._km[3] = S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]; + this._km[4] = S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + this._km[5] = S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]; + this._km[6] = S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]; + this._km[7] = S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]; + this._km[8] = S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + this._km[9] = S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]; + this._km[10] = S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]; + this._km[11] = S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]; + this._km[12] = S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]; + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + this._km[13] = S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]; + this._km[14] = S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]; + this._km[15] = S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]; + this._km[16] = S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]; + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + this._kr[1] = (int)((S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]) & 0x1f); + this._kr[2] = (int)((S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]) & 0x1f); + this._kr[3] = (int)((S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]) & 0x1f); + this._kr[4] = (int)((S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]) & 0x1f); + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + this._kr[5] = (int)((S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]) & 0x1f); + this._kr[6] = (int)((S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]) & 0x1f); + this._kr[7] = (int)((S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]) & 0x1f); + this._kr[8] = (int)((S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]) & 0x1f); + + x03 = IntsTo32bits(x, 0x0); + x47 = IntsTo32bits(x, 0x4); + x8B = IntsTo32bits(x, 0x8); + xCF = IntsTo32bits(x, 0xC); + z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; + Bits32ToInts(z03, z, 0x0); + z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; + Bits32ToInts(z47, z, 0x4); + z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; + Bits32ToInts(z8B, z, 0x8); + zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; + Bits32ToInts(zCF, z, 0xC); + this._kr[9] = (int)((S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]) & 0x1f); + this._kr[10] = (int)((S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]) & 0x1f); + this._kr[11] = (int)((S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]) & 0x1f); + this._kr[12] = (int)((S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]) & 0x1f); + + z03 = IntsTo32bits(z, 0x0); + z47 = IntsTo32bits(z, 0x4); + z8B = IntsTo32bits(z, 0x8); + zCF = IntsTo32bits(z, 0xC); + x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; + Bits32ToInts(x03, x, 0x0); + x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; + Bits32ToInts(x47, x, 0x4); + x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; + Bits32ToInts(x8B, x, 0x8); + xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; + Bits32ToInts(xCF, x, 0xC); + this._kr[13] = (int)((S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]) & 0x1f); + this._kr[14] = (int)((S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]) & 0x1f); + this._kr[15] = (int)((S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]) & 0x1f); + this._kr[16] = (int)((S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]) & 0x1f); + } + + /// + /// The first of the three processing functions for the encryption and decryption. + /// + /// The input to be processed. + /// The mask to be used from Km[n]. + /// The rotation value to be used. + /// + private static uint F1(uint D, uint Kmi, int Kri) + { + uint I = Kmi + D; + I = I << Kri | (I >> (32 - Kri)); + return ((S1[(I >> 24) & 0xff] ^ S2[(I >> 16) & 0xff]) - S3[(I >> 8) & 0xff]) + S4[I & 0xff]; + } + + /// + /// The second of the three processing functions for the encryption and decryption. + /// + /// The input to be processed. + /// The mask to be used from Km[n]. + /// The rotation value to be used. + /// + private static uint F2(uint D, uint Kmi, int Kri) + { + uint I = Kmi ^ D; + I = I << Kri | (I >> (32 - Kri)); + return ((S1[(I >> 24) & 0xff] - S2[(I >> 16) & 0xff]) + S3[(I >> 8) & 0xff]) ^ S4[I & 0xff]; + } + + /// + /// The third of the three processing functions for the encryption and decryption. + /// + /// The input to be processed. + /// The mask to be used from Km[n]. + /// The rotation value to be used. + /// + private static uint F3(uint D, uint Kmi, int Kri) + { + uint I = Kmi - D; + I = I << Kri | (I >> (32 - Kri)); + return ((S1[(I >> 24) & 0xff] + S2[(I >> 16) & 0xff]) ^ S3[(I >> 8) & 0xff]) - S4[I & 0xff]; + } + + /// + /// Does the 16 rounds to encrypt the block. + /// + /// The LH-32bits of the plaintext block. + /// The RH-32bits of the plaintext block. + /// The result. + private void CastEncipher(uint L0, uint R0, uint[] result) + { + uint Lp = L0; // the previous value, equiv to L[i-1] + uint Rp = R0; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + uint Li = L0, Ri = R0; + + for (int i = 1; i <= this._rounds; i++) + { + Lp = Li; + Rp = Ri; + + Li = Rp; + switch (i) + { + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, this._km[i], this._kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, this._km[i], this._kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, this._km[i], this._kr[i]); + break; + } + } + + result[0] = Ri; + result[1] = Li; + } + + private void CastDecipher(uint L16, uint R16, uint[] result) + { + uint Lp = L16; // the previous value, equiv to L[i-1] + uint Rp = R16; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + uint Li = L16, Ri = R16; + + for (int i = this._rounds; i > 0; i--) + { + Lp = Li; + Rp = Ri; + + Li = Rp; + switch (i) + { + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + Ri = Lp ^ F1(Rp, this._km[i], this._kr[i]); + break; + case 2: + case 5: + case 8: + case 11: + case 14: + Ri = Lp ^ F2(Rp, this._km[i], this._kr[i]); + break; + case 3: + case 6: + case 9: + case 12: + case 15: + Ri = Lp ^ F3(Rp, this._km[i], this._kr[i]); + break; + } + } + + result[0] = Ri; + result[1] = Li; + } + + private static void Bits32ToInts(uint inData, int[] b, int offset) + { + b[offset + 3] = (int)(inData & 0xff); + b[offset + 2] = (int)((inData >> 8) & 0xff); + b[offset + 1] = (int)((inData >> 16) & 0xff); + b[offset] = (int)((inData >> 24) & 0xff); + } + + private static uint IntsTo32bits(int[] b, int i) + { + return (uint)(((b[i] & 0xff) << 24) | + ((b[i + 1] & 0xff) << 16) | + ((b[i + 2] & 0xff) << 8) | + ((b[i + 3] & 0xff))); + } + + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs index 6984f72..a57cb5a 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs @@ -1,74 +1,71 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Base class for cipher mode implementations - /// - public abstract class CipherMode - { - /// - /// Gets the cipher. - /// - protected BlockCipher Cipher; - - /// - /// Gets the IV vector. - /// - protected byte[] IV; - - /// - /// Holds block size of the cipher. - /// - protected int _blockSize; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - protected CipherMode(byte[] iv) - { - this.IV = iv; - } - - /// - /// Initializes the specified cipher mode. - /// - /// The cipher. - internal void Init(BlockCipher cipher) - { - this.Cipher = cipher; - this._blockSize = cipher.BlockSize; - this.IV = this.IV.Take(this._blockSize).ToArray(); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - } -} +using System.Linq; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Base class for cipher mode implementations + /// + public abstract class CipherMode + { + /// + /// Gets the cipher. + /// + protected BlockCipher Cipher; + + /// + /// Gets the IV vector. + /// + protected byte[] IV; + + /// + /// Holds block size of the cipher. + /// + protected int _blockSize; + + /// + /// Initializes a new instance of the class. + /// + /// The iv. + protected CipherMode(byte[] iv) + { + this.IV = iv; + } + + /// + /// Initializes the specified cipher mode. + /// + /// The cipher. + internal void Init(BlockCipher cipher) + { + this.Cipher = cipher; + this._blockSize = cipher.BlockSize; + this.IV = this.IV.Take(this._blockSize).ToArray(); + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs b/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs index 691da9d..c76080d 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs @@ -1,21 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Base class for cipher padding implementations - /// - public abstract class CipherPadding - { - /// - /// Pads specified input to match block size. - /// - /// Size of the block. - /// The input. - /// Padded data array. - public abstract byte[] Pad(int blockSize, byte[] input); - } -} +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Base class for cipher padding implementations + /// + public abstract class CipherPadding + { + /// + /// Pads specified input to match block size. + /// + /// Size of the block. + /// The input. + /// Padded data array. + public abstract byte[] Pad(int blockSize, byte[] input); + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs index fe73139..41ff0cc 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs @@ -1,487 +1,484 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements DES cipher algorithm. - /// - public class DesCipher : BlockCipher - { - private int[] _encryptionKey; - - private int[] _decryptionKey; - - #region Static tables - - private static readonly short[] bytebit = - { - 128, 64, 32, 16, 8, 4, 2, 1 - }; - - private static readonly 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). - */ - private static readonly 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 - }; - - private static readonly byte[] totrot = - { - 1, 2, 4, 6, 8, 10, 12, 14, - 15, 17, 19, 21, 23, 25, 27, 28 - }; - - private static readonly 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - #endregion - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - public DesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 8, mode, padding) - { - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._encryptionKey == null) - { - this._encryptionKey = GenerateWorkingKey(true, this.Key); - } - - DesCipher.DesFunc(this._encryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._decryptionKey == null) - { - this._decryptionKey = GenerateWorkingKey(false, this.Key); - } - - DesCipher.DesFunc(this._decryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Generates the working key. - /// - /// if set to true [encrypting]. - /// The key. - /// Generated working key. - protected int[] GenerateWorkingKey(bool encrypting, byte[] key) - { - this.ValidateKey(); - - int[] newKey = new int[32]; - bool[] pc1m = new bool[56]; - bool[] pcr = new bool[56]; - - for (int j = 0; j < 56; j++) - { - int l = pc1[j]; - - pc1m[j] = ((key[(uint)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] = (int) ((uint)((i1 & 0x00fc0000) << 6) | - (uint)((i1 & 0x00000fc0) << 10) | - ((uint)(i2 & 0x00fc0000) >> 10) | - ((uint)(i2 & 0x00000fc0) >> 6)); - - newKey[i + 1] = (int)((uint)((i1 & 0x0003f000) << 12) | - (uint)((i1 & 0x0000003f) << 16) | - ((uint)(i2 & 0x0003f000) >> 4) | - (uint)(i2 & 0x0000003f)); - } - - return newKey; - } - - /// - /// Validates the key. - /// - protected virtual void ValidateKey() - { - var keySize = this.Key.Length * 8; - - if (!(keySize == 64)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - } - - /// - /// Performs DES function. - /// - /// The w key. - /// The input. - /// The in off. - /// The out bytes. - /// The out off. - protected static void DesFunc(int[] wKey, byte[] input, int inOff, byte[] outBytes, int outOff) - { - uint left = BigEndianToUInt32(input, inOff); - uint right = BigEndianToUInt32(input, inOff + 4); - uint work; - - 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); - work = (left ^ right) & 0xaaaaaaaa; - left ^= work; - right ^= work; - left = (left << 1) | (left >> 31); - - for (int round = 0; round < 8; round++) - { - uint fval; - - work = (right << 28) | (right >> 4); - work ^= (uint)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 ^ (uint)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 ^= (uint)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 ^ (uint)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); - - UInt32ToBigEndian(right, outBytes, outOff); - UInt32ToBigEndian(left, outBytes, outOff + 4); - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Implements DES cipher algorithm. + /// + public class DesCipher : BlockCipher + { + private int[] _encryptionKey; + + private int[] _decryptionKey; + + #region Static tables + + private static readonly short[] bytebit = + { + 128, 64, 32, 16, 8, 4, 2, 1 + }; + + private static readonly 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). + */ + private static readonly 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 + }; + + private static readonly byte[] totrot = + { + 1, 2, 4, 6, 8, 10, 12, 14, + 15, 17, 19, 21, 23, 25, 27, 28 + }; + + private static readonly 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 + }; + + private static readonly uint[] 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 + }; + + private static readonly uint[] 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 + }; + + private static readonly uint[] 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 + }; + + private static readonly uint[] 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 + }; + + private static readonly uint[] 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 + }; + + private static readonly uint[] 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 + }; + + private static readonly uint[] 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 + }; + + private static readonly uint[] 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 + }; + + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The mode. + /// The padding. + /// is null. + public DesCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 8, mode, padding) + { + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if ((inputOffset + this.BlockSize) > inputBuffer.Length) + throw new IndexOutOfRangeException("input buffer too short"); + + if ((outputOffset + this.BlockSize) > outputBuffer.Length) + throw new IndexOutOfRangeException("output buffer too short"); + + if (this._encryptionKey == null) + { + this._encryptionKey = GenerateWorkingKey(true, this.Key); + } + + DesFunc(this._encryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); + + return this.BlockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if ((inputOffset + this.BlockSize) > inputBuffer.Length) + throw new IndexOutOfRangeException("input buffer too short"); + + if ((outputOffset + this.BlockSize) > outputBuffer.Length) + throw new IndexOutOfRangeException("output buffer too short"); + + if (this._decryptionKey == null) + { + this._decryptionKey = GenerateWorkingKey(false, this.Key); + } + + DesFunc(this._decryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); + + return this.BlockSize; + } + + /// + /// Generates the working key. + /// + /// if set to true [encrypting]. + /// The key. + /// Generated working key. + protected int[] GenerateWorkingKey(bool encrypting, byte[] key) + { + this.ValidateKey(); + + int[] newKey = new int[32]; + bool[] pc1m = new bool[56]; + bool[] pcr = new bool[56]; + + for (int j = 0; j < 56; j++) + { + int l = pc1[j]; + + pc1m[j] = ((key[(uint)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] = (int) ((uint)((i1 & 0x00fc0000) << 6) | + (uint)((i1 & 0x00000fc0) << 10) | + ((uint)(i2 & 0x00fc0000) >> 10) | + ((uint)(i2 & 0x00000fc0) >> 6)); + + newKey[i + 1] = (int)((uint)((i1 & 0x0003f000) << 12) | + (uint)((i1 & 0x0000003f) << 16) | + ((uint)(i2 & 0x0003f000) >> 4) | + (uint)(i2 & 0x0000003f)); + } + + return newKey; + } + + /// + /// Validates the key. + /// + protected virtual void ValidateKey() + { + var keySize = this.Key.Length * 8; + + if (!(keySize == 64)) + throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + } + + /// + /// Performs DES function. + /// + /// The w key. + /// The input. + /// The in off. + /// The out bytes. + /// The out off. + protected static void DesFunc(int[] wKey, byte[] input, int inOff, byte[] outBytes, int outOff) + { + uint left = BigEndianToUInt32(input, inOff); + uint right = BigEndianToUInt32(input, inOff + 4); + uint work; + + 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); + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = (left << 1) | (left >> 31); + + for (int round = 0; round < 8; round++) + { + uint fval; + + work = (right << 28) | (right >> 4); + work ^= (uint)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 ^ (uint)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 ^= (uint)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 ^ (uint)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); + + UInt32ToBigEndian(right, outBytes, outOff); + UInt32ToBigEndian(left, outBytes, outOff + 4); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs index ecb544e..fff0ba9 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs @@ -1,92 +1,89 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements CBC cipher mode - /// - public class CbcCipherMode : CipherMode - { - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public CbcCipherMode(byte[] iv) - : base(iv) - { - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - for (int i = 0; i < this._blockSize; i++) - { - this.IV[i] ^= inputBuffer[inputOffset + i]; - } - - this.Cipher.EncryptBlock(this.IV, 0, inputCount, outputBuffer, outputOffset); - - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, 0, this.IV.Length); - - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] ^= this.IV[i]; - } - - Buffer.BlockCopy(inputBuffer, inputOffset, this.IV, 0, this.IV.Length); - - return this._blockSize; - } - } -} +using System; +using System.Globalization; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +{ + /// + /// Implements CBC cipher mode + /// + public class CbcCipherMode : CipherMode + { + /// + /// Initializes a new instance of the class. + /// + /// The iv. + public CbcCipherMode(byte[] iv) + : base(iv) + { + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + for (int i = 0; i < this._blockSize; i++) + { + this.IV[i] ^= inputBuffer[inputOffset + i]; + } + + this.Cipher.EncryptBlock(this.IV, 0, inputCount, outputBuffer, outputOffset); + + Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, 0, this.IV.Length); + + + return this._blockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + this.Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); + + for (int i = 0; i < this._blockSize; i++) + { + outputBuffer[outputOffset + i] ^= this.IV[i]; + } + + Buffer.BlockCopy(inputBuffer, inputOffset, this.IV, 0, this.IV.Length); + + return this._blockSize; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs index c40937c..71bfca9 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs @@ -1,96 +1,93 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements CFB cipher mode - /// - public class CfbCipherMode : CipherMode - { - private readonly byte[] _ivOutput; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public CfbCipherMode(byte[] iv) - : base(iv) - { - this._ivOutput = new byte[iv.Length]; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(inputBuffer, inputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - return this._blockSize; - } - } -} +using System; +using System.Globalization; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +{ + /// + /// Implements CFB cipher mode + /// + public class CfbCipherMode : CipherMode + { + private readonly byte[] _ivOutput; + + /// + /// Initializes a new instance of the class. + /// + /// The iv. + public CfbCipherMode(byte[] iv) + : base(iv) + { + this._ivOutput = new byte[iv.Length]; + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); + + for (int i = 0; i < this._blockSize; i++) + { + outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); + } + + Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); + Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); + + return this._blockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); + + Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); + Buffer.BlockCopy(inputBuffer, inputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); + + for (int i = 0; i < this._blockSize; i++) + { + outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); + } + + return this._blockSize; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs index 2f8f7c8..b51fdef 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs @@ -1,96 +1,93 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements CTR cipher mode - /// - public class CtrCipherMode : CipherMode - { - private readonly byte[] _ivOutput; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public CtrCipherMode(byte[] iv) - : base(iv) - { - this._ivOutput = new byte[iv.Length]; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - int j = this.IV.Length; - while (--j >= 0 && ++this.IV[j] == 0) ; - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - int j = this.IV.Length; - while (--j >= 0 && ++this.IV[j] == 0) ; - - return this._blockSize; - } - } -} +using System; +using System.Globalization; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +{ + /// + /// Implements CTR cipher mode + /// + public class CtrCipherMode : CipherMode + { + private readonly byte[] _ivOutput; + + /// + /// Initializes a new instance of the class. + /// + /// The iv. + public CtrCipherMode(byte[] iv) + : base(iv) + { + this._ivOutput = new byte[iv.Length]; + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); + + for (int i = 0; i < this._blockSize; i++) + { + outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); + } + + int j = this.IV.Length; + while (--j >= 0 && ++this.IV[j] == 0) ; + + return this._blockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); + + for (int i = 0; i < this._blockSize; i++) + { + outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); + } + + int j = this.IV.Length; + while (--j >= 0 && ++this.IV[j] == 0) ; + + return this._blockSize; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs index 9ed7621..be320e5 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs @@ -1,99 +1,95 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Security.Cryptography.Ciphers; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements OFB cipher mode - /// - public class OfbCipherMode : CipherMode - { - private readonly byte[] _ivOutput; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public OfbCipherMode(byte[] iv) - : base(iv) - { - this._ivOutput = new byte[iv.Length]; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - return this._blockSize; - } - - - } -} +using System; +using System.Globalization; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes +{ + /// + /// Implements OFB cipher mode + /// + public class OfbCipherMode : CipherMode + { + private readonly byte[] _ivOutput; + + /// + /// Initializes a new instance of the class. + /// + /// The iv. + public OfbCipherMode(byte[] iv) + : base(iv) + { + this._ivOutput = new byte[iv.Length]; + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); + + for (int i = 0; i < this._blockSize; i++) + { + outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); + } + + Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); + Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); + + return this._blockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputBuffer.Length - inputOffset < this._blockSize) + throw new ArgumentException("Invalid input buffer"); + + if (outputBuffer.Length - outputOffset < this._blockSize) + throw new ArgumentException("Invalid output buffer"); + + if (inputCount != this._blockSize) + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); + + this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); + + for (int i = 0; i < this._blockSize; i++) + { + outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); + } + + Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); + Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); + + return this._blockSize; + } + + + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs index fc624be..e83f444 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs @@ -1,35 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings -{ - /// - /// Implements PKCS5 cipher padding - /// - public class PKCS5Padding : CipherPadding - { - /// - /// Transforms the specified input. - /// - /// Size of the block. - /// The input. - /// - /// Padded data array. - /// - public override byte[] Pad(int blockSize, byte[] input) - { - var numOfPaddedBytes = blockSize - (input.Length % blockSize); - - var output = new byte[input.Length + numOfPaddedBytes]; - Buffer.BlockCopy(input, 0, output, 0, input.Length); - for (int i = 0; i < numOfPaddedBytes; i++) - { - output[input.Length + i] = (byte)numOfPaddedBytes; - } - - return output; - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings +{ + /// + /// Implements PKCS5 cipher padding + /// + public class PKCS5Padding : CipherPadding + { + /// + /// Transforms the specified input. + /// + /// Size of the block. + /// The input. + /// + /// Padded data array. + /// + public override byte[] Pad(int blockSize, byte[] input) + { + var numOfPaddedBytes = blockSize - (input.Length % blockSize); + + var output = new byte[input.Length + numOfPaddedBytes]; + Buffer.BlockCopy(input, 0, output, 0, input.Length); + for (int i = 0; i < numOfPaddedBytes; i++) + { + output[input.Length + i] = (byte)numOfPaddedBytes; + } + + return output; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs index 578edf0..372ab3f 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs @@ -1,35 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings -{ - /// - /// Implements PKCS7 cipher padding - /// - public class PKCS7Padding : CipherPadding - { - /// - /// Transforms the specified input. - /// - /// Size of the block. - /// The input. - /// - /// Padded data array. - /// - public override byte[] Pad(int blockSize, byte[] input) - { - var numOfPaddedBytes = blockSize - (input.Length % blockSize); - - var output = new byte[input.Length + numOfPaddedBytes]; - Buffer.BlockCopy(input, 0, output, 0, input.Length); - for (int i = 0; i < numOfPaddedBytes; i++) - { - output[input.Length + i] = output[input.Length - 1]; - } - - return output; - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings +{ + /// + /// Implements PKCS7 cipher padding + /// + public class PKCS7Padding : CipherPadding + { + /// + /// Transforms the specified input. + /// + /// Size of the block. + /// The input. + /// + /// Padded data array. + /// + public override byte[] Pad(int blockSize, byte[] input) + { + var numOfPaddedBytes = blockSize - (input.Length % blockSize); + + var output = new byte[input.Length + numOfPaddedBytes]; + Buffer.BlockCopy(input, 0, output, 0, input.Length); + for (int i = 0; i < numOfPaddedBytes; i++) + { + output[input.Length + i] = (byte)numOfPaddedBytes; + } + + return output; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs index 7a9bab2..c80b135 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs @@ -1,133 +1,130 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements RSA cipher algorithm. - /// - public class RsaCipher : AsymmetricCipher - { - private bool _isPrivate; - - private RsaKey _key; - - /// - /// Initializes a new instance of the class. - /// - /// The RSA key. - public RsaCipher(RsaKey key) - { - if (key == null) - throw new ArgumentNullException("key"); - - this._key = key; - - this._isPrivate = !this._key.D.IsZero; - } - - /// - /// Encrypts the specified data. - /// - /// The data. - /// Encrypted data. - public override byte[] Encrypt(byte[] data) - { - // Calculate signature - var bitLength = this._key.Modulus.BitLength; - - var paddedBlock = new byte[bitLength / 8 + (bitLength % 8 > 0 ? 1 : 0) - 1]; - - paddedBlock[0] = 0x01; - for (int i = 1; i < paddedBlock.Length - data.Length - 1; i++) - { - paddedBlock[i] = 0xFF; - } - - Buffer.BlockCopy(data, 0, paddedBlock, paddedBlock.Length - data.Length, data.Length); - - return this.Transform(paddedBlock); - } - - /// - /// Decrypts the specified data. - /// - /// The data. - /// - /// Decrypted data. - /// - /// Only block type 01 or 02 are supported. - /// Thrown when decrypted block type is not supported. - public override byte[] Decrypt(byte[] data) - { - var paddedBlock = this.Transform(data); - - if (paddedBlock[0] != 1 && paddedBlock[0] != 2) - throw new NotSupportedException("Only block type 01 or 02 are supported."); - - var position = 1; - while (position < paddedBlock.Length && paddedBlock[position] != 0) - position++; - position++; - - var result = new byte[paddedBlock.Length - position]; - - Buffer.BlockCopy(paddedBlock, position, result, 0, result.Length); - - return result; - } - - private byte[] Transform(byte[] data) - { - var bytes = new List(data.Reverse()); - bytes.Add(0); - - var input = new BigInteger(bytes.ToArray()); - - BigInteger result; - - if (this._isPrivate) - { - BigInteger random = BigInteger.One; - - var max = this._key.Modulus - 1; - - var bitLength = this._key.Modulus.BitLength; - - if (max < BigInteger.One) - throw new SshException("Invalid RSA key."); - - while (random <= BigInteger.One || random >= max) - { - random = BigInteger.Random(bitLength); - } - - BigInteger blindedInput = BigInteger.PositiveMod((BigInteger.ModPow(random, this._key.Exponent, this._key.Modulus) * input), this._key.Modulus); - - // mP = ((input Mod p) ^ dP)) Mod p - var mP = BigInteger.ModPow((blindedInput % this._key.P), this._key.DP, this._key.P); - - // mQ = ((input Mod q) ^ dQ)) Mod q - var mQ = BigInteger.ModPow((blindedInput % this._key.Q), this._key.DQ, this._key.Q); - - var h = BigInteger.PositiveMod(((mP - mQ) * this._key.InverseQ), this._key.P); - - var m = h * this._key.Q + mQ; - - BigInteger rInv = BigInteger.ModInverse(random, this._key.Modulus); - - result = BigInteger.PositiveMod((m * rInv), this._key.Modulus); - } - else - { - result = BigInteger.ModPow(input, this._key.Exponent, this._key.Modulus); - } - - return result.ToByteArray().Reverse().ToArray(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Implements RSA cipher algorithm. + /// + public class RsaCipher : AsymmetricCipher + { + private readonly bool _isPrivate; + + private readonly RsaKey _key; + + /// + /// Initializes a new instance of the class. + /// + /// The RSA key. + public RsaCipher(RsaKey key) + { + if (key == null) + throw new ArgumentNullException("key"); + + this._key = key; + this._isPrivate = !this._key.D.IsZero; + } + + /// + /// Encrypts the specified data. + /// + /// The data. + /// Encrypted data. + public override byte[] Encrypt(byte[] data) + { + // Calculate signature + var bitLength = this._key.Modulus.BitLength; + + var paddedBlock = new byte[bitLength / 8 + (bitLength % 8 > 0 ? 1 : 0) - 1]; + + paddedBlock[0] = 0x01; + for (int i = 1; i < paddedBlock.Length - data.Length - 1; i++) + { + paddedBlock[i] = 0xFF; + } + + Buffer.BlockCopy(data, 0, paddedBlock, paddedBlock.Length - data.Length, data.Length); + + return this.Transform(paddedBlock); + } + + /// + /// Decrypts the specified data. + /// + /// The data. + /// + /// Decrypted data. + /// + /// Only block type 01 or 02 are supported. + /// Thrown when decrypted block type is not supported. + public override byte[] Decrypt(byte[] data) + { + var paddedBlock = this.Transform(data); + + if (paddedBlock[0] != 1 && paddedBlock[0] != 2) + throw new NotSupportedException("Only block type 01 or 02 are supported."); + + var position = 1; + while (position < paddedBlock.Length && paddedBlock[position] != 0) + position++; + position++; + + var result = new byte[paddedBlock.Length - position]; + + Buffer.BlockCopy(paddedBlock, position, result, 0, result.Length); + + return result; + } + + private byte[] Transform(byte[] data) + { + var bytes = new List(data.Reverse()); + bytes.Add(0); + + var input = new BigInteger(bytes.ToArray()); + + BigInteger result; + + if (this._isPrivate) + { + BigInteger random = BigInteger.One; + + var max = this._key.Modulus - 1; + + var bitLength = this._key.Modulus.BitLength; + + if (max < BigInteger.One) + throw new SshException("Invalid RSA key."); + + while (random <= BigInteger.One || random >= max) + { + random = BigInteger.Random(bitLength); + } + + BigInteger blindedInput = BigInteger.PositiveMod((BigInteger.ModPow(random, this._key.Exponent, this._key.Modulus) * input), this._key.Modulus); + + // mP = ((input Mod p) ^ dP)) Mod p + var mP = BigInteger.ModPow((blindedInput % this._key.P), this._key.DP, this._key.P); + + // mQ = ((input Mod q) ^ dQ)) Mod q + var mQ = BigInteger.ModPow((blindedInput % this._key.Q), this._key.DQ, this._key.Q); + + var h = BigInteger.PositiveMod(((mP - mQ) * this._key.InverseQ), this._key.P); + + var m = h * this._key.Q + mQ; + + BigInteger rInv = BigInteger.ModInverse(random, this._key.Modulus); + + result = BigInteger.PositiveMod((m * rInv), this._key.Modulus); + } + else + { + result = BigInteger.ModPow(input, this._key.Exponent, this._key.Modulus); + } + + return result.ToByteArray().Reverse().ToArray(); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs index 810d995..5f58320 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs @@ -1,772 +1,769 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements Serpent cipher algorithm. - /// - public sealed class SerpentCipher : BlockCipher - { - private static readonly int ROUNDS = 32; - - private static readonly int PHI = unchecked((int)0x9E3779B9); // (Sqrt(5) - 1) * 2**31 - - private int[] _workingKey; - - private int _x0, _x1, _x2, _x3; // registers - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public SerpentCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize == 128 || keySize == 192 || keySize == 256)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - this._workingKey = this.MakeWorkingKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - this._x3 = BytesToWord(inputBuffer, inputOffset); - this._x2 = BytesToWord(inputBuffer, inputOffset + 4); - this._x1 = BytesToWord(inputBuffer, inputOffset + 8); - this._x0 = BytesToWord(inputBuffer, inputOffset + 12); - - Sb0(this._workingKey[0] ^ this._x0, this._workingKey[1] ^ this._x1, this._workingKey[2] ^ this._x2, this._workingKey[3] ^ this._x3); LT(); - Sb1(this._workingKey[4] ^ this._x0, this._workingKey[5] ^ this._x1, this._workingKey[6] ^ this._x2, this._workingKey[7] ^ this._x3); LT(); - Sb2(this._workingKey[8] ^ this._x0, this._workingKey[9] ^ this._x1, this._workingKey[10] ^ this._x2, this._workingKey[11] ^ this._x3); LT(); - Sb3(this._workingKey[12] ^ this._x0, this._workingKey[13] ^ this._x1, this._workingKey[14] ^ this._x2, this._workingKey[15] ^ this._x3); LT(); - Sb4(this._workingKey[16] ^ this._x0, this._workingKey[17] ^ this._x1, this._workingKey[18] ^ this._x2, this._workingKey[19] ^ this._x3); LT(); - Sb5(this._workingKey[20] ^ this._x0, this._workingKey[21] ^ this._x1, this._workingKey[22] ^ this._x2, this._workingKey[23] ^ this._x3); LT(); - Sb6(this._workingKey[24] ^ this._x0, this._workingKey[25] ^ this._x1, this._workingKey[26] ^ this._x2, this._workingKey[27] ^ this._x3); LT(); - Sb7(this._workingKey[28] ^ this._x0, this._workingKey[29] ^ this._x1, this._workingKey[30] ^ this._x2, this._workingKey[31] ^ this._x3); LT(); - Sb0(this._workingKey[32] ^ this._x0, this._workingKey[33] ^ this._x1, this._workingKey[34] ^ this._x2, this._workingKey[35] ^ this._x3); LT(); - Sb1(this._workingKey[36] ^ this._x0, this._workingKey[37] ^ this._x1, this._workingKey[38] ^ this._x2, this._workingKey[39] ^ this._x3); LT(); - Sb2(this._workingKey[40] ^ this._x0, this._workingKey[41] ^ this._x1, this._workingKey[42] ^ this._x2, this._workingKey[43] ^ this._x3); LT(); - Sb3(this._workingKey[44] ^ this._x0, this._workingKey[45] ^ this._x1, this._workingKey[46] ^ this._x2, this._workingKey[47] ^ this._x3); LT(); - Sb4(this._workingKey[48] ^ this._x0, this._workingKey[49] ^ this._x1, this._workingKey[50] ^ this._x2, this._workingKey[51] ^ this._x3); LT(); - Sb5(this._workingKey[52] ^ this._x0, this._workingKey[53] ^ this._x1, this._workingKey[54] ^ this._x2, this._workingKey[55] ^ this._x3); LT(); - Sb6(this._workingKey[56] ^ this._x0, this._workingKey[57] ^ this._x1, this._workingKey[58] ^ this._x2, this._workingKey[59] ^ this._x3); LT(); - Sb7(this._workingKey[60] ^ this._x0, this._workingKey[61] ^ this._x1, this._workingKey[62] ^ this._x2, this._workingKey[63] ^ this._x3); LT(); - Sb0(this._workingKey[64] ^ this._x0, this._workingKey[65] ^ this._x1, this._workingKey[66] ^ this._x2, this._workingKey[67] ^ this._x3); LT(); - Sb1(this._workingKey[68] ^ this._x0, this._workingKey[69] ^ this._x1, this._workingKey[70] ^ this._x2, this._workingKey[71] ^ this._x3); LT(); - Sb2(this._workingKey[72] ^ this._x0, this._workingKey[73] ^ this._x1, this._workingKey[74] ^ this._x2, this._workingKey[75] ^ this._x3); LT(); - Sb3(this._workingKey[76] ^ this._x0, this._workingKey[77] ^ this._x1, this._workingKey[78] ^ this._x2, this._workingKey[79] ^ this._x3); LT(); - Sb4(this._workingKey[80] ^ this._x0, this._workingKey[81] ^ this._x1, this._workingKey[82] ^ this._x2, this._workingKey[83] ^ this._x3); LT(); - Sb5(this._workingKey[84] ^ this._x0, this._workingKey[85] ^ this._x1, this._workingKey[86] ^ this._x2, this._workingKey[87] ^ this._x3); LT(); - Sb6(this._workingKey[88] ^ this._x0, this._workingKey[89] ^ this._x1, this._workingKey[90] ^ this._x2, this._workingKey[91] ^ this._x3); LT(); - Sb7(this._workingKey[92] ^ this._x0, this._workingKey[93] ^ this._x1, this._workingKey[94] ^ this._x2, this._workingKey[95] ^ this._x3); LT(); - Sb0(this._workingKey[96] ^ this._x0, this._workingKey[97] ^ this._x1, this._workingKey[98] ^ this._x2, this._workingKey[99] ^ this._x3); LT(); - Sb1(this._workingKey[100] ^ this._x0, this._workingKey[101] ^ this._x1, this._workingKey[102] ^ this._x2, this._workingKey[103] ^ this._x3); LT(); - Sb2(this._workingKey[104] ^ this._x0, this._workingKey[105] ^ this._x1, this._workingKey[106] ^ this._x2, this._workingKey[107] ^ this._x3); LT(); - Sb3(this._workingKey[108] ^ this._x0, this._workingKey[109] ^ this._x1, this._workingKey[110] ^ this._x2, this._workingKey[111] ^ this._x3); LT(); - Sb4(this._workingKey[112] ^ this._x0, this._workingKey[113] ^ this._x1, this._workingKey[114] ^ this._x2, this._workingKey[115] ^ this._x3); LT(); - Sb5(this._workingKey[116] ^ this._x0, this._workingKey[117] ^ this._x1, this._workingKey[118] ^ this._x2, this._workingKey[119] ^ this._x3); LT(); - Sb6(this._workingKey[120] ^ this._x0, this._workingKey[121] ^ this._x1, this._workingKey[122] ^ this._x2, this._workingKey[123] ^ this._x3); LT(); - Sb7(this._workingKey[124] ^ this._x0, this._workingKey[125] ^ this._x1, this._workingKey[126] ^ this._x2, this._workingKey[127] ^ this._x3); - - WordToBytes(this._workingKey[131] ^ this._x3, outputBuffer, outputOffset); - WordToBytes(this._workingKey[130] ^ this._x2, outputBuffer, outputOffset + 4); - WordToBytes(this._workingKey[129] ^ this._x1, outputBuffer, outputOffset + 8); - WordToBytes(this._workingKey[128] ^ this._x0, outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - this._x3 = this._workingKey[131] ^ BytesToWord(inputBuffer, inputOffset); - this._x2 = this._workingKey[130] ^ BytesToWord(inputBuffer, inputOffset + 4); - this._x1 = this._workingKey[129] ^ BytesToWord(inputBuffer, inputOffset + 8); - this._x0 = this._workingKey[128] ^ BytesToWord(inputBuffer, inputOffset + 12); - - Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[124]; this._x1 ^= this._workingKey[125]; this._x2 ^= this._workingKey[126]; this._x3 ^= this._workingKey[127]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[120]; this._x1 ^= this._workingKey[121]; this._x2 ^= this._workingKey[122]; this._x3 ^= this._workingKey[123]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[116]; this._x1 ^= this._workingKey[117]; this._x2 ^= this._workingKey[118]; this._x3 ^= this._workingKey[119]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[112]; this._x1 ^= this._workingKey[113]; this._x2 ^= this._workingKey[114]; this._x3 ^= this._workingKey[115]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[108]; this._x1 ^= this._workingKey[109]; this._x2 ^= this._workingKey[110]; this._x3 ^= this._workingKey[111]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[104]; this._x1 ^= this._workingKey[105]; this._x2 ^= this._workingKey[106]; this._x3 ^= this._workingKey[107]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[100]; this._x1 ^= this._workingKey[101]; this._x2 ^= this._workingKey[102]; this._x3 ^= this._workingKey[103]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[96]; this._x1 ^= this._workingKey[97]; this._x2 ^= this._workingKey[98]; this._x3 ^= this._workingKey[99]; - InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[92]; this._x1 ^= this._workingKey[93]; this._x2 ^= this._workingKey[94]; this._x3 ^= this._workingKey[95]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[88]; this._x1 ^= this._workingKey[89]; this._x2 ^= this._workingKey[90]; this._x3 ^= this._workingKey[91]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[84]; this._x1 ^= this._workingKey[85]; this._x2 ^= this._workingKey[86]; this._x3 ^= this._workingKey[87]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[80]; this._x1 ^= this._workingKey[81]; this._x2 ^= this._workingKey[82]; this._x3 ^= this._workingKey[83]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[76]; this._x1 ^= this._workingKey[77]; this._x2 ^= this._workingKey[78]; this._x3 ^= this._workingKey[79]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[72]; this._x1 ^= this._workingKey[73]; this._x2 ^= this._workingKey[74]; this._x3 ^= this._workingKey[75]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[68]; this._x1 ^= this._workingKey[69]; this._x2 ^= this._workingKey[70]; this._x3 ^= this._workingKey[71]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[64]; this._x1 ^= this._workingKey[65]; this._x2 ^= this._workingKey[66]; this._x3 ^= this._workingKey[67]; - InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[60]; this._x1 ^= this._workingKey[61]; this._x2 ^= this._workingKey[62]; this._x3 ^= this._workingKey[63]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[56]; this._x1 ^= this._workingKey[57]; this._x2 ^= this._workingKey[58]; this._x3 ^= this._workingKey[59]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[52]; this._x1 ^= this._workingKey[53]; this._x2 ^= this._workingKey[54]; this._x3 ^= this._workingKey[55]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[48]; this._x1 ^= this._workingKey[49]; this._x2 ^= this._workingKey[50]; this._x3 ^= this._workingKey[51]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[44]; this._x1 ^= this._workingKey[45]; this._x2 ^= this._workingKey[46]; this._x3 ^= this._workingKey[47]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[40]; this._x1 ^= this._workingKey[41]; this._x2 ^= this._workingKey[42]; this._x3 ^= this._workingKey[43]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[36]; this._x1 ^= this._workingKey[37]; this._x2 ^= this._workingKey[38]; this._x3 ^= this._workingKey[39]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[32]; this._x1 ^= this._workingKey[33]; this._x2 ^= this._workingKey[34]; this._x3 ^= this._workingKey[35]; - InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[28]; this._x1 ^= this._workingKey[29]; this._x2 ^= this._workingKey[30]; this._x3 ^= this._workingKey[31]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[24]; this._x1 ^= this._workingKey[25]; this._x2 ^= this._workingKey[26]; this._x3 ^= this._workingKey[27]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[20]; this._x1 ^= this._workingKey[21]; this._x2 ^= this._workingKey[22]; this._x3 ^= this._workingKey[23]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[16]; this._x1 ^= this._workingKey[17]; this._x2 ^= this._workingKey[18]; this._x3 ^= this._workingKey[19]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[12]; this._x1 ^= this._workingKey[13]; this._x2 ^= this._workingKey[14]; this._x3 ^= this._workingKey[15]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[8]; this._x1 ^= this._workingKey[9]; this._x2 ^= this._workingKey[10]; this._x3 ^= this._workingKey[11]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[4]; this._x1 ^= this._workingKey[5]; this._x2 ^= this._workingKey[6]; this._x3 ^= this._workingKey[7]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - - WordToBytes(this._x3 ^ this._workingKey[3], outputBuffer, outputOffset); - WordToBytes(this._x2 ^ this._workingKey[2], outputBuffer, outputOffset + 4); - WordToBytes(this._x1 ^ this._workingKey[1], outputBuffer, outputOffset + 8); - WordToBytes(this._x0 ^ this._workingKey[0], outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - /** - * Expand a user-supplied key material into a session key. - * - * @param key The user-key bytes (multiples of 4) to use. - * @exception ArgumentException - */ - private int[] MakeWorkingKey(byte[] key) - { - // - // pad key to 256 bits - // - int[] kPad = new int[16]; - int off = 0; - int length = 0; - - for (off = key.Length - 4; off > 0; off -= 4) - { - kPad[length++] = BytesToWord(key, off); - } - - if (off == 0) - { - kPad[length++] = BytesToWord(key, 0); - if (length < 8) - { - kPad[length] = 1; - } - } - else - { - throw new ArgumentException("key must be a multiple of 4 bytes"); - } - - // - // expand the padded key up to 33 x 128 bits of key material - // - int amount = (ROUNDS + 1) * 4; - int[] w = new int[amount]; - - // - // compute w0 to w7 from w-8 to w-1 - // - for (int i = 8; i < 16; i++) - { - kPad[i] = RotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11); - } - - Buffer.BlockCopy(kPad, 8, w, 0, 8); - - // - // compute w8 to w136 - // - for (int i = 8; i < amount; i++) - { - w[i] = RotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11); - } - - // - // create the working keys by processing w with the Sbox and IP - // - Sb3(w[0], w[1], w[2], w[3]); - w[0] = this._x0; w[1] = this._x1; w[2] = this._x2; w[3] = this._x3; - Sb2(w[4], w[5], w[6], w[7]); - w[4] = this._x0; w[5] = this._x1; w[6] = this._x2; w[7] = this._x3; - Sb1(w[8], w[9], w[10], w[11]); - w[8] = this._x0; w[9] = this._x1; w[10] = this._x2; w[11] = this._x3; - Sb0(w[12], w[13], w[14], w[15]); - w[12] = this._x0; w[13] = this._x1; w[14] = this._x2; w[15] = this._x3; - Sb7(w[16], w[17], w[18], w[19]); - w[16] = this._x0; w[17] = this._x1; w[18] = this._x2; w[19] = this._x3; - Sb6(w[20], w[21], w[22], w[23]); - w[20] = this._x0; w[21] = this._x1; w[22] = this._x2; w[23] = this._x3; - Sb5(w[24], w[25], w[26], w[27]); - w[24] = this._x0; w[25] = this._x1; w[26] = this._x2; w[27] = this._x3; - Sb4(w[28], w[29], w[30], w[31]); - w[28] = this._x0; w[29] = this._x1; w[30] = this._x2; w[31] = this._x3; - Sb3(w[32], w[33], w[34], w[35]); - w[32] = this._x0; w[33] = this._x1; w[34] = this._x2; w[35] = this._x3; - Sb2(w[36], w[37], w[38], w[39]); - w[36] = this._x0; w[37] = this._x1; w[38] = this._x2; w[39] = this._x3; - Sb1(w[40], w[41], w[42], w[43]); - w[40] = this._x0; w[41] = this._x1; w[42] = this._x2; w[43] = this._x3; - Sb0(w[44], w[45], w[46], w[47]); - w[44] = this._x0; w[45] = this._x1; w[46] = this._x2; w[47] = this._x3; - Sb7(w[48], w[49], w[50], w[51]); - w[48] = this._x0; w[49] = this._x1; w[50] = this._x2; w[51] = this._x3; - Sb6(w[52], w[53], w[54], w[55]); - w[52] = this._x0; w[53] = this._x1; w[54] = this._x2; w[55] = this._x3; - Sb5(w[56], w[57], w[58], w[59]); - w[56] = this._x0; w[57] = this._x1; w[58] = this._x2; w[59] = this._x3; - Sb4(w[60], w[61], w[62], w[63]); - w[60] = this._x0; w[61] = this._x1; w[62] = this._x2; w[63] = this._x3; - Sb3(w[64], w[65], w[66], w[67]); - w[64] = this._x0; w[65] = this._x1; w[66] = this._x2; w[67] = this._x3; - Sb2(w[68], w[69], w[70], w[71]); - w[68] = this._x0; w[69] = this._x1; w[70] = this._x2; w[71] = this._x3; - Sb1(w[72], w[73], w[74], w[75]); - w[72] = this._x0; w[73] = this._x1; w[74] = this._x2; w[75] = this._x3; - Sb0(w[76], w[77], w[78], w[79]); - w[76] = this._x0; w[77] = this._x1; w[78] = this._x2; w[79] = this._x3; - Sb7(w[80], w[81], w[82], w[83]); - w[80] = this._x0; w[81] = this._x1; w[82] = this._x2; w[83] = this._x3; - Sb6(w[84], w[85], w[86], w[87]); - w[84] = this._x0; w[85] = this._x1; w[86] = this._x2; w[87] = this._x3; - Sb5(w[88], w[89], w[90], w[91]); - w[88] = this._x0; w[89] = this._x1; w[90] = this._x2; w[91] = this._x3; - Sb4(w[92], w[93], w[94], w[95]); - w[92] = this._x0; w[93] = this._x1; w[94] = this._x2; w[95] = this._x3; - Sb3(w[96], w[97], w[98], w[99]); - w[96] = this._x0; w[97] = this._x1; w[98] = this._x2; w[99] = this._x3; - Sb2(w[100], w[101], w[102], w[103]); - w[100] = this._x0; w[101] = this._x1; w[102] = this._x2; w[103] = this._x3; - Sb1(w[104], w[105], w[106], w[107]); - w[104] = this._x0; w[105] = this._x1; w[106] = this._x2; w[107] = this._x3; - Sb0(w[108], w[109], w[110], w[111]); - w[108] = this._x0; w[109] = this._x1; w[110] = this._x2; w[111] = this._x3; - Sb7(w[112], w[113], w[114], w[115]); - w[112] = this._x0; w[113] = this._x1; w[114] = this._x2; w[115] = this._x3; - Sb6(w[116], w[117], w[118], w[119]); - w[116] = this._x0; w[117] = this._x1; w[118] = this._x2; w[119] = this._x3; - Sb5(w[120], w[121], w[122], w[123]); - w[120] = this._x0; w[121] = this._x1; w[122] = this._x2; w[123] = this._x3; - Sb4(w[124], w[125], w[126], w[127]); - w[124] = this._x0; w[125] = this._x1; w[126] = this._x2; w[127] = this._x3; - Sb3(w[128], w[129], w[130], w[131]); - w[128] = this._x0; w[129] = this._x1; w[130] = this._x2; w[131] = this._x3; - - return w; - } - - private static int RotateLeft(int x, int bits) - { - return ((x << bits) | (int)((uint)x >> (32 - bits))); - } - - private static int RotateRight(int x, int bits) - { - return ((int)((uint)x >> bits) | (x << (32 - bits))); - } - - private static int BytesToWord(byte[] src, int srcOff) - { - return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) | - ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff))); - } - - private static void WordToBytes(int word, byte[] dst, int dstOff) - { - dst[dstOff + 3] = (byte)(word); - dst[dstOff + 2] = (byte)((uint)word >> 8); - dst[dstOff + 1] = (byte)((uint)word >> 16); - dst[dstOff] = (byte)((uint)word >> 24); - } - - /* - * The sboxes below are based on the work of Brian Gladman and - * Sam Simpson, whose original notice appears below. - *

- * For further details see: - * http://fp.gladman.plus.com/cryptography_technology/serpent/ - *

- */ - - /* Partially optimised Serpent S Box bool functions derived */ - /* using a recursive descent analyser but without a full search */ - /* of all subtrees. This set of S boxes is the result of work */ - /* by Sam Simpson and Brian Gladman using the spare time on a */ - /* cluster of high capacity servers to search for S boxes with */ - /* this customised search engine. There are now an average of */ - /* 15.375 terms per S box. */ - /* */ - /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */ - /* and Sam Simpson (s.simpson@mia.co.uk) */ - /* 17th December 1998 */ - /* */ - /* We hereby give permission for information in this file to be */ - /* used freely subject only to acknowledgement of its origin. */ - - /// - /// S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb0(int a, int b, int c, int d) - { - int t1 = a ^ d; - int t3 = c ^ t1; - int t4 = b ^ t3; - this._x3 = (a & d) ^ t4; - int t7 = a ^ (b & t1); - this._x2 = t4 ^ (c | t7); - int t12 = this._x3 & (t3 ^ t7); - this._x1 = (~t3) ^ t12; - this._x0 = t12 ^ (~t7); - } - - /// - /// InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib0(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ b; - int t4 = d ^ (t1 | t2); - int t5 = c ^ t4; - this._x2 = t2 ^ t5; - int t8 = t1 ^ (d & t2); - this._x1 = t4 ^ (this._x2 & t8); - this._x3 = (a & t4) ^ (t5 | this._x1); - this._x0 = this._x3 ^ (t5 ^ t8); - } - - /// - /// S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb1(int a, int b, int c, int d) - { - int t2 = b ^ (~a); - int t5 = c ^ (a | t2); - this._x2 = d ^ t5; - int t7 = b ^ (d | t2); - int t8 = t2 ^ this._x2; - this._x3 = t8 ^ (t5 & t7); - int t11 = t5 ^ t7; - this._x1 = this._x3 ^ t11; - this._x0 = t5 ^ (t8 & t11); - } - - /// - /// InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib1(int a, int b, int c, int d) - { - int t1 = b ^ d; - int t3 = a ^ (b & t1); - int t4 = t1 ^ t3; - this._x3 = c ^ t4; - int t7 = b ^ (t1 & t3); - int t8 = this._x3 | t7; - this._x1 = t3 ^ t8; - int t10 = ~this._x1; - int t11 = this._x3 ^ t7; - this._x0 = t10 ^ t11; - this._x2 = t4 ^ (t10 | t11); - } - - /// - /// S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb2(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = b ^ d; - int t3 = c & t1; - this._x0 = t2 ^ t3; - int t5 = c ^ t1; - int t6 = c ^ this._x0; - int t7 = b & t6; - this._x3 = t5 ^ t7; - this._x2 = a ^ ((d | t7) & (this._x0 | t5)); - this._x1 = (t2 ^ this._x3) ^ (this._x2 ^ (d | t1)); - } - - /// - /// InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib2(int a, int b, int c, int d) - { - int t1 = b ^ d; - int t2 = ~t1; - int t3 = a ^ c; - int t4 = c ^ t1; - int t5 = b & t4; - this._x0 = t3 ^ t5; - int t7 = a | t2; - int t8 = d ^ t7; - int t9 = t3 | t8; - this._x3 = t1 ^ t9; - int t11 = ~t4; - int t12 = this._x0 | this._x3; - this._x1 = t11 ^ t12; - this._x2 = (d & t11) ^ (t3 ^ t12); - } - - /// - /// S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb3(int a, int b, int c, int d) - { - int t1 = a ^ b; - int t2 = a & c; - int t3 = a | d; - int t4 = c ^ d; - int t5 = t1 & t3; - int t6 = t2 | t5; - this._x2 = t4 ^ t6; - int t8 = b ^ t3; - int t9 = t6 ^ t8; - int t10 = t4 & t9; - this._x0 = t1 ^ t10; - int t12 = this._x2 & this._x0; - this._x1 = t9 ^ t12; - this._x3 = (b | d) ^ (t4 ^ t12); - } - - /// - /// InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib3(int a, int b, int c, int d) - { - int t1 = a | b; - int t2 = b ^ c; - int t3 = b & t2; - int t4 = a ^ t3; - int t5 = c ^ t4; - int t6 = d | t4; - this._x0 = t2 ^ t6; - int t8 = t2 | t6; - int t9 = d ^ t8; - this._x2 = t5 ^ t9; - int t11 = t1 ^ t9; - int t12 = this._x0 & t11; - this._x3 = t4 ^ t12; - this._x1 = this._x3 ^ (this._x0 ^ t11); - } - - /// - /// S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb4(int a, int b, int c, int d) - { - int t1 = a ^ d; - int t2 = d & t1; - int t3 = c ^ t2; - int t4 = b | t3; - this._x3 = t1 ^ t4; - int t6 = ~b; - int t7 = t1 | t6; - this._x0 = t3 ^ t7; - int t9 = a & this._x0; - int t10 = t1 ^ t6; - int t11 = t4 & t10; - this._x2 = t9 ^ t11; - this._x1 = (a ^ t3) ^ (t10 & this._x2); - } - - /// - /// InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib4(int a, int b, int c, int d) - { - int t1 = c | d; - int t2 = a & t1; - int t3 = b ^ t2; - int t4 = a & t3; - int t5 = c ^ t4; - this._x1 = d ^ t5; - int t7 = ~a; - int t8 = t5 & this._x1; - this._x3 = t3 ^ t8; - int t10 = this._x1 | t7; - int t11 = d ^ t10; - this._x0 = this._x3 ^ t11; - this._x2 = (t3 & t11) ^ (this._x1 ^ t7); - } - - /// - /// S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb5(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ b; - int t3 = a ^ d; - int t4 = c ^ t1; - int t5 = t2 | t3; - this._x0 = t4 ^ t5; - int t7 = d & this._x0; - int t8 = t2 ^ this._x0; - this._x1 = t7 ^ t8; - int t10 = t1 | this._x0; - int t11 = t2 | t7; - int t12 = t3 ^ t10; - this._x2 = t11 ^ t12; - this._x3 = (b ^ t7) ^ (this._x1 & t12); - } - - /// - /// InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib5(int a, int b, int c, int d) - { - int t1 = ~c; - int t2 = b & t1; - int t3 = d ^ t2; - int t4 = a & t3; - int t5 = b ^ t1; - this._x3 = t4 ^ t5; - int t7 = b | this._x3; - int t8 = a & t7; - this._x1 = t3 ^ t8; - int t10 = a | d; - int t11 = t1 ^ t7; - this._x0 = t10 ^ t11; - this._x2 = (b & t10) ^ (t4 | (a ^ c)); - } - - /// - /// S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb6(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ d; - int t3 = b ^ t2; - int t4 = t1 | t2; - int t5 = c ^ t4; - this._x1 = b ^ t5; - int t7 = t2 | this._x1; - int t8 = d ^ t7; - int t9 = t5 & t8; - this._x2 = t3 ^ t9; - int t11 = t5 ^ t8; - this._x0 = this._x2 ^ t11; - this._x3 = (~t5) ^ (t3 & t11); - } - - /// - /// InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib6(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ b; - int t3 = c ^ t2; - int t4 = c | t1; - int t5 = d ^ t4; - this._x1 = t3 ^ t5; - int t7 = t3 & t5; - int t8 = t2 ^ t7; - int t9 = b | t8; - this._x3 = t5 ^ t9; - int t11 = b | this._x3; - this._x0 = t8 ^ t11; - this._x2 = (d & t1) ^ (t3 ^ t11); - } - - /// - /// S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb7(int a, int b, int c, int d) - { - int t1 = b ^ c; - int t2 = c & t1; - int t3 = d ^ t2; - int t4 = a ^ t3; - int t5 = d | t1; - int t6 = t4 & t5; - this._x1 = b ^ t6; - int t8 = t3 | this._x1; - int t9 = a & t4; - this._x3 = t1 ^ t9; - int t11 = t4 ^ t8; - int t12 = this._x3 & t11; - this._x2 = t3 ^ t12; - this._x0 = (~t11) ^ (this._x3 & this._x2); - } - - /// - /// InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib7(int a, int b, int c, int d) - { - int t3 = c | (a & b); - int t4 = d & (a | b); - this._x3 = t3 ^ t4; - int t6 = ~d; - int t7 = b ^ t4; - int t9 = t7 | (this._x3 ^ t6); - this._x1 = a ^ t9; - this._x0 = (c ^ t7) ^ (d | this._x1); - this._x2 = (t3 ^ this._x1) ^ (this._x0 ^ (a & this._x3)); - } - - /// - /// Apply the linear transformation to the register set. - /// - private void LT() - { - int x0 = RotateLeft(this._x0, 13); - int x2 = RotateLeft(this._x2, 3); - int x1 = this._x1 ^ x0 ^ x2; - int x3 = this._x3 ^ x2 ^ x0 << 3; - - this._x1 = RotateLeft(x1, 1); - this._x3 = RotateLeft(x3, 7); - this._x0 = RotateLeft(x0 ^ this._x1 ^ this._x3, 5); - this._x2 = RotateLeft(x2 ^ this._x3 ^ (this._x1 << 7), 22); - } - - /// - /// Apply the inverse of the linear transformation to the register set. - /// - private void InverseLT() - { - int x2 = RotateRight(this._x2, 22) ^ this._x3 ^ (this._x1 << 7); - int x0 = RotateRight(this._x0, 5) ^ this._x1 ^ this._x3; - int x3 = RotateRight(this._x3, 7); - int x1 = RotateRight(this._x1, 1); - this._x3 = x3 ^ x2 ^ x0 << 3; - this._x1 = x1 ^ x0 ^ x2; - this._x2 = RotateRight(x2, 3); - this._x0 = RotateRight(x0, 13); - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Implements Serpent cipher algorithm. + /// + public sealed class SerpentCipher : BlockCipher + { + private const int ROUNDS = 32; + + private const int PHI = unchecked((int) 0x9E3779B9); // (Sqrt(5) - 1) * 2**31 + + private readonly int[] _workingKey; + + private int _x0, _x1, _x2, _x3; // registers + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The mode. + /// The padding. + /// is null. + /// Keysize is not valid for this algorithm. + public SerpentCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 16, mode, padding) + { + var keySize = key.Length * 8; + + if (!(keySize == 128 || keySize == 192 || keySize == 256)) + throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + + this._workingKey = this.MakeWorkingKey(key); + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputCount != this.BlockSize) + throw new ArgumentException("inputCount"); + + this._x3 = BytesToWord(inputBuffer, inputOffset); + this._x2 = BytesToWord(inputBuffer, inputOffset + 4); + this._x1 = BytesToWord(inputBuffer, inputOffset + 8); + this._x0 = BytesToWord(inputBuffer, inputOffset + 12); + + Sb0(this._workingKey[0] ^ this._x0, this._workingKey[1] ^ this._x1, this._workingKey[2] ^ this._x2, this._workingKey[3] ^ this._x3); LT(); + Sb1(this._workingKey[4] ^ this._x0, this._workingKey[5] ^ this._x1, this._workingKey[6] ^ this._x2, this._workingKey[7] ^ this._x3); LT(); + Sb2(this._workingKey[8] ^ this._x0, this._workingKey[9] ^ this._x1, this._workingKey[10] ^ this._x2, this._workingKey[11] ^ this._x3); LT(); + Sb3(this._workingKey[12] ^ this._x0, this._workingKey[13] ^ this._x1, this._workingKey[14] ^ this._x2, this._workingKey[15] ^ this._x3); LT(); + Sb4(this._workingKey[16] ^ this._x0, this._workingKey[17] ^ this._x1, this._workingKey[18] ^ this._x2, this._workingKey[19] ^ this._x3); LT(); + Sb5(this._workingKey[20] ^ this._x0, this._workingKey[21] ^ this._x1, this._workingKey[22] ^ this._x2, this._workingKey[23] ^ this._x3); LT(); + Sb6(this._workingKey[24] ^ this._x0, this._workingKey[25] ^ this._x1, this._workingKey[26] ^ this._x2, this._workingKey[27] ^ this._x3); LT(); + Sb7(this._workingKey[28] ^ this._x0, this._workingKey[29] ^ this._x1, this._workingKey[30] ^ this._x2, this._workingKey[31] ^ this._x3); LT(); + Sb0(this._workingKey[32] ^ this._x0, this._workingKey[33] ^ this._x1, this._workingKey[34] ^ this._x2, this._workingKey[35] ^ this._x3); LT(); + Sb1(this._workingKey[36] ^ this._x0, this._workingKey[37] ^ this._x1, this._workingKey[38] ^ this._x2, this._workingKey[39] ^ this._x3); LT(); + Sb2(this._workingKey[40] ^ this._x0, this._workingKey[41] ^ this._x1, this._workingKey[42] ^ this._x2, this._workingKey[43] ^ this._x3); LT(); + Sb3(this._workingKey[44] ^ this._x0, this._workingKey[45] ^ this._x1, this._workingKey[46] ^ this._x2, this._workingKey[47] ^ this._x3); LT(); + Sb4(this._workingKey[48] ^ this._x0, this._workingKey[49] ^ this._x1, this._workingKey[50] ^ this._x2, this._workingKey[51] ^ this._x3); LT(); + Sb5(this._workingKey[52] ^ this._x0, this._workingKey[53] ^ this._x1, this._workingKey[54] ^ this._x2, this._workingKey[55] ^ this._x3); LT(); + Sb6(this._workingKey[56] ^ this._x0, this._workingKey[57] ^ this._x1, this._workingKey[58] ^ this._x2, this._workingKey[59] ^ this._x3); LT(); + Sb7(this._workingKey[60] ^ this._x0, this._workingKey[61] ^ this._x1, this._workingKey[62] ^ this._x2, this._workingKey[63] ^ this._x3); LT(); + Sb0(this._workingKey[64] ^ this._x0, this._workingKey[65] ^ this._x1, this._workingKey[66] ^ this._x2, this._workingKey[67] ^ this._x3); LT(); + Sb1(this._workingKey[68] ^ this._x0, this._workingKey[69] ^ this._x1, this._workingKey[70] ^ this._x2, this._workingKey[71] ^ this._x3); LT(); + Sb2(this._workingKey[72] ^ this._x0, this._workingKey[73] ^ this._x1, this._workingKey[74] ^ this._x2, this._workingKey[75] ^ this._x3); LT(); + Sb3(this._workingKey[76] ^ this._x0, this._workingKey[77] ^ this._x1, this._workingKey[78] ^ this._x2, this._workingKey[79] ^ this._x3); LT(); + Sb4(this._workingKey[80] ^ this._x0, this._workingKey[81] ^ this._x1, this._workingKey[82] ^ this._x2, this._workingKey[83] ^ this._x3); LT(); + Sb5(this._workingKey[84] ^ this._x0, this._workingKey[85] ^ this._x1, this._workingKey[86] ^ this._x2, this._workingKey[87] ^ this._x3); LT(); + Sb6(this._workingKey[88] ^ this._x0, this._workingKey[89] ^ this._x1, this._workingKey[90] ^ this._x2, this._workingKey[91] ^ this._x3); LT(); + Sb7(this._workingKey[92] ^ this._x0, this._workingKey[93] ^ this._x1, this._workingKey[94] ^ this._x2, this._workingKey[95] ^ this._x3); LT(); + Sb0(this._workingKey[96] ^ this._x0, this._workingKey[97] ^ this._x1, this._workingKey[98] ^ this._x2, this._workingKey[99] ^ this._x3); LT(); + Sb1(this._workingKey[100] ^ this._x0, this._workingKey[101] ^ this._x1, this._workingKey[102] ^ this._x2, this._workingKey[103] ^ this._x3); LT(); + Sb2(this._workingKey[104] ^ this._x0, this._workingKey[105] ^ this._x1, this._workingKey[106] ^ this._x2, this._workingKey[107] ^ this._x3); LT(); + Sb3(this._workingKey[108] ^ this._x0, this._workingKey[109] ^ this._x1, this._workingKey[110] ^ this._x2, this._workingKey[111] ^ this._x3); LT(); + Sb4(this._workingKey[112] ^ this._x0, this._workingKey[113] ^ this._x1, this._workingKey[114] ^ this._x2, this._workingKey[115] ^ this._x3); LT(); + Sb5(this._workingKey[116] ^ this._x0, this._workingKey[117] ^ this._x1, this._workingKey[118] ^ this._x2, this._workingKey[119] ^ this._x3); LT(); + Sb6(this._workingKey[120] ^ this._x0, this._workingKey[121] ^ this._x1, this._workingKey[122] ^ this._x2, this._workingKey[123] ^ this._x3); LT(); + Sb7(this._workingKey[124] ^ this._x0, this._workingKey[125] ^ this._x1, this._workingKey[126] ^ this._x2, this._workingKey[127] ^ this._x3); + + WordToBytes(this._workingKey[131] ^ this._x3, outputBuffer, outputOffset); + WordToBytes(this._workingKey[130] ^ this._x2, outputBuffer, outputOffset + 4); + WordToBytes(this._workingKey[129] ^ this._x1, outputBuffer, outputOffset + 8); + WordToBytes(this._workingKey[128] ^ this._x0, outputBuffer, outputOffset + 12); + + return this.BlockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if (inputCount != this.BlockSize) + throw new ArgumentException("inputCount"); + + this._x3 = this._workingKey[131] ^ BytesToWord(inputBuffer, inputOffset); + this._x2 = this._workingKey[130] ^ BytesToWord(inputBuffer, inputOffset + 4); + this._x1 = this._workingKey[129] ^ BytesToWord(inputBuffer, inputOffset + 8); + this._x0 = this._workingKey[128] ^ BytesToWord(inputBuffer, inputOffset + 12); + + Ib7(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[124]; this._x1 ^= this._workingKey[125]; this._x2 ^= this._workingKey[126]; this._x3 ^= this._workingKey[127]; + InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[120]; this._x1 ^= this._workingKey[121]; this._x2 ^= this._workingKey[122]; this._x3 ^= this._workingKey[123]; + InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[116]; this._x1 ^= this._workingKey[117]; this._x2 ^= this._workingKey[118]; this._x3 ^= this._workingKey[119]; + InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[112]; this._x1 ^= this._workingKey[113]; this._x2 ^= this._workingKey[114]; this._x3 ^= this._workingKey[115]; + InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[108]; this._x1 ^= this._workingKey[109]; this._x2 ^= this._workingKey[110]; this._x3 ^= this._workingKey[111]; + InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[104]; this._x1 ^= this._workingKey[105]; this._x2 ^= this._workingKey[106]; this._x3 ^= this._workingKey[107]; + InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[100]; this._x1 ^= this._workingKey[101]; this._x2 ^= this._workingKey[102]; this._x3 ^= this._workingKey[103]; + InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[96]; this._x1 ^= this._workingKey[97]; this._x2 ^= this._workingKey[98]; this._x3 ^= this._workingKey[99]; + InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[92]; this._x1 ^= this._workingKey[93]; this._x2 ^= this._workingKey[94]; this._x3 ^= this._workingKey[95]; + InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[88]; this._x1 ^= this._workingKey[89]; this._x2 ^= this._workingKey[90]; this._x3 ^= this._workingKey[91]; + InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[84]; this._x1 ^= this._workingKey[85]; this._x2 ^= this._workingKey[86]; this._x3 ^= this._workingKey[87]; + InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[80]; this._x1 ^= this._workingKey[81]; this._x2 ^= this._workingKey[82]; this._x3 ^= this._workingKey[83]; + InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[76]; this._x1 ^= this._workingKey[77]; this._x2 ^= this._workingKey[78]; this._x3 ^= this._workingKey[79]; + InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[72]; this._x1 ^= this._workingKey[73]; this._x2 ^= this._workingKey[74]; this._x3 ^= this._workingKey[75]; + InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[68]; this._x1 ^= this._workingKey[69]; this._x2 ^= this._workingKey[70]; this._x3 ^= this._workingKey[71]; + InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[64]; this._x1 ^= this._workingKey[65]; this._x2 ^= this._workingKey[66]; this._x3 ^= this._workingKey[67]; + InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[60]; this._x1 ^= this._workingKey[61]; this._x2 ^= this._workingKey[62]; this._x3 ^= this._workingKey[63]; + InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[56]; this._x1 ^= this._workingKey[57]; this._x2 ^= this._workingKey[58]; this._x3 ^= this._workingKey[59]; + InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[52]; this._x1 ^= this._workingKey[53]; this._x2 ^= this._workingKey[54]; this._x3 ^= this._workingKey[55]; + InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[48]; this._x1 ^= this._workingKey[49]; this._x2 ^= this._workingKey[50]; this._x3 ^= this._workingKey[51]; + InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[44]; this._x1 ^= this._workingKey[45]; this._x2 ^= this._workingKey[46]; this._x3 ^= this._workingKey[47]; + InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[40]; this._x1 ^= this._workingKey[41]; this._x2 ^= this._workingKey[42]; this._x3 ^= this._workingKey[43]; + InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[36]; this._x1 ^= this._workingKey[37]; this._x2 ^= this._workingKey[38]; this._x3 ^= this._workingKey[39]; + InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[32]; this._x1 ^= this._workingKey[33]; this._x2 ^= this._workingKey[34]; this._x3 ^= this._workingKey[35]; + InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[28]; this._x1 ^= this._workingKey[29]; this._x2 ^= this._workingKey[30]; this._x3 ^= this._workingKey[31]; + InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[24]; this._x1 ^= this._workingKey[25]; this._x2 ^= this._workingKey[26]; this._x3 ^= this._workingKey[27]; + InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[20]; this._x1 ^= this._workingKey[21]; this._x2 ^= this._workingKey[22]; this._x3 ^= this._workingKey[23]; + InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[16]; this._x1 ^= this._workingKey[17]; this._x2 ^= this._workingKey[18]; this._x3 ^= this._workingKey[19]; + InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[12]; this._x1 ^= this._workingKey[13]; this._x2 ^= this._workingKey[14]; this._x3 ^= this._workingKey[15]; + InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[8]; this._x1 ^= this._workingKey[9]; this._x2 ^= this._workingKey[10]; this._x3 ^= this._workingKey[11]; + InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); + this._x0 ^= this._workingKey[4]; this._x1 ^= this._workingKey[5]; this._x2 ^= this._workingKey[6]; this._x3 ^= this._workingKey[7]; + InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); + + WordToBytes(this._x3 ^ this._workingKey[3], outputBuffer, outputOffset); + WordToBytes(this._x2 ^ this._workingKey[2], outputBuffer, outputOffset + 4); + WordToBytes(this._x1 ^ this._workingKey[1], outputBuffer, outputOffset + 8); + WordToBytes(this._x0 ^ this._workingKey[0], outputBuffer, outputOffset + 12); + + return this.BlockSize; + } + + /** + * Expand a user-supplied key material into a session key. + * + * @param key The user-key bytes (multiples of 4) to use. + * @exception ArgumentException + */ + private int[] MakeWorkingKey(byte[] key) + { + // + // pad key to 256 bits + // + int[] kPad = new int[16]; + int off; + int length = 0; + + for (off = key.Length - 4; off > 0; off -= 4) + { + kPad[length++] = BytesToWord(key, off); + } + + if (off == 0) + { + kPad[length++] = BytesToWord(key, 0); + if (length < 8) + { + kPad[length] = 1; + } + } + else + { + throw new ArgumentException("key must be a multiple of 4 bytes"); + } + + // + // expand the padded key up to 33 x 128 bits of key material + // + int amount = (ROUNDS + 1) * 4; + int[] w = new int[amount]; + + // + // compute w0 to w7 from w-8 to w-1 + // + for (int i = 8; i < 16; i++) + { + kPad[i] = RotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11); + } + + Buffer.BlockCopy(kPad, 8, w, 0, 8); + + // + // compute w8 to w136 + // + for (int i = 8; i < amount; i++) + { + w[i] = RotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11); + } + + // + // create the working keys by processing w with the Sbox and IP + // + Sb3(w[0], w[1], w[2], w[3]); + w[0] = this._x0; w[1] = this._x1; w[2] = this._x2; w[3] = this._x3; + Sb2(w[4], w[5], w[6], w[7]); + w[4] = this._x0; w[5] = this._x1; w[6] = this._x2; w[7] = this._x3; + Sb1(w[8], w[9], w[10], w[11]); + w[8] = this._x0; w[9] = this._x1; w[10] = this._x2; w[11] = this._x3; + Sb0(w[12], w[13], w[14], w[15]); + w[12] = this._x0; w[13] = this._x1; w[14] = this._x2; w[15] = this._x3; + Sb7(w[16], w[17], w[18], w[19]); + w[16] = this._x0; w[17] = this._x1; w[18] = this._x2; w[19] = this._x3; + Sb6(w[20], w[21], w[22], w[23]); + w[20] = this._x0; w[21] = this._x1; w[22] = this._x2; w[23] = this._x3; + Sb5(w[24], w[25], w[26], w[27]); + w[24] = this._x0; w[25] = this._x1; w[26] = this._x2; w[27] = this._x3; + Sb4(w[28], w[29], w[30], w[31]); + w[28] = this._x0; w[29] = this._x1; w[30] = this._x2; w[31] = this._x3; + Sb3(w[32], w[33], w[34], w[35]); + w[32] = this._x0; w[33] = this._x1; w[34] = this._x2; w[35] = this._x3; + Sb2(w[36], w[37], w[38], w[39]); + w[36] = this._x0; w[37] = this._x1; w[38] = this._x2; w[39] = this._x3; + Sb1(w[40], w[41], w[42], w[43]); + w[40] = this._x0; w[41] = this._x1; w[42] = this._x2; w[43] = this._x3; + Sb0(w[44], w[45], w[46], w[47]); + w[44] = this._x0; w[45] = this._x1; w[46] = this._x2; w[47] = this._x3; + Sb7(w[48], w[49], w[50], w[51]); + w[48] = this._x0; w[49] = this._x1; w[50] = this._x2; w[51] = this._x3; + Sb6(w[52], w[53], w[54], w[55]); + w[52] = this._x0; w[53] = this._x1; w[54] = this._x2; w[55] = this._x3; + Sb5(w[56], w[57], w[58], w[59]); + w[56] = this._x0; w[57] = this._x1; w[58] = this._x2; w[59] = this._x3; + Sb4(w[60], w[61], w[62], w[63]); + w[60] = this._x0; w[61] = this._x1; w[62] = this._x2; w[63] = this._x3; + Sb3(w[64], w[65], w[66], w[67]); + w[64] = this._x0; w[65] = this._x1; w[66] = this._x2; w[67] = this._x3; + Sb2(w[68], w[69], w[70], w[71]); + w[68] = this._x0; w[69] = this._x1; w[70] = this._x2; w[71] = this._x3; + Sb1(w[72], w[73], w[74], w[75]); + w[72] = this._x0; w[73] = this._x1; w[74] = this._x2; w[75] = this._x3; + Sb0(w[76], w[77], w[78], w[79]); + w[76] = this._x0; w[77] = this._x1; w[78] = this._x2; w[79] = this._x3; + Sb7(w[80], w[81], w[82], w[83]); + w[80] = this._x0; w[81] = this._x1; w[82] = this._x2; w[83] = this._x3; + Sb6(w[84], w[85], w[86], w[87]); + w[84] = this._x0; w[85] = this._x1; w[86] = this._x2; w[87] = this._x3; + Sb5(w[88], w[89], w[90], w[91]); + w[88] = this._x0; w[89] = this._x1; w[90] = this._x2; w[91] = this._x3; + Sb4(w[92], w[93], w[94], w[95]); + w[92] = this._x0; w[93] = this._x1; w[94] = this._x2; w[95] = this._x3; + Sb3(w[96], w[97], w[98], w[99]); + w[96] = this._x0; w[97] = this._x1; w[98] = this._x2; w[99] = this._x3; + Sb2(w[100], w[101], w[102], w[103]); + w[100] = this._x0; w[101] = this._x1; w[102] = this._x2; w[103] = this._x3; + Sb1(w[104], w[105], w[106], w[107]); + w[104] = this._x0; w[105] = this._x1; w[106] = this._x2; w[107] = this._x3; + Sb0(w[108], w[109], w[110], w[111]); + w[108] = this._x0; w[109] = this._x1; w[110] = this._x2; w[111] = this._x3; + Sb7(w[112], w[113], w[114], w[115]); + w[112] = this._x0; w[113] = this._x1; w[114] = this._x2; w[115] = this._x3; + Sb6(w[116], w[117], w[118], w[119]); + w[116] = this._x0; w[117] = this._x1; w[118] = this._x2; w[119] = this._x3; + Sb5(w[120], w[121], w[122], w[123]); + w[120] = this._x0; w[121] = this._x1; w[122] = this._x2; w[123] = this._x3; + Sb4(w[124], w[125], w[126], w[127]); + w[124] = this._x0; w[125] = this._x1; w[126] = this._x2; w[127] = this._x3; + Sb3(w[128], w[129], w[130], w[131]); + w[128] = this._x0; w[129] = this._x1; w[130] = this._x2; w[131] = this._x3; + + return w; + } + + private static int RotateLeft(int x, int bits) + { + return ((x << bits) | (int)((uint)x >> (32 - bits))); + } + + private static int RotateRight(int x, int bits) + { + return ((int)((uint)x >> bits) | (x << (32 - bits))); + } + + private static int BytesToWord(byte[] src, int srcOff) + { + return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) | + ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff))); + } + + private static void WordToBytes(int word, byte[] dst, int dstOff) + { + dst[dstOff + 3] = (byte)(word); + dst[dstOff + 2] = (byte)((uint)word >> 8); + dst[dstOff + 1] = (byte)((uint)word >> 16); + dst[dstOff] = (byte)((uint)word >> 24); + } + + /* + * The sboxes below are based on the work of Brian Gladman and + * Sam Simpson, whose original notice appears below. + *

+ * For further details see: + * http://fp.gladman.plus.com/cryptography_technology/serpent/ + *

+ */ + + /* Partially optimised Serpent S Box bool functions derived */ + /* using a recursive descent analyser but without a full search */ + /* of all subtrees. This set of S boxes is the result of work */ + /* by Sam Simpson and Brian Gladman using the spare time on a */ + /* cluster of high capacity servers to search for S boxes with */ + /* this customised search engine. There are now an average of */ + /* 15.375 terms per S box. */ + /* */ + /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */ + /* and Sam Simpson (s.simpson@mia.co.uk) */ + /* 17th December 1998 */ + /* */ + /* We hereby give permission for information in this file to be */ + /* used freely subject only to acknowledgement of its origin. */ + + /// + /// S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb0(int a, int b, int c, int d) + { + int t1 = a ^ d; + int t3 = c ^ t1; + int t4 = b ^ t3; + this._x3 = (a & d) ^ t4; + int t7 = a ^ (b & t1); + this._x2 = t4 ^ (c | t7); + int t12 = this._x3 & (t3 ^ t7); + this._x1 = (~t3) ^ t12; + this._x0 = t12 ^ (~t7); + } + + /// + /// InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib0(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ b; + int t4 = d ^ (t1 | t2); + int t5 = c ^ t4; + this._x2 = t2 ^ t5; + int t8 = t1 ^ (d & t2); + this._x1 = t4 ^ (this._x2 & t8); + this._x3 = (a & t4) ^ (t5 | this._x1); + this._x0 = this._x3 ^ (t5 ^ t8); + } + + /// + /// S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb1(int a, int b, int c, int d) + { + int t2 = b ^ (~a); + int t5 = c ^ (a | t2); + this._x2 = d ^ t5; + int t7 = b ^ (d | t2); + int t8 = t2 ^ this._x2; + this._x3 = t8 ^ (t5 & t7); + int t11 = t5 ^ t7; + this._x1 = this._x3 ^ t11; + this._x0 = t5 ^ (t8 & t11); + } + + /// + /// InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib1(int a, int b, int c, int d) + { + int t1 = b ^ d; + int t3 = a ^ (b & t1); + int t4 = t1 ^ t3; + this._x3 = c ^ t4; + int t7 = b ^ (t1 & t3); + int t8 = this._x3 | t7; + this._x1 = t3 ^ t8; + int t10 = ~this._x1; + int t11 = this._x3 ^ t7; + this._x0 = t10 ^ t11; + this._x2 = t4 ^ (t10 | t11); + } + + /// + /// S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb2(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = b ^ d; + int t3 = c & t1; + this._x0 = t2 ^ t3; + int t5 = c ^ t1; + int t6 = c ^ this._x0; + int t7 = b & t6; + this._x3 = t5 ^ t7; + this._x2 = a ^ ((d | t7) & (this._x0 | t5)); + this._x1 = (t2 ^ this._x3) ^ (this._x2 ^ (d | t1)); + } + + /// + /// InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib2(int a, int b, int c, int d) + { + int t1 = b ^ d; + int t2 = ~t1; + int t3 = a ^ c; + int t4 = c ^ t1; + int t5 = b & t4; + this._x0 = t3 ^ t5; + int t7 = a | t2; + int t8 = d ^ t7; + int t9 = t3 | t8; + this._x3 = t1 ^ t9; + int t11 = ~t4; + int t12 = this._x0 | this._x3; + this._x1 = t11 ^ t12; + this._x2 = (d & t11) ^ (t3 ^ t12); + } + + /// + /// S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb3(int a, int b, int c, int d) + { + int t1 = a ^ b; + int t2 = a & c; + int t3 = a | d; + int t4 = c ^ d; + int t5 = t1 & t3; + int t6 = t2 | t5; + this._x2 = t4 ^ t6; + int t8 = b ^ t3; + int t9 = t6 ^ t8; + int t10 = t4 & t9; + this._x0 = t1 ^ t10; + int t12 = this._x2 & this._x0; + this._x1 = t9 ^ t12; + this._x3 = (b | d) ^ (t4 ^ t12); + } + + /// + /// InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib3(int a, int b, int c, int d) + { + int t1 = a | b; + int t2 = b ^ c; + int t3 = b & t2; + int t4 = a ^ t3; + int t5 = c ^ t4; + int t6 = d | t4; + this._x0 = t2 ^ t6; + int t8 = t2 | t6; + int t9 = d ^ t8; + this._x2 = t5 ^ t9; + int t11 = t1 ^ t9; + int t12 = this._x0 & t11; + this._x3 = t4 ^ t12; + this._x1 = this._x3 ^ (this._x0 ^ t11); + } + + /// + /// S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb4(int a, int b, int c, int d) + { + int t1 = a ^ d; + int t2 = d & t1; + int t3 = c ^ t2; + int t4 = b | t3; + this._x3 = t1 ^ t4; + int t6 = ~b; + int t7 = t1 | t6; + this._x0 = t3 ^ t7; + int t9 = a & this._x0; + int t10 = t1 ^ t6; + int t11 = t4 & t10; + this._x2 = t9 ^ t11; + this._x1 = (a ^ t3) ^ (t10 & this._x2); + } + + /// + /// InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib4(int a, int b, int c, int d) + { + int t1 = c | d; + int t2 = a & t1; + int t3 = b ^ t2; + int t4 = a & t3; + int t5 = c ^ t4; + this._x1 = d ^ t5; + int t7 = ~a; + int t8 = t5 & this._x1; + this._x3 = t3 ^ t8; + int t10 = this._x1 | t7; + int t11 = d ^ t10; + this._x0 = this._x3 ^ t11; + this._x2 = (t3 & t11) ^ (this._x1 ^ t7); + } + + /// + /// S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb5(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ b; + int t3 = a ^ d; + int t4 = c ^ t1; + int t5 = t2 | t3; + this._x0 = t4 ^ t5; + int t7 = d & this._x0; + int t8 = t2 ^ this._x0; + this._x1 = t7 ^ t8; + int t10 = t1 | this._x0; + int t11 = t2 | t7; + int t12 = t3 ^ t10; + this._x2 = t11 ^ t12; + this._x3 = (b ^ t7) ^ (this._x1 & t12); + } + + /// + /// InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib5(int a, int b, int c, int d) + { + int t1 = ~c; + int t2 = b & t1; + int t3 = d ^ t2; + int t4 = a & t3; + int t5 = b ^ t1; + this._x3 = t4 ^ t5; + int t7 = b | this._x3; + int t8 = a & t7; + this._x1 = t3 ^ t8; + int t10 = a | d; + int t11 = t1 ^ t7; + this._x0 = t10 ^ t11; + this._x2 = (b & t10) ^ (t4 | (a ^ c)); + } + + /// + /// S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb6(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ d; + int t3 = b ^ t2; + int t4 = t1 | t2; + int t5 = c ^ t4; + this._x1 = b ^ t5; + int t7 = t2 | this._x1; + int t8 = d ^ t7; + int t9 = t5 & t8; + this._x2 = t3 ^ t9; + int t11 = t5 ^ t8; + this._x0 = this._x2 ^ t11; + this._x3 = (~t5) ^ (t3 & t11); + } + + /// + /// InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib6(int a, int b, int c, int d) + { + int t1 = ~a; + int t2 = a ^ b; + int t3 = c ^ t2; + int t4 = c | t1; + int t5 = d ^ t4; + this._x1 = t3 ^ t5; + int t7 = t3 & t5; + int t8 = t2 ^ t7; + int t9 = b | t8; + this._x3 = t5 ^ t9; + int t11 = b | this._x3; + this._x0 = t8 ^ t11; + this._x2 = (d & t1) ^ (t3 ^ t11); + } + + /// + /// S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Sb7(int a, int b, int c, int d) + { + int t1 = b ^ c; + int t2 = c & t1; + int t3 = d ^ t2; + int t4 = a ^ t3; + int t5 = d | t1; + int t6 = t4 & t5; + this._x1 = b ^ t6; + int t8 = t3 | this._x1; + int t9 = a & t4; + this._x3 = t1 ^ t9; + int t11 = t4 ^ t8; + int t12 = this._x3 & t11; + this._x2 = t3 ^ t12; + this._x0 = (~t11) ^ (this._x3 & this._x2); + } + + /// + /// InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms. + /// + /// A. + /// The b. + /// The c. + /// The d. + private void Ib7(int a, int b, int c, int d) + { + int t3 = c | (a & b); + int t4 = d & (a | b); + this._x3 = t3 ^ t4; + int t6 = ~d; + int t7 = b ^ t4; + int t9 = t7 | (this._x3 ^ t6); + this._x1 = a ^ t9; + this._x0 = (c ^ t7) ^ (d | this._x1); + this._x2 = (t3 ^ this._x1) ^ (this._x0 ^ (a & this._x3)); + } + + /// + /// Apply the linear transformation to the register set. + /// + private void LT() + { + int x0 = RotateLeft(this._x0, 13); + int x2 = RotateLeft(this._x2, 3); + int x1 = this._x1 ^ x0 ^ x2; + int x3 = this._x3 ^ x2 ^ x0 << 3; + + this._x1 = RotateLeft(x1, 1); + this._x3 = RotateLeft(x3, 7); + this._x0 = RotateLeft(x0 ^ this._x1 ^ this._x3, 5); + this._x2 = RotateLeft(x2 ^ this._x3 ^ (this._x1 << 7), 22); + } + + /// + /// Apply the inverse of the linear transformation to the register set. + /// + private void InverseLT() + { + int x2 = RotateRight(this._x2, 22) ^ this._x3 ^ (this._x1 << 7); + int x0 = RotateRight(this._x0, 5) ^ this._x1 ^ this._x3; + int x3 = RotateRight(this._x3, 7); + int x1 = RotateRight(this._x1, 1); + this._x3 = x3 ^ x2 ^ x0 << 3; + this._x1 = x1 ^ x0 ^ x2; + this._x2 = RotateRight(x2, 3); + this._x0 = RotateRight(x0, 13); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs index 25ca07f..6bd2d51 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs @@ -1,153 +1,150 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements 3DES cipher algorithm. - /// - public sealed class TripleDesCipher : DesCipher - { - private int[] _encryptionKey1; - - private int[] _encryptionKey2; - - private int[] _encryptionKey3; - - private int[] _decryptionKey1; - - private int[] _decryptionKey2; - - private int[] _decryptionKey3; - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - public TripleDesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, mode, padding) - { - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._encryptionKey1 == null || this._encryptionKey2 == null || this._encryptionKey3 == null) - { - var part1 = new byte[8]; - var part2 = new byte[8]; - - Buffer.BlockCopy(this.Key, 0, part1, 0, 8); - Buffer.BlockCopy(this.Key, 8, part2, 0, 8); - - this._encryptionKey1 = this.GenerateWorkingKey(true, part1); - - this._encryptionKey2 = this.GenerateWorkingKey(false, part2); - - if (this.Key.Length == 24) - { - var part3 = new byte[8]; - Buffer.BlockCopy(this.Key, 16, part3, 0, 8); - - this._encryptionKey3 = this.GenerateWorkingKey(true, part3); - } - else - { - this._encryptionKey3 = this._encryptionKey1; - } - } - - byte[] temp = new byte[this.BlockSize]; - - DesCipher.DesFunc(this._encryptionKey1, inputBuffer, inputOffset, temp, 0); - DesCipher.DesFunc(this._encryptionKey2, temp, 0, temp, 0); - DesCipher.DesFunc(this._encryptionKey3, temp, 0, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._decryptionKey1 == null || this._decryptionKey2 == null || this._decryptionKey3 == null) - { - var part1 = new byte[8]; - var part2 = new byte[8]; - - Buffer.BlockCopy(this.Key, 0, part1, 0, 8); - Buffer.BlockCopy(this.Key, 8, part2, 0, 8); - - this._decryptionKey1 = this.GenerateWorkingKey(false, part1); - this._decryptionKey2 = this.GenerateWorkingKey(true, part2); - - if (this.Key.Length == 24) - { - var part3 = new byte[8]; - Buffer.BlockCopy(this.Key, 16, part3, 0, 8); - - this._decryptionKey3 = this.GenerateWorkingKey(false, part3); - } - else - { - this._decryptionKey3 = this._decryptionKey1; - } - } - - byte[] temp = new byte[this.BlockSize]; - - DesCipher.DesFunc(this._decryptionKey3, inputBuffer, inputOffset, temp, 0); - DesCipher.DesFunc(this._decryptionKey2, temp, 0, temp, 0); - DesCipher.DesFunc(this._decryptionKey1, temp, 0, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Validates the key. - /// - protected override void ValidateKey() - { - var keySize = this.Key.Length * 8; - - if (!(keySize == 128 || keySize == 128 + 64)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Implements 3DES cipher algorithm. + /// + public sealed class TripleDesCipher : DesCipher + { + private int[] _encryptionKey1; + + private int[] _encryptionKey2; + + private int[] _encryptionKey3; + + private int[] _decryptionKey1; + + private int[] _decryptionKey2; + + private int[] _decryptionKey3; + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The mode. + /// The padding. + /// is null. + public TripleDesCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, mode, padding) + { + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if ((inputOffset + this.BlockSize) > inputBuffer.Length) + throw new IndexOutOfRangeException("input buffer too short"); + + if ((outputOffset + this.BlockSize) > outputBuffer.Length) + throw new IndexOutOfRangeException("output buffer too short"); + + if (this._encryptionKey1 == null || this._encryptionKey2 == null || this._encryptionKey3 == null) + { + var part1 = new byte[8]; + var part2 = new byte[8]; + + Buffer.BlockCopy(this.Key, 0, part1, 0, 8); + Buffer.BlockCopy(this.Key, 8, part2, 0, 8); + + this._encryptionKey1 = this.GenerateWorkingKey(true, part1); + + this._encryptionKey2 = this.GenerateWorkingKey(false, part2); + + if (this.Key.Length == 24) + { + var part3 = new byte[8]; + Buffer.BlockCopy(this.Key, 16, part3, 0, 8); + + this._encryptionKey3 = this.GenerateWorkingKey(true, part3); + } + else + { + this._encryptionKey3 = this._encryptionKey1; + } + } + + byte[] temp = new byte[this.BlockSize]; + + DesCipher.DesFunc(this._encryptionKey1, inputBuffer, inputOffset, temp, 0); + DesCipher.DesFunc(this._encryptionKey2, temp, 0, temp, 0); + DesCipher.DesFunc(this._encryptionKey3, temp, 0, outputBuffer, outputOffset); + + return this.BlockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + if ((inputOffset + this.BlockSize) > inputBuffer.Length) + throw new IndexOutOfRangeException("input buffer too short"); + + if ((outputOffset + this.BlockSize) > outputBuffer.Length) + throw new IndexOutOfRangeException("output buffer too short"); + + if (this._decryptionKey1 == null || this._decryptionKey2 == null || this._decryptionKey3 == null) + { + var part1 = new byte[8]; + var part2 = new byte[8]; + + Buffer.BlockCopy(this.Key, 0, part1, 0, 8); + Buffer.BlockCopy(this.Key, 8, part2, 0, 8); + + this._decryptionKey1 = this.GenerateWorkingKey(false, part1); + this._decryptionKey2 = this.GenerateWorkingKey(true, part2); + + if (this.Key.Length == 24) + { + var part3 = new byte[8]; + Buffer.BlockCopy(this.Key, 16, part3, 0, 8); + + this._decryptionKey3 = this.GenerateWorkingKey(false, part3); + } + else + { + this._decryptionKey3 = this._decryptionKey1; + } + } + + byte[] temp = new byte[this.BlockSize]; + + DesCipher.DesFunc(this._decryptionKey3, inputBuffer, inputOffset, temp, 0); + DesCipher.DesFunc(this._decryptionKey2, temp, 0, temp, 0); + DesCipher.DesFunc(this._decryptionKey1, temp, 0, outputBuffer, outputOffset); + + return this.BlockSize; + } + + /// + /// Validates the key. + /// + protected override void ValidateKey() + { + var keySize = this.Key.Length * 8; + + if (!(keySize == 128 || keySize == 128 + 64)) + throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs index fac38e4..04bfb9f 100644 --- a/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs +++ b/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs @@ -1,617 +1,614 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements Twofish cipher algorithm - /// - public sealed class TwofishCipher : BlockCipher - { - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public TwofishCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize == 128 || keySize == 192 || keySize == 256)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - // TODO: Refactor this algorithm - - // calculate the MDS matrix - int[] m1 = new int[2]; - int[] mX = new int[2]; - int[] mY = new int[2]; - int j; - - for (int i = 0; i < MAX_KEY_BITS; i++) - { - j = P[0 + i] & 0xff; - m1[0] = j; - mX[0] = Mx_X(j) & 0xff; - mY[0] = Mx_Y(j) & 0xff; - - j = P[(1 * 256) + i] & 0xff; - m1[1] = j; - mX[1] = Mx_X(j) & 0xff; - mY[1] = Mx_Y(j) & 0xff; - - gMDS0[i] = m1[P_00] | mX[P_00] << 8 | mY[P_00] << 16 | mY[P_00] << 24; - - gMDS1[i] = mY[P_10] | mY[P_10] << 8 | mX[P_10] << 16 | m1[P_10] << 24; - - gMDS2[i] = mX[P_20] | mY[P_20] << 8 | m1[P_20] << 16 | mY[P_20] << 24; - - gMDS3[i] = mX[P_30] | m1[P_30] << 8 | mY[P_30] << 16 | mX[P_30] << 24; - } - - this.k64Cnt = key.Length / 8; // pre-padded ? - this.SetKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - int x0 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[INPUT_WHITEN]; - int x1 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[INPUT_WHITEN + 1]; - int x2 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[INPUT_WHITEN + 2]; - int x3 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[INPUT_WHITEN + 3]; - - int k = ROUND_SUBKEYS; - int t0, t1; - for (int r = 0; r < ROUNDS; r += 2) - { - t0 = Fe32_0(gSBox, x0); - t1 = Fe32_3(gSBox, x1); - x2 ^= t0 + t1 + gSubKeys[k++]; - x2 = (int)((uint)x2 >> 1) | x2 << 31; - x3 = (x3 << 1 | (int)((uint)x3 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); - - t0 = Fe32_0(gSBox, x2); - t1 = Fe32_3(gSBox, x3); - x0 ^= t0 + t1 + gSubKeys[k++]; - x0 = (int)((uint)x0 >> 1) | x0 << 31; - x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); - } - - Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], outputBuffer, outputOffset); - Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], outputBuffer, outputOffset + 4); - Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], outputBuffer, outputOffset + 8); - Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - int x2 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[OUTPUT_WHITEN]; - int x3 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[OUTPUT_WHITEN + 1]; - int x0 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[OUTPUT_WHITEN + 2]; - int x1 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[OUTPUT_WHITEN + 3]; - - int k = ROUND_SUBKEYS + 2 * ROUNDS - 1; - int t0, t1; - for (int r = 0; r < ROUNDS; r += 2) - { - t0 = Fe32_0(gSBox, x2); - t1 = Fe32_3(gSBox, x3); - x1 ^= t0 + 2 * t1 + gSubKeys[k--]; - x0 = (x0 << 1 | (int)((uint)x0 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); - x1 = (int)((uint)x1 >> 1) | x1 << 31; - - t0 = Fe32_0(gSBox, x0); - t1 = Fe32_3(gSBox, x1); - x3 ^= t0 + 2 * t1 + gSubKeys[k--]; - x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); - x3 = (int)((uint)x3 >> 1) | x3 << 31; - } - - Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], outputBuffer, outputOffset); - Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], outputBuffer, outputOffset + 4); - Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], outputBuffer, outputOffset + 8); - Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - #region Static Definition Tables - - private static readonly byte[] P = { - //{ // p0 - (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8, - (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76, - (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78, - (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38, - (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98, - (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C, - (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26, - (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48, - (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30, - (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23, - (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59, - (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82, - (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E, - (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C, - (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE, - (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61, - (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5, - (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B, - (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B, - (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1, - (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45, - (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66, - (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56, - (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7, - (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5, - (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA, - (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF, - (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71, - (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD, - (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8, - (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D, - (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7, - (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED, - (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2, - (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11, - (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90, - (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF, - (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB, - (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B, - (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF, - (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE, - (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B, - (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46, - (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64, - (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F, - (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A, - (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A, - (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A, - (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29, - (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02, - (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17, - (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D, - (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74, - (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72, - (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12, - (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34, - (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68, - (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8, - (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40, - (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4, - (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0, - (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00, - (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42, - (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0, - // }, - //{ // p1 - (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4, - (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8, - (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B, - (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B, - (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD, - (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1, - (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B, - (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F, - (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B, - (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D, - (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E, - (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5, - (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14, - (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3, - (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54, - (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51, - (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A, - (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96, - (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10, - (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C, - (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7, - (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70, - (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB, - (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8, - (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF, - (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC, - (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF, - (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2, - (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82, - (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9, - (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97, - (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17, - (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D, - (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3, - (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C, - (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E, - (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F, - (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49, - (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21, - (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9, - (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD, - (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01, - (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F, - (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48, - (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E, - (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19, - (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57, - (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64, - (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE, - (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5, - (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44, - (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69, - (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15, - (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E, - (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34, - (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC, - (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B, - (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB, - (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52, - (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9, - (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4, - (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2, - (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56, - (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 - //} - }; - - #endregion - - /** - * Define the fixed p0/p1 permutations used in keyed S-box lookup. - * By changing the following constant definitions, the S-boxes will - * automatically Get changed in the Twofish engine. - */ - private const int P_00 = 1; - private const int P_01 = 0; - private const int P_02 = 0; - private const int P_03 = P_01 ^ 1; - private const int P_04 = 1; - - private const int P_10 = 0; - private const int P_11 = 0; - private const int P_12 = 1; - private const int P_13 = P_11 ^ 1; - private const int P_14 = 0; - - private const int P_20 = 1; - private const int P_21 = 1; - private const int P_22 = 0; - private const int P_23 = P_21 ^ 1; - private const int P_24 = 0; - - private const int P_30 = 0; - private const int P_31 = 1; - private const int P_32 = 1; - private const int P_33 = P_31 ^ 1; - private const int P_34 = 1; - - /* Primitive polynomial for GF(256) */ - private const int GF256_FDBK = 0x169; - private const int GF256_FDBK_2 = GF256_FDBK / 2; - private const int GF256_FDBK_4 = GF256_FDBK / 4; - - private const int RS_GF_FDBK = 0x14D; // field generator - - //==================================== - // Useful constants - //==================================== - - private const int ROUNDS = 16; - private const int MAX_ROUNDS = 16; // bytes = 128 bits - private const int MAX_KEY_BITS = 256; - - private const int INPUT_WHITEN = 0; - private const int OUTPUT_WHITEN = INPUT_WHITEN + 16 / 4; // 4 - private const int ROUND_SUBKEYS = OUTPUT_WHITEN + 16 / 4;// 8 - - private const int TOTAL_SUBKEYS = ROUND_SUBKEYS + 2 * MAX_ROUNDS;// 40 - - private const int SK_STEP = 0x02020202; - private const int SK_BUMP = 0x01010101; - private const int SK_ROTL = 9; - - private int[] gMDS0 = new int[MAX_KEY_BITS]; - private int[] gMDS1 = new int[MAX_KEY_BITS]; - private int[] gMDS2 = new int[MAX_KEY_BITS]; - private int[] gMDS3 = new int[MAX_KEY_BITS]; - - /** - * gSubKeys[] and gSBox[] are eventually used in the - * encryption and decryption methods. - */ - private int[] gSubKeys; - private int[] gSBox; - - private int k64Cnt; - - private void SetKey(byte[] key) - { - int[] k32e = new int[MAX_KEY_BITS / 64]; // 4 - int[] k32o = new int[MAX_KEY_BITS / 64]; // 4 - - int[] sBoxKeys = new int[MAX_KEY_BITS / 64]; // 4 - gSubKeys = new int[TOTAL_SUBKEYS]; - - if (k64Cnt < 1) - { - throw new ArgumentException("Key size less than 64 bits"); - } - - if (k64Cnt > 4) - { - throw new ArgumentException("Key size larger than 256 bits"); - } - - /* - * k64Cnt is the number of 8 byte blocks (64 chunks) - * that are in the input key. The input key is a - * maximum of 32 bytes ( 256 bits ), so the range - * for k64Cnt is 1..4 - */ - for (int i = 0, p = 0; i < k64Cnt; i++) - { - p = i * 8; - - k32e[i] = BytesTo32Bits(key, p); - k32o[i] = BytesTo32Bits(key, p + 4); - - sBoxKeys[k64Cnt - 1 - i] = RS_MDS_Encode(k32e[i], k32o[i]); - } - - int q, A, B; - for (int i = 0; i < TOTAL_SUBKEYS / 2; i++) - { - q = i * SK_STEP; - A = F32(q, k32e); - B = F32(q + SK_BUMP, k32o); - B = B << 8 | (int)((uint)B >> 24); - A += B; - gSubKeys[i * 2] = A; - A += B; - gSubKeys[i * 2 + 1] = A << SK_ROTL | (int)((uint)A >> (32 - SK_ROTL)); - } - - /* - * fully expand the table for speed - */ - int k0 = sBoxKeys[0]; - int k1 = sBoxKeys[1]; - int k2 = sBoxKeys[2]; - int k3 = sBoxKeys[3]; - int b0, b1, b2, b3; - gSBox = new int[4 * MAX_KEY_BITS]; - for (int i = 0; i < MAX_KEY_BITS; i++) - { - b0 = b1 = b2 = b3 = i; - switch (k64Cnt & 3) - { - case 1: - gSBox[i * 2] = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)]; - gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)]; - gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)]; - gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; - break; - case 0: /* 256 bits of key */ - b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); - b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3); - b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3); - b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3); - goto case 3; - case 3: - b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2); - b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2); - b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2); - b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2); - goto case 2; - case 2: - gSBox[i * 2] = gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)]; - gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)]; - gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)]; - gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; - break; - } - } - - /* - * the function exits having setup the gSBox with the - * input key material. - */ - } - - /* - * TODO: This can be optimised and made cleaner by combining - * the functionality in this function and applying it appropriately - * to the creation of the subkeys during key setup. - */ - private int F32(int x, int[] k32) - { - int b0 = M_b0(x); - int b1 = M_b1(x); - int b2 = M_b2(x); - int b3 = M_b3(x); - int k0 = k32[0]; - int k1 = k32[1]; - int k2 = k32[2]; - int k3 = k32[3]; - - int result = 0; - switch (k64Cnt & 3) - { - case 1: - result = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)] ^ - gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)] ^ - gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)] ^ - gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; - break; - case 0: /* 256 bits of key */ - b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); - b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3); - b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3); - b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3); - goto case 3; - case 3: - b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2); - b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2); - b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2); - b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2); - goto case 2; - case 2: - result = - gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)] ^ - gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)] ^ - gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)] ^ - gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; - break; - } - return result; - } - - /** - * Use (12, 8) Reed-Solomon code over GF(256) to produce - * a key S-box 32-bit entity from 2 key material 32-bit - * entities. - * - * @param k0 first 32-bit entity - * @param k1 second 32-bit entity - * @return Remainder polynomial Generated using RS code - */ - private static int RS_MDS_Encode(int k0, int k1) - { - int r = k1; - // shift 1 byte at a time - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - r ^= k0; - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - - return r; - } - - /** - * Reed-Solomon code parameters: (12,8) reversible code: - *

- *

-        * G(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
-        * 
- * where a = primitive root of field generator 0x14D - *

- */ - private static int RS_rem(int x) - { - int b = (int)(((uint)x >> 24) & 0xff); - int g2 = ((b << 1) ^ - ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; - int g3 = ((int)((uint)b >> 1) ^ - ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2; - return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b); - } - - private static int LFSR1(int x) - { - return (x >> 1) ^ - (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); - } - - private static int LFSR2(int x) - { - return (x >> 2) ^ - (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ - (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); - } - - private static int Mx_X(int x) - { - return x ^ LFSR2(x); - } // 5B - - private static int Mx_Y(int x) - { - return x ^ LFSR1(x) ^ LFSR2(x); - } // EF - - private static int M_b0(int x) - { - return x & 0xff; - } - - private static int M_b1(int x) - { - return (int)((uint)x >> 8) & 0xff; - } - - private static int M_b2(int x) - { - return (int)((uint)x >> 16) & 0xff; - } - - private static int M_b3(int x) - { - return (int)((uint)x >> 24) & 0xff; - } - - private static int Fe32_0(int[] gSBox1, int x) - { - return gSBox1[0x000 + 2 * (x & 0xff)] ^ - gSBox1[0x001 + 2 * ((int)((uint)x >> 8) & 0xff)] ^ - gSBox1[0x200 + 2 * ((int)((uint)x >> 16) & 0xff)] ^ - gSBox1[0x201 + 2 * ((int)((uint)x >> 24) & 0xff)]; - } - - private static int Fe32_3(int[] gSBox1, int x) - { - return gSBox1[0x000 + 2 * ((int)((uint)x >> 24) & 0xff)] ^ - gSBox1[0x001 + 2 * (x & 0xff)] ^ - gSBox1[0x200 + 2 * ((int)((uint)x >> 8) & 0xff)] ^ - gSBox1[0x201 + 2 * ((int)((uint)x >> 16) & 0xff)]; - } - - private static int BytesTo32Bits(byte[] b, int p) - { - return ((b[p] & 0xff)) | - ((b[p + 1] & 0xff) << 8) | - ((b[p + 2] & 0xff) << 16) | - ((b[p + 3] & 0xff) << 24); - } - - private static void Bits32ToBytes(int inData, byte[] b, int offset) - { - b[offset] = (byte)inData; - b[offset + 1] = (byte)(inData >> 8); - b[offset + 2] = (byte)(inData >> 16); - b[offset + 3] = (byte)(inData >> 24); - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography.Ciphers +{ + /// + /// Implements Twofish cipher algorithm + /// + public sealed class TwofishCipher : BlockCipher + { + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// The mode. + /// The padding. + /// is null. + /// Keysize is not valid for this algorithm. + public TwofishCipher(byte[] key, CipherMode mode, CipherPadding padding) + : base(key, 16, mode, padding) + { + var keySize = key.Length * 8; + + if (!(keySize == 128 || keySize == 192 || keySize == 256)) + throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); + + // TODO: Refactor this algorithm + + // calculate the MDS matrix + int[] m1 = new int[2]; + int[] mX = new int[2]; + int[] mY = new int[2]; + int j; + + for (int i = 0; i < MAX_KEY_BITS; i++) + { + j = P[0 + i] & 0xff; + m1[0] = j; + mX[0] = Mx_X(j) & 0xff; + mY[0] = Mx_Y(j) & 0xff; + + j = P[(1 * 256) + i] & 0xff; + m1[1] = j; + mX[1] = Mx_X(j) & 0xff; + mY[1] = Mx_Y(j) & 0xff; + + gMDS0[i] = m1[P_00] | mX[P_00] << 8 | mY[P_00] << 16 | mY[P_00] << 24; + + gMDS1[i] = mY[P_10] | mY[P_10] << 8 | mX[P_10] << 16 | m1[P_10] << 24; + + gMDS2[i] = mX[P_20] | mY[P_20] << 8 | m1[P_20] << 16 | mY[P_20] << 24; + + gMDS3[i] = mX[P_30] | m1[P_30] << 8 | mY[P_30] << 16 | mX[P_30] << 24; + } + + this.k64Cnt = key.Length / 8; // pre-padded ? + this.SetKey(key); + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + int x0 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[INPUT_WHITEN]; + int x1 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[INPUT_WHITEN + 1]; + int x2 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[INPUT_WHITEN + 2]; + int x3 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[INPUT_WHITEN + 3]; + + int k = ROUND_SUBKEYS; + int t0, t1; + for (int r = 0; r < ROUNDS; r += 2) + { + t0 = Fe32_0(gSBox, x0); + t1 = Fe32_3(gSBox, x1); + x2 ^= t0 + t1 + gSubKeys[k++]; + x2 = (int)((uint)x2 >> 1) | x2 << 31; + x3 = (x3 << 1 | (int)((uint)x3 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); + + t0 = Fe32_0(gSBox, x2); + t1 = Fe32_3(gSBox, x3); + x0 ^= t0 + t1 + gSubKeys[k++]; + x0 = (int)((uint)x0 >> 1) | x0 << 31; + x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); + } + + Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], outputBuffer, outputOffset); + Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], outputBuffer, outputOffset + 4); + Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], outputBuffer, outputOffset + 8); + Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], outputBuffer, outputOffset + 12); + + return this.BlockSize; + } + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) + { + int x2 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[OUTPUT_WHITEN]; + int x3 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[OUTPUT_WHITEN + 1]; + int x0 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[OUTPUT_WHITEN + 2]; + int x1 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[OUTPUT_WHITEN + 3]; + + int k = ROUND_SUBKEYS + 2 * ROUNDS - 1; + int t0, t1; + for (int r = 0; r < ROUNDS; r += 2) + { + t0 = Fe32_0(gSBox, x2); + t1 = Fe32_3(gSBox, x3); + x1 ^= t0 + 2 * t1 + gSubKeys[k--]; + x0 = (x0 << 1 | (int)((uint)x0 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + x1 = (int)((uint)x1 >> 1) | x1 << 31; + + t0 = Fe32_0(gSBox, x0); + t1 = Fe32_3(gSBox, x1); + x3 ^= t0 + 2 * t1 + gSubKeys[k--]; + x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + x3 = (int)((uint)x3 >> 1) | x3 << 31; + } + + Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], outputBuffer, outputOffset); + Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], outputBuffer, outputOffset + 4); + Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], outputBuffer, outputOffset + 8); + Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], outputBuffer, outputOffset + 12); + + return this.BlockSize; + } + + #region Static Definition Tables + + private static readonly byte[] P = { + //{ // p0 + (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8, + (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76, + (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78, + (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38, + (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98, + (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C, + (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26, + (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48, + (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30, + (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23, + (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59, + (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82, + (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E, + (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C, + (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE, + (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61, + (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5, + (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B, + (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B, + (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1, + (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45, + (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66, + (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56, + (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7, + (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5, + (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA, + (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF, + (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71, + (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD, + (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8, + (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D, + (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7, + (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED, + (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2, + (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11, + (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90, + (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF, + (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB, + (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B, + (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF, + (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE, + (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B, + (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46, + (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64, + (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F, + (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A, + (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A, + (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A, + (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29, + (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02, + (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17, + (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D, + (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74, + (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72, + (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12, + (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34, + (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68, + (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8, + (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40, + (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4, + (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0, + (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00, + (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42, + (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0, + // }, + //{ // p1 + (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4, + (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8, + (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B, + (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B, + (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD, + (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1, + (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B, + (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F, + (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B, + (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D, + (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E, + (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5, + (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14, + (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3, + (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54, + (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51, + (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A, + (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96, + (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10, + (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C, + (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7, + (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70, + (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB, + (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8, + (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF, + (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC, + (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF, + (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2, + (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82, + (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9, + (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97, + (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17, + (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D, + (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3, + (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C, + (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E, + (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F, + (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49, + (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21, + (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9, + (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD, + (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01, + (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F, + (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48, + (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E, + (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19, + (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57, + (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64, + (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE, + (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5, + (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44, + (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69, + (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15, + (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E, + (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34, + (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC, + (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B, + (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB, + (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52, + (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9, + (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4, + (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2, + (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56, + (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 + //} + }; + + #endregion + + /** + * Define the fixed p0/p1 permutations used in keyed S-box lookup. + * By changing the following constant definitions, the S-boxes will + * automatically Get changed in the Twofish engine. + */ + private const int P_00 = 1; + private const int P_01 = 0; + private const int P_02 = 0; + private const int P_03 = P_01 ^ 1; + private const int P_04 = 1; + + private const int P_10 = 0; + private const int P_11 = 0; + private const int P_12 = 1; + private const int P_13 = P_11 ^ 1; + private const int P_14 = 0; + + private const int P_20 = 1; + private const int P_21 = 1; + private const int P_22 = 0; + private const int P_23 = P_21 ^ 1; + private const int P_24 = 0; + + private const int P_30 = 0; + private const int P_31 = 1; + private const int P_32 = 1; + private const int P_33 = P_31 ^ 1; + private const int P_34 = 1; + + /* Primitive polynomial for GF(256) */ + private const int GF256_FDBK = 0x169; + private const int GF256_FDBK_2 = GF256_FDBK / 2; + private const int GF256_FDBK_4 = GF256_FDBK / 4; + + private const int RS_GF_FDBK = 0x14D; // field generator + + //==================================== + // Useful constants + //==================================== + + private const int ROUNDS = 16; + private const int MAX_ROUNDS = 16; // bytes = 128 bits + private const int MAX_KEY_BITS = 256; + + private const int INPUT_WHITEN = 0; + private const int OUTPUT_WHITEN = INPUT_WHITEN + 16 / 4; // 4 + private const int ROUND_SUBKEYS = OUTPUT_WHITEN + 16 / 4;// 8 + + private const int TOTAL_SUBKEYS = ROUND_SUBKEYS + 2 * MAX_ROUNDS;// 40 + + private const int SK_STEP = 0x02020202; + private const int SK_BUMP = 0x01010101; + private const int SK_ROTL = 9; + + private readonly int[] gMDS0 = new int[MAX_KEY_BITS]; + private readonly int[] gMDS1 = new int[MAX_KEY_BITS]; + private readonly int[] gMDS2 = new int[MAX_KEY_BITS]; + private readonly int[] gMDS3 = new int[MAX_KEY_BITS]; + + /** + * gSubKeys[] and gSBox[] are eventually used in the + * encryption and decryption methods. + */ + private int[] gSubKeys; + private int[] gSBox; + + private int k64Cnt; + + private void SetKey(byte[] key) + { + int[] k32e = new int[MAX_KEY_BITS / 64]; // 4 + int[] k32o = new int[MAX_KEY_BITS / 64]; // 4 + + int[] sBoxKeys = new int[MAX_KEY_BITS / 64]; // 4 + gSubKeys = new int[TOTAL_SUBKEYS]; + + if (k64Cnt < 1) + { + throw new ArgumentException("Key size less than 64 bits"); + } + + if (k64Cnt > 4) + { + throw new ArgumentException("Key size larger than 256 bits"); + } + + /* + * k64Cnt is the number of 8 byte blocks (64 chunks) + * that are in the input key. The input key is a + * maximum of 32 bytes ( 256 bits ), so the range + * for k64Cnt is 1..4 + */ + for (int i = 0; i < k64Cnt; i++) + { + var p = i * 8; + + k32e[i] = BytesTo32Bits(key, p); + k32o[i] = BytesTo32Bits(key, p + 4); + + sBoxKeys[k64Cnt - 1 - i] = RS_MDS_Encode(k32e[i], k32o[i]); + } + + int q, A, B; + for (int i = 0; i < TOTAL_SUBKEYS / 2; i++) + { + q = i * SK_STEP; + A = F32(q, k32e); + B = F32(q + SK_BUMP, k32o); + B = B << 8 | (int)((uint)B >> 24); + A += B; + gSubKeys[i * 2] = A; + A += B; + gSubKeys[i * 2 + 1] = A << SK_ROTL | (int)((uint)A >> (32 - SK_ROTL)); + } + + /* + * fully expand the table for speed + */ + int k0 = sBoxKeys[0]; + int k1 = sBoxKeys[1]; + int k2 = sBoxKeys[2]; + int k3 = sBoxKeys[3]; + int b0, b1, b2, b3; + gSBox = new int[4 * MAX_KEY_BITS]; + for (int i = 0; i < MAX_KEY_BITS; i++) + { + b0 = b1 = b2 = b3 = i; + switch (k64Cnt & 3) + { + case 1: + gSBox[i * 2] = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)]; + gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)]; + gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)]; + gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; + break; + case 0: /* 256 bits of key */ + b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); + b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3); + b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3); + b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3); + goto case 3; + case 3: + b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2); + b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2); + b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2); + b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2); + goto case 2; + case 2: + gSBox[i * 2] = gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)]; + gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)]; + gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)]; + gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; + break; + } + } + + /* + * the function exits having setup the gSBox with the + * input key material. + */ + } + + /* + * TODO: This can be optimised and made cleaner by combining + * the functionality in this function and applying it appropriately + * to the creation of the subkeys during key setup. + */ + private int F32(int x, int[] k32) + { + int b0 = M_b0(x); + int b1 = M_b1(x); + int b2 = M_b2(x); + int b3 = M_b3(x); + int k0 = k32[0]; + int k1 = k32[1]; + int k2 = k32[2]; + int k3 = k32[3]; + + int result = 0; + switch (k64Cnt & 3) + { + case 1: + result = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)] ^ + gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)] ^ + gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)] ^ + gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; + break; + case 0: /* 256 bits of key */ + b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); + b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3); + b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3); + b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3); + goto case 3; + case 3: + b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2); + b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2); + b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2); + b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2); + goto case 2; + case 2: + result = + gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)] ^ + gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)] ^ + gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)] ^ + gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; + break; + } + return result; + } + + /** + * Use (12, 8) Reed-Solomon code over GF(256) to produce + * a key S-box 32-bit entity from 2 key material 32-bit + * entities. + * + * @param k0 first 32-bit entity + * @param k1 second 32-bit entity + * @return Remainder polynomial Generated using RS code + */ + private static int RS_MDS_Encode(int k0, int k1) + { + int r = k1; + // shift 1 byte at a time + r = RS_rem(r); + r = RS_rem(r); + r = RS_rem(r); + r = RS_rem(r); + r ^= k0; + r = RS_rem(r); + r = RS_rem(r); + r = RS_rem(r); + r = RS_rem(r); + + return r; + } + + /** + * Reed-Solomon code parameters: (12,8) reversible code: + *

+ *

+        * G(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
+        * 
+ * where a = primitive root of field generator 0x14D + *

+ */ + private static int RS_rem(int x) + { + int b = (int)(((uint)x >> 24) & 0xff); + int g2 = ((b << 1) ^ + ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; + int g3 = ((int)((uint)b >> 1) ^ + ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2; + return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b); + } + + private static int LFSR1(int x) + { + return (x >> 1) ^ + (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); + } + + private static int LFSR2(int x) + { + return (x >> 2) ^ + (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ + (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); + } + + private static int Mx_X(int x) + { + return x ^ LFSR2(x); + } // 5B + + private static int Mx_Y(int x) + { + return x ^ LFSR1(x) ^ LFSR2(x); + } // EF + + private static int M_b0(int x) + { + return x & 0xff; + } + + private static int M_b1(int x) + { + return (int)((uint)x >> 8) & 0xff; + } + + private static int M_b2(int x) + { + return (int)((uint)x >> 16) & 0xff; + } + + private static int M_b3(int x) + { + return (int)((uint)x >> 24) & 0xff; + } + + private static int Fe32_0(int[] gSBox1, int x) + { + return gSBox1[0x000 + 2 * (x & 0xff)] ^ + gSBox1[0x001 + 2 * ((int)((uint)x >> 8) & 0xff)] ^ + gSBox1[0x200 + 2 * ((int)((uint)x >> 16) & 0xff)] ^ + gSBox1[0x201 + 2 * ((int)((uint)x >> 24) & 0xff)]; + } + + private static int Fe32_3(int[] gSBox1, int x) + { + return gSBox1[0x000 + 2 * ((int)((uint)x >> 24) & 0xff)] ^ + gSBox1[0x001 + 2 * (x & 0xff)] ^ + gSBox1[0x200 + 2 * ((int)((uint)x >> 8) & 0xff)] ^ + gSBox1[0x201 + 2 * ((int)((uint)x >> 16) & 0xff)]; + } + + private static int BytesTo32Bits(byte[] b, int p) + { + return ((b[p] & 0xff)) | + ((b[p + 1] & 0xff) << 8) | + ((b[p + 2] & 0xff) << 16) | + ((b[p + 3] & 0xff) << 24); + } + + private static void Bits32ToBytes(int inData, byte[] b, int offset) + { + b[offset] = (byte)inData; + b[offset + 1] = (byte)(inData >> 8); + b[offset + 2] = (byte)(inData >> 16); + b[offset + 3] = (byte)(inData >> 24); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/DigitalSignature.cs b/Renci.SshNet/Security/Cryptography/DigitalSignature.cs index c49b1c0..e0a4e40 100644 --- a/Renci.SshNet/Security/Cryptography/DigitalSignature.cs +++ b/Renci.SshNet/Security/Cryptography/DigitalSignature.cs @@ -1,29 +1,23 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for signature implementations - /// - public abstract class DigitalSignature - { - /// - /// Verifies the signature. - /// - /// The input. - /// The signature. - /// True if signature was successfully verified; otherwise false. - public abstract bool Verify(byte[] input, byte[] signature); - - /// - /// Creates the signature. - /// - /// The input. - /// Signed input data. - public abstract byte[] Sign(byte[] input); - } -} +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Base class for signature implementations + /// + public abstract class DigitalSignature + { + /// + /// Verifies the signature. + /// + /// The input. + /// The signature. + /// True if signature was successfully verified; otherwise false. + public abstract bool Verify(byte[] input, byte[] signature); + + /// + /// Creates the signature. + /// + /// The input. + /// Signed input data. + public abstract byte[] Sign(byte[] input); + } +} diff --git a/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs b/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs index 8e98442..a9b47fc 100644 --- a/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs +++ b/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs @@ -1,200 +1,206 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Implements DSA digital signature algorithm. - /// - public class DsaDigitalSignature : DigitalSignature, IDisposable - { - private HashAlgorithm _hash; - - private DsaKey _key; - - /// - /// Initializes a new instance of the class. - /// - /// The DSA key. - /// key - public DsaDigitalSignature(DsaKey key) - { - if (key == null) - throw new ArgumentNullException("key"); - - this._key = key; - - this._hash = new SHA1Hash(); - } - - /// - /// Verifies the signature. - /// - /// The input. - /// The signature. - /// - /// True if signature was successfully verified; otherwise false. - /// - /// Invalid signature. - public override bool Verify(byte[] input, byte[] signature) - { - var hashInput = this._hash.ComputeHash(input); - - BigInteger hm = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray()); - - if (signature.Length != 40) - throw new InvalidOperationException("Invalid signature."); - - // Extract r and s numbers from the signature - var rBytes = new byte[21]; - var sBytes = new byte[21]; - - for (int i = 0, j = 20; i < 20; i++, j--) - { - rBytes[i] = signature[j - 1]; - sBytes[i] = signature[j + 20 - 1]; - } - - BigInteger r = new BigInteger(rBytes); - BigInteger s = new BigInteger(sBytes); - - // Reject the signature if 0 < r < q or 0 < s < q is not satisfied. - if (r <= 0 || r >= this._key.Q) - return false; - - if (s <= 0 || s >= this._key.Q) - return false; - - // Calculate w = s−1 mod q - BigInteger w = BigInteger.ModInverse(s, this._key.Q); - - // Calculate u1 = H(m)·w mod q - BigInteger u1 = hm * w % this._key.Q; - - // Calculate u2 = r * w mod q - BigInteger u2 = r * w % this._key.Q; - - u1 = BigInteger.ModPow(this._key.G, u1, this._key.P); - u2 = BigInteger.ModPow(this._key.Y, u2, this._key.P); - - // Calculate v = ((g pow u1 * y pow u2) mod p) mod q - BigInteger v = ((u1 * u2) % this._key.P) % this._key.Q; - - // The signature is valid if v = r - return v == r; - } - - /// - /// Creates the signature. - /// - /// The input. - /// - /// Signed input data. - /// - /// Invalid DSA key. - public override byte[] Sign(byte[] input) - { - var hashInput = this._hash.ComputeHash(input); - - BigInteger m = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray()); - - BigInteger s = BigInteger.Zero; - BigInteger r = BigInteger.Zero; - - do - { - BigInteger k = BigInteger.Zero; - - do - { - // Generate a random per-message value k where 0 < k < q - var bitLength = this._key.Q.BitLength; - - if (this._key.Q < BigInteger.Zero) - throw new SshException("Invalid DSA key."); - - while (k <= 0 || k >= this._key.Q) - { - k = BigInteger.Random(bitLength); - } - - // Calculate r = ((g pow k) mod p) mod q - r = BigInteger.ModPow(this._key.G, k, this._key.P) % this._key.Q; - - // In the unlikely case that r = 0, start again with a different random k - } while (r.IsZero); - - - // Calculate s = ((k pow −1)(H(m) + x*r)) mod q - k = (BigInteger.ModInverse(k, this._key.Q) * (m + this._key.X * r)); - - s = k % this._key.Q; - - // In the unlikely case that s = 0, start again with a different random k - } while (s.IsZero); - - // The signature is (r, s) - return r.ToByteArray().Reverse().TrimLeadingZero().Concat(s.ToByteArray().Reverse().TrimLeadingZero()).ToArray(); - } - - #region IDisposable Members - - private bool _isDisposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._hash != null) - { - this._hash.Clear(); - this._hash = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~DsaDigitalSignature() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - } -} +using System; +using System.Linq; +using System.Security.Cryptography; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Implements DSA digital signature algorithm. + /// + public class DsaDigitalSignature : DigitalSignature, IDisposable + { + private HashAlgorithm _hash; + + private readonly DsaKey _key; + + /// + /// Initializes a new instance of the class. + /// + /// The DSA key. + /// key + public DsaDigitalSignature(DsaKey key) + { + if (key == null) + throw new ArgumentNullException("key"); + + this._key = key; + + this._hash = new SHA1Hash(); + } + + /// + /// Verifies the signature. + /// + /// The input. + /// The signature. + /// + /// True if signature was successfully verified; otherwise false. + /// + /// Invalid signature. + public override bool Verify(byte[] input, byte[] signature) + { + var hashInput = this._hash.ComputeHash(input); + + BigInteger hm = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray()); + + if (signature.Length != 40) + throw new InvalidOperationException("Invalid signature."); + + // Extract r and s numbers from the signature + var rBytes = new byte[21]; + var sBytes = new byte[21]; + + for (int i = 0, j = 20; i < 20; i++, j--) + { + rBytes[i] = signature[j - 1]; + sBytes[i] = signature[j + 20 - 1]; + } + + BigInteger r = new BigInteger(rBytes); + BigInteger s = new BigInteger(sBytes); + + // Reject the signature if 0 < r < q or 0 < s < q is not satisfied. + if (r <= 0 || r >= this._key.Q) + return false; + + if (s <= 0 || s >= this._key.Q) + return false; + + // Calculate w = s−1 mod q + BigInteger w = BigInteger.ModInverse(s, this._key.Q); + + // Calculate u1 = H(m)·w mod q + BigInteger u1 = hm * w % this._key.Q; + + // Calculate u2 = r * w mod q + BigInteger u2 = r * w % this._key.Q; + + u1 = BigInteger.ModPow(this._key.G, u1, this._key.P); + u2 = BigInteger.ModPow(this._key.Y, u2, this._key.P); + + // Calculate v = ((g pow u1 * y pow u2) mod p) mod q + BigInteger v = ((u1 * u2) % this._key.P) % this._key.Q; + + // The signature is valid if v = r + return v == r; + } + + /// + /// Creates the signature. + /// + /// The input. + /// + /// Signed input data. + /// + /// Invalid DSA key. + public override byte[] Sign(byte[] input) + { + var hashInput = this._hash.ComputeHash(input); + + BigInteger m = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray()); + + BigInteger s; + BigInteger r; + + do + { + BigInteger k = BigInteger.Zero; + + do + { + // Generate a random per-message value k where 0 < k < q + var bitLength = this._key.Q.BitLength; + + if (this._key.Q < BigInteger.Zero) + throw new SshException("Invalid DSA key."); + + while (k <= 0 || k >= this._key.Q) + { + k = BigInteger.Random(bitLength); + } + + // Calculate r = ((g pow k) mod p) mod q + r = BigInteger.ModPow(this._key.G, k, this._key.P) % this._key.Q; + + // In the unlikely case that r = 0, start again with a different random k + } while (r.IsZero); + + + // Calculate s = ((k pow −1)(H(m) + x*r)) mod q + k = (BigInteger.ModInverse(k, this._key.Q) * (m + this._key.X * r)); + + s = k % this._key.Q; + + // In the unlikely case that s = 0, start again with a different random k + } while (s.IsZero); + + // The signature is (r, s) + var signature = new byte[40]; + + // issue #1918: pad part with zero's on the left if length is less than 20 + var rBytes = r.ToByteArray().Reverse().TrimLeadingZero().ToArray(); + Array.Copy(rBytes, 0, signature, 20 - rBytes.Length, rBytes.Length); + + // issue #1918: pad part with zero's on the left if length is less than 20 + var sBytes = s.ToByteArray().Reverse().TrimLeadingZero().ToArray(); + Array.Copy(sBytes, 0, signature, 40 - sBytes.Length, sBytes.Length); + + return signature; + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._hash != null) + { + this._hash.Clear(); + this._hash = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~DsaDigitalSignature() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Security/Cryptography/DsaKey.cs b/Renci.SshNet/Security/Cryptography/DsaKey.cs index 5abd074..b8565ae 100644 --- a/Renci.SshNet/Security/Cryptography/DsaKey.cs +++ b/Renci.SshNet/Security/Cryptography/DsaKey.cs @@ -1,213 +1,209 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Contains DSA private and public key - /// - public class DsaKey : Key, IDisposable - { - /// - /// Gets the P. - /// - public BigInteger P - { - get - { - return this._privateKey[0]; - } - } - - /// - /// Gets the Q. - /// - public BigInteger Q - { - get - { - return this._privateKey[1]; - } - } - - /// - /// Gets the G. - /// - public BigInteger G - { - get - { - return this._privateKey[2]; - } - } - - /// - /// Gets public key Y. - /// - public BigInteger Y - { - get - { - return this._privateKey[3]; - } - } - - /// - /// Gets private key X. - /// - public BigInteger X - { - get - { - return this._privateKey[4]; - } - } - - /// - /// Gets the length of the key. - /// - /// - /// The length of the key. - /// - public override int KeyLength - { - get - { - return this.P.BitLength; - } - } - - private DsaDigitalSignature _digitalSignature; - /// - /// Gets the digital signature. - /// - protected override DigitalSignature DigitalSignature - { - get - { - if (this._digitalSignature == null) - { - this._digitalSignature = new DsaDigitalSignature(this); - } - return this._digitalSignature; - } - } - - /// - /// Gets or sets the public. - /// - /// - /// The public. - /// - public override BigInteger[] Public - { - get - { - return new BigInteger[] { this.P, this.Q, this.G, this.Y }; - } - set - { - if (value.Length != 4) - throw new InvalidOperationException("Invalid public key."); - - this._privateKey = value; - } - } - - /// - /// Initializes a new instance of the class. - /// - public DsaKey() - : base() - { - this._privateKey = new BigInteger[5]; - } - - /// - /// Initializes a new instance of the class. - /// - /// DER encoded private key data. - public DsaKey(byte[] data) - : base(data) - { - if (this._privateKey.Length != 5) - throw new InvalidOperationException("Invalid private key."); - } - - /// - /// Initializes a new instance of the class. - /// - /// The p. - /// The q. - /// The g. - /// The y. - /// The x. - public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger x) - { - this._privateKey = new BigInteger[5]; - this._privateKey[0] = p; - this._privateKey[1] = q; - this._privateKey[2] = g; - this._privateKey[3] = y; - this._privateKey[4] = x; - } - - #region IDisposable Members - - private bool _isDisposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._digitalSignature != null) - { - this._digitalSignature.Dispose(); - this._digitalSignature = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~DsaKey() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Security +{ + /// + /// Contains DSA private and public key + /// + public class DsaKey : Key, IDisposable + { + /// + /// Gets the P. + /// + public BigInteger P + { + get + { + return this._privateKey[0]; + } + } + + /// + /// Gets the Q. + /// + public BigInteger Q + { + get + { + return this._privateKey[1]; + } + } + + /// + /// Gets the G. + /// + public BigInteger G + { + get + { + return this._privateKey[2]; + } + } + + /// + /// Gets public key Y. + /// + public BigInteger Y + { + get + { + return this._privateKey[3]; + } + } + + /// + /// Gets private key X. + /// + public BigInteger X + { + get + { + return this._privateKey[4]; + } + } + + /// + /// Gets the length of the key. + /// + /// + /// The length of the key. + /// + public override int KeyLength + { + get + { + return this.P.BitLength; + } + } + + private DsaDigitalSignature _digitalSignature; + /// + /// Gets the digital signature. + /// + protected override DigitalSignature DigitalSignature + { + get + { + if (this._digitalSignature == null) + { + this._digitalSignature = new DsaDigitalSignature(this); + } + return this._digitalSignature; + } + } + + /// + /// Gets or sets the public. + /// + /// + /// The public. + /// + public override BigInteger[] Public + { + get + { + return new BigInteger[] { this.P, this.Q, this.G, this.Y }; + } + set + { + if (value.Length != 4) + throw new InvalidOperationException("Invalid public key."); + + this._privateKey = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public DsaKey() + { + this._privateKey = new BigInteger[5]; + } + + /// + /// Initializes a new instance of the class. + /// + /// DER encoded private key data. + public DsaKey(byte[] data) + : base(data) + { + if (this._privateKey.Length != 5) + throw new InvalidOperationException("Invalid private key."); + } + + /// + /// Initializes a new instance of the class. + /// + /// The p. + /// The q. + /// The g. + /// The y. + /// The x. + public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger x) + { + this._privateKey = new BigInteger[5]; + this._privateKey[0] = p; + this._privateKey[1] = q; + this._privateKey[2] = g; + this._privateKey[3] = y; + this._privateKey[4] = x; + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._digitalSignature != null) + { + this._digitalSignature.Dispose(); + this._digitalSignature = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~DsaKey() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Security/Cryptography/HMAC.cs b/Renci.SshNet/Security/Cryptography/HMAC.cs index c93131c..e5941f6 100644 --- a/Renci.SshNet/Security/Cryptography/HMAC.cs +++ b/Renci.SshNet/Security/Cryptography/HMAC.cs @@ -1,170 +1,171 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Provides HMAC algorithm implementation. - /// - /// Class that implements . - public class HMac : KeyedHashAlgorithm where T : HashAlgorithm, new() - { - private HashAlgorithm _hash; - //private bool _isHashing; - private byte[] _innerPadding; - private byte[] _outerPadding; - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - protected int BlockSize - { - get - { - return this._hash.InputBlockSize; - } - } - - private HMac() - { - // Create the hash algorithms. - this._hash = new T(); - this.HashSizeValue = this._hash.HashSize; - } - - /// - /// Rfc 2104. - /// - /// The key. - public HMac(byte[] key, int hashSizeValue) - : this(key) - { - this.HashSizeValue = hashSizeValue; - } - - public HMac(byte[] key) - : this() - { - base.KeyValue = key; - - this.InternalInitialize(); - } - - - /// - /// Gets or sets the key to use in the hash algorithm. - /// - /// The key to use in the hash algorithm. - public override byte[] Key - { - get - { - return (byte[])base.KeyValue.Clone(); - } - set - { - this.SetKey(value); - } - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - /// - /// Hashes the core. - /// - /// The RGB. - /// The ib. - /// The cb. - protected override void HashCore(byte[] rgb, int ib, int cb) - { - this._hash.TransformBlock(rgb, ib, cb, rgb, ib); - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - // Finalize the original hash. - this._hash.TransformFinalBlock(new byte[0], 0, 0); - - var hashValue = this._hash.Hash; - - // Write the outer array. - this._hash.TransformBlock(this._outerPadding, 0, this.BlockSize, this._outerPadding, 0); - - // Write the inner hash and finalize the hash. - this._hash.TransformFinalBlock(hashValue, 0, hashValue.Length); - - return this._hash.Hash.Take(this.HashSize / 8).ToArray(); - } - - private void InternalInitialize() - { - this.SetKey(base.KeyValue); - } - - private void SetKey(byte[] value) - { - this._hash.Initialize(); - - if (value.Length > this.BlockSize) - { - this.KeyValue = this._hash.ComputeHash(value); - // No need to call Initialize, ComputeHash does it automatically. - } - else - { - this.KeyValue = value.Clone() as byte[]; - } - - this._innerPadding = new byte[this.BlockSize]; - this._outerPadding = new byte[this.BlockSize]; - - // Compute inner and outer padding. - int i = 0; - for (i = 0; i < this.KeyValue.Length; i++) - { - this._innerPadding[i] = (byte)(0x36 ^ this.KeyValue[i]); - this._outerPadding[i] = (byte)(0x5C ^ this.KeyValue[i]); - } - for (i = this.KeyValue.Length; i < this.BlockSize; i++) - { - this._innerPadding[i] = 0x36; - this._outerPadding[i] = 0x5C; - } - - this._hash.TransformBlock(this._innerPadding, 0, this.BlockSize, this._innerPadding, 0); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._hash != null) - { - this._hash.Clear(); - this._hash = null; - } - } - } -} +using System.Linq; +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Provides HMAC algorithm implementation. + /// + /// Class that implements . + public class HMac : KeyedHashAlgorithm where T : HashAlgorithm, new() + { + private HashAlgorithm _hash; + //private bool _isHashing; + private byte[] _innerPadding; + private byte[] _outerPadding; + + /// + /// Gets the size of the block. + /// + /// + /// The size of the block. + /// + protected int BlockSize + { + get + { + return this._hash.InputBlockSize; + } + } + + private HMac() + { + // Create the hash algorithms. + this._hash = new T(); + this.HashSizeValue = this._hash.HashSize; + } + + /// + /// Rfc 2104. + /// + /// The key. + /// The size, in bits, of the computed hash code. + public HMac(byte[] key, int hashSizeValue) + : this(key) + { + this.HashSizeValue = hashSizeValue; + } + + /// + /// Rfc 2104. + /// + /// The key. + public HMac(byte[] key) + : this() + { + base.KeyValue = key; + + this.InternalInitialize(); + } + + + /// + /// Gets or sets the key to use in the hash algorithm. + /// + /// The key to use in the hash algorithm. + public override byte[] Key + { + get + { + return (byte[])base.KeyValue.Clone(); + } + set + { + this.SetKey(value); + } + } + + /// + /// Initializes an implementation of the class. + /// + public override void Initialize() + { + this.InternalInitialize(); + } + + /// + /// Hashes the core. + /// + /// The RGB. + /// The ib. + /// The cb. + protected override void HashCore(byte[] rgb, int ib, int cb) + { + this._hash.TransformBlock(rgb, ib, cb, rgb, ib); + } + + /// + /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. + /// + /// + /// The computed hash code. + /// + protected override byte[] HashFinal() + { + // Finalize the original hash. + this._hash.TransformFinalBlock(new byte[0], 0, 0); + + var hashValue = this._hash.Hash; + + // Write the outer array. + this._hash.TransformBlock(this._outerPadding, 0, this.BlockSize, this._outerPadding, 0); + + // Write the inner hash and finalize the hash. + this._hash.TransformFinalBlock(hashValue, 0, hashValue.Length); + + return this._hash.Hash.Take(this.HashSize / 8).ToArray(); + } + + private void InternalInitialize() + { + this.SetKey(base.KeyValue); + } + + private void SetKey(byte[] value) + { + this._hash.Initialize(); + + if (value.Length > this.BlockSize) + { + this.KeyValue = this._hash.ComputeHash(value); + // No need to call Initialize, ComputeHash does it automatically. + } + else + { + this.KeyValue = (byte[]) value.Clone(); + } + + this._innerPadding = new byte[this.BlockSize]; + this._outerPadding = new byte[this.BlockSize]; + + // Compute inner and outer padding. + for (var i = 0; i < this.KeyValue.Length; i++) + { + this._innerPadding[i] = (byte)(0x36 ^ this.KeyValue[i]); + this._outerPadding[i] = (byte)(0x5C ^ this.KeyValue[i]); + } + for (var i = this.KeyValue.Length; i < this.BlockSize; i++) + { + this._innerPadding[i] = 0x36; + this._outerPadding[i] = 0x5C; + } + + this._hash.TransformBlock(this._innerPadding, 0, this.BlockSize, this._innerPadding, 0); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (this._hash != null) + { + this._hash.Clear(); + this._hash = null; + } + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/MD5Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/MD5Hash.cs index 48f103c..95dfb12 100644 --- a/Renci.SshNet/Security/Cryptography/Hashes/MD5Hash.cs +++ b/Renci.SshNet/Security/Cryptography/Hashes/MD5Hash.cs @@ -1,390 +1,385 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// MD5 algorithm implementation - /// - public sealed class MD5Hash : HashAlgorithm - { - private byte[] _buffer = new byte[4]; - private int _bufferOffset; - private long _byteCount; - private int H1, H2, H3, H4; // IV's - private int[] _hashValue = new int[16]; - private int _offset; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return 128; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public MD5Hash() - { - this.InternalInitialize(); - } - - /// - /// Routes data written to the object into the hash algorithm for computing the hash. - /// - /// The input to compute the hash code for. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - long bitLength = (this._byteCount << 3); - - // Add the pad bytes. - this.Update((byte)128); - - while (this._bufferOffset != 0) - this.Update((byte)0); - - if (this._offset > 14) - { - this.ProcessBlock(); - } - - this._hashValue[14] = (int)(bitLength & 0xffffffff); - this._hashValue[15] = (int)((ulong)bitLength >> 32); - - this.ProcessBlock(); - - var output = new byte[16]; - - this.UnpackWord(H1, output, 0); - this.UnpackWord(H2, output, 0 + 4); - this.UnpackWord(H3, output, 0 + 8); - this.UnpackWord(H4, output, 0 + 12); - - this.Initialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - private void InternalInitialize() - { - var i = 0; - this._byteCount = 0; - this._bufferOffset = 0; - for (i = 0; i < 4; i++) - { - this._buffer[i] = 0; - } - - H1 = unchecked((int)0x67452301); - H2 = unchecked((int)0xefcdab89); - H3 = unchecked((int)0x98badcfe); - H4 = unchecked((int)0x10325476); - - this._offset = 0; - for (i = 0; i != this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - private void ProcessWord(byte[] input, int inOff) - { - this._hashValue[this._offset++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) - | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); - - if (this._offset == 16) - { - ProcessBlock(); - } - } - - private void UnpackWord(int word, byte[] outBytes, int outOff) - { - outBytes[outOff] = (byte)word; - outBytes[outOff + 1] = (byte)((uint)word >> 8); - outBytes[outOff + 2] = (byte)((uint)word >> 16); - outBytes[outOff + 3] = (byte)((uint)word >> 24); - } - - // - // round 1 left rotates - // - private static readonly int S11 = 7; - private static readonly int S12 = 12; - private static readonly int S13 = 17; - private static readonly int S14 = 22; - - // - // round 2 left rotates - // - private static readonly int S21 = 5; - private static readonly int S22 = 9; - private static readonly int S23 = 14; - private static readonly int S24 = 20; - - // - // round 3 left rotates - // - private static readonly int S31 = 4; - private static readonly int S32 = 11; - private static readonly int S33 = 16; - private static readonly int S34 = 23; - - // - // round 4 left rotates - // - private static readonly int S41 = 6; - private static readonly int S42 = 10; - private static readonly int S43 = 15; - private static readonly int S44 = 21; - - /* - * rotate int x left n bits. - */ - private static int RotateLeft(int x, int n) - { - return (x << n) | (int)((uint)x >> (32 - n)); - } - - /* - * F, G, H and I are the basic MD5 functions. - */ - private static int F(int u, int v, int w) - { - return (u & v) | (~u & w); - } - - private static int G(int u, int v, int w) - { - return (u & w) | (v & ~w); - } - - private static int H(int u, int v, int w) - { - return u ^ v ^ w; - } - - private static int K(int u, int v, int w) - { - return v ^ (u | ~w); - } - - private void ProcessBlock() - { - int a = H1; - int b = H2; - int c = H3; - int d = H4; - - // - // Round 1 - F cycle, 16 times. - // - a = RotateLeft((a + F(b, c, d) + this._hashValue[0] + unchecked((int)0xd76aa478)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[1] + unchecked((int)0xe8c7b756)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[2] + unchecked((int)0x242070db)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[3] + unchecked((int)0xc1bdceee)), S14) + c; - a = RotateLeft((a + F(b, c, d) + this._hashValue[4] + unchecked((int)0xf57c0faf)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[5] + unchecked((int)0x4787c62a)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[6] + unchecked((int)0xa8304613)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[7] + unchecked((int)0xfd469501)), S14) + c; - a = RotateLeft((a + F(b, c, d) + this._hashValue[8] + unchecked((int)0x698098d8)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[9] + unchecked((int)0x8b44f7af)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[10] + unchecked((int)0xffff5bb1)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[11] + unchecked((int)0x895cd7be)), S14) + c; - a = RotateLeft((a + F(b, c, d) + this._hashValue[12] + unchecked((int)0x6b901122)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[13] + unchecked((int)0xfd987193)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[14] + unchecked((int)0xa679438e)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[15] + unchecked((int)0x49b40821)), S14) + c; - - // - // Round 2 - G cycle, 16 times. - // - a = RotateLeft((a + G(b, c, d) + this._hashValue[1] + unchecked((int)0xf61e2562)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[6] + unchecked((int)0xc040b340)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[11] + unchecked((int)0x265e5a51)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[0] + unchecked((int)0xe9b6c7aa)), S24) + c; - a = RotateLeft((a + G(b, c, d) + this._hashValue[5] + unchecked((int)0xd62f105d)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[10] + unchecked((int)0x02441453)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[15] + unchecked((int)0xd8a1e681)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[4] + unchecked((int)0xe7d3fbc8)), S24) + c; - a = RotateLeft((a + G(b, c, d) + this._hashValue[9] + unchecked((int)0x21e1cde6)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[14] + unchecked((int)0xc33707d6)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[3] + unchecked((int)0xf4d50d87)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[8] + unchecked((int)0x455a14ed)), S24) + c; - a = RotateLeft((a + G(b, c, d) + this._hashValue[13] + unchecked((int)0xa9e3e905)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[2] + unchecked((int)0xfcefa3f8)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[7] + unchecked((int)0x676f02d9)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[12] + unchecked((int)0x8d2a4c8a)), S24) + c; - - // - // Round 3 - H cycle, 16 times. - // - a = RotateLeft((a + H(b, c, d) + this._hashValue[5] + unchecked((int)0xfffa3942)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[8] + unchecked((int)0x8771f681)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[11] + unchecked((int)0x6d9d6122)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[14] + unchecked((int)0xfde5380c)), S34) + c; - a = RotateLeft((a + H(b, c, d) + this._hashValue[1] + unchecked((int)0xa4beea44)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[4] + unchecked((int)0x4bdecfa9)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[7] + unchecked((int)0xf6bb4b60)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[10] + unchecked((int)0xbebfbc70)), S34) + c; - a = RotateLeft((a + H(b, c, d) + this._hashValue[13] + unchecked((int)0x289b7ec6)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[0] + unchecked((int)0xeaa127fa)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[3] + unchecked((int)0xd4ef3085)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[6] + unchecked((int)0x04881d05)), S34) + c; - a = RotateLeft((a + H(b, c, d) + this._hashValue[9] + unchecked((int)0xd9d4d039)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[12] + unchecked((int)0xe6db99e5)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[15] + unchecked((int)0x1fa27cf8)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[2] + unchecked((int)0xc4ac5665)), S34) + c; - - // - // Round 4 - K cycle, 16 times. - // - a = RotateLeft((a + K(b, c, d) + this._hashValue[0] + unchecked((int)0xf4292244)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[7] + unchecked((int)0x432aff97)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[14] + unchecked((int)0xab9423a7)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[5] + unchecked((int)0xfc93a039)), S44) + c; - a = RotateLeft((a + K(b, c, d) + this._hashValue[12] + unchecked((int)0x655b59c3)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[3] + unchecked((int)0x8f0ccc92)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[10] + unchecked((int)0xffeff47d)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[1] + unchecked((int)0x85845dd1)), S44) + c; - a = RotateLeft((a + K(b, c, d) + this._hashValue[8] + unchecked((int)0x6fa87e4f)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[15] + unchecked((int)0xfe2ce6e0)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[6] + unchecked((int)0xa3014314)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[13] + unchecked((int)0x4e0811a1)), S44) + c; - a = RotateLeft((a + K(b, c, d) + this._hashValue[4] + unchecked((int)0xf7537e82)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[11] + unchecked((int)0xbd3af235)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[2] + unchecked((int)0x2ad7d2bb)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[9] + unchecked((int)0xeb86d391)), S44) + c; - - H1 += a; - H2 += b; - H3 += c; - H4 += d; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i != this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - } -} +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// MD5 algorithm implementation + /// + public sealed class MD5Hash : HashAlgorithm + { + private readonly byte[] _buffer = new byte[4]; + private int _bufferOffset; + private long _byteCount; + private int H1, H2, H3, H4; // IV's + private readonly int[] _hashValue = new int[16]; + private int _offset; + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// The size, in bits, of the computed hash code. + public override int HashSize + { + get + { + return 128; + } + } + + /// + /// Gets the input block size. + /// + /// The input block size. + public override int InputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets the output block size. + /// + /// The output block size. + public override int OutputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + /// Always true. + public override bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + /// true if multiple blocks can be transformed; otherwise, false. + public override bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + /// + /// Initializes a new instance of the class. + /// + public MD5Hash() + { + this.InternalInitialize(); + } + + /// + /// Routes data written to the object into the hash algorithm for computing the hash. + /// + /// The input to compute the hash code for. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the byte array to use as data. + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + // Fill the current word + while ((this._bufferOffset != 0) && (cbSize > 0)) + { + this.Update(array[ibStart]); + ibStart++; + cbSize--; + } + + // Process whole words. + while (cbSize > this._buffer.Length) + { + this.ProcessWord(array, ibStart); + + ibStart += this._buffer.Length; + cbSize -= this._buffer.Length; + this._byteCount += this._buffer.Length; + } + + // Load in the remainder. + while (cbSize > 0) + { + this.Update(array[ibStart]); + + ibStart++; + cbSize--; + } + } + + /// + /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. + /// + /// + /// The computed hash code. + /// + protected override byte[] HashFinal() + { + long bitLength = (this._byteCount << 3); + + // Add the pad bytes. + this.Update((byte)128); + + while (this._bufferOffset != 0) + this.Update((byte)0); + + if (this._offset > 14) + { + this.ProcessBlock(); + } + + this._hashValue[14] = (int)(bitLength & 0xffffffff); + this._hashValue[15] = (int)((ulong)bitLength >> 32); + + this.ProcessBlock(); + + var output = new byte[16]; + + this.UnpackWord(H1, output, 0); + this.UnpackWord(H2, output, 0 + 4); + this.UnpackWord(H3, output, 0 + 8); + this.UnpackWord(H4, output, 0 + 12); + + this.Initialize(); + + return output; + } + + /// + /// Initializes an implementation of the class. + /// + public override void Initialize() + { + this.InternalInitialize(); + } + + private void InternalInitialize() + { + this._byteCount = 0; + this._bufferOffset = 0; + for (var i = 0; i < 4; i++) + { + this._buffer[i] = 0; + } + + H1 = unchecked((int)0x67452301); + H2 = unchecked((int)0xefcdab89); + H3 = unchecked((int)0x98badcfe); + H4 = unchecked((int)0x10325476); + + this._offset = 0; + for (var i = 0; i != this._hashValue.Length; i++) + { + this._hashValue[i] = 0; + } + } + + private void Update(byte input) + { + this._buffer[this._bufferOffset++] = input; + + if (this._bufferOffset == this._buffer.Length) + { + this.ProcessWord(this._buffer, 0); + this._bufferOffset = 0; + } + + this._byteCount++; + } + + private void ProcessWord(byte[] input, int inOff) + { + this._hashValue[this._offset++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (this._offset == 16) + { + ProcessBlock(); + } + } + + private void UnpackWord(int word, byte[] outBytes, int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint)word >> 8); + outBytes[outOff + 2] = (byte)((uint)word >> 16); + outBytes[outOff + 3] = (byte)((uint)word >> 24); + } + + // + // round 1 left rotates + // + private const int S11 = 7; + private const int S12 = 12; + private const int S13 = 17; + private const int S14 = 22; + + // + // round 2 left rotates + // + private const int S21 = 5; + private const int S22 = 9; + private const int S23 = 14; + private const int S24 = 20; + + // + // round 3 left rotates + // + private const int S31 = 4; + private const int S32 = 11; + private const int S33 = 16; + private const int S34 = 23; + + // + // round 4 left rotates + // + private const int S41 = 6; + private const int S42 = 10; + private const int S43 = 15; + private const int S44 = 21; + + /* + * rotate int x left n bits. + */ + private static int RotateLeft(int x, int n) + { + return (x << n) | (int)((uint)x >> (32 - n)); + } + + /* + * F, G, H and I are the basic MD5 functions. + */ + private static int F(int u, int v, int w) + { + return (u & v) | (~u & w); + } + + private static int G(int u, int v, int w) + { + return (u & w) | (v & ~w); + } + + private static int H(int u, int v, int w) + { + return u ^ v ^ w; + } + + private static int K(int u, int v, int w) + { + return v ^ (u | ~w); + } + + private void ProcessBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = RotateLeft((a + F(b, c, d) + this._hashValue[0] + unchecked((int)0xd76aa478)), S11) + b; + d = RotateLeft((d + F(a, b, c) + this._hashValue[1] + unchecked((int)0xe8c7b756)), S12) + a; + c = RotateLeft((c + F(d, a, b) + this._hashValue[2] + unchecked((int)0x242070db)), S13) + d; + b = RotateLeft((b + F(c, d, a) + this._hashValue[3] + unchecked((int)0xc1bdceee)), S14) + c; + a = RotateLeft((a + F(b, c, d) + this._hashValue[4] + unchecked((int)0xf57c0faf)), S11) + b; + d = RotateLeft((d + F(a, b, c) + this._hashValue[5] + unchecked((int)0x4787c62a)), S12) + a; + c = RotateLeft((c + F(d, a, b) + this._hashValue[6] + unchecked((int)0xa8304613)), S13) + d; + b = RotateLeft((b + F(c, d, a) + this._hashValue[7] + unchecked((int)0xfd469501)), S14) + c; + a = RotateLeft((a + F(b, c, d) + this._hashValue[8] + unchecked((int)0x698098d8)), S11) + b; + d = RotateLeft((d + F(a, b, c) + this._hashValue[9] + unchecked((int)0x8b44f7af)), S12) + a; + c = RotateLeft((c + F(d, a, b) + this._hashValue[10] + unchecked((int)0xffff5bb1)), S13) + d; + b = RotateLeft((b + F(c, d, a) + this._hashValue[11] + unchecked((int)0x895cd7be)), S14) + c; + a = RotateLeft((a + F(b, c, d) + this._hashValue[12] + unchecked((int)0x6b901122)), S11) + b; + d = RotateLeft((d + F(a, b, c) + this._hashValue[13] + unchecked((int)0xfd987193)), S12) + a; + c = RotateLeft((c + F(d, a, b) + this._hashValue[14] + unchecked((int)0xa679438e)), S13) + d; + b = RotateLeft((b + F(c, d, a) + this._hashValue[15] + unchecked((int)0x49b40821)), S14) + c; + + // + // Round 2 - G cycle, 16 times. + // + a = RotateLeft((a + G(b, c, d) + this._hashValue[1] + unchecked((int)0xf61e2562)), S21) + b; + d = RotateLeft((d + G(a, b, c) + this._hashValue[6] + unchecked((int)0xc040b340)), S22) + a; + c = RotateLeft((c + G(d, a, b) + this._hashValue[11] + unchecked((int)0x265e5a51)), S23) + d; + b = RotateLeft((b + G(c, d, a) + this._hashValue[0] + unchecked((int)0xe9b6c7aa)), S24) + c; + a = RotateLeft((a + G(b, c, d) + this._hashValue[5] + unchecked((int)0xd62f105d)), S21) + b; + d = RotateLeft((d + G(a, b, c) + this._hashValue[10] + unchecked((int)0x02441453)), S22) + a; + c = RotateLeft((c + G(d, a, b) + this._hashValue[15] + unchecked((int)0xd8a1e681)), S23) + d; + b = RotateLeft((b + G(c, d, a) + this._hashValue[4] + unchecked((int)0xe7d3fbc8)), S24) + c; + a = RotateLeft((a + G(b, c, d) + this._hashValue[9] + unchecked((int)0x21e1cde6)), S21) + b; + d = RotateLeft((d + G(a, b, c) + this._hashValue[14] + unchecked((int)0xc33707d6)), S22) + a; + c = RotateLeft((c + G(d, a, b) + this._hashValue[3] + unchecked((int)0xf4d50d87)), S23) + d; + b = RotateLeft((b + G(c, d, a) + this._hashValue[8] + unchecked((int)0x455a14ed)), S24) + c; + a = RotateLeft((a + G(b, c, d) + this._hashValue[13] + unchecked((int)0xa9e3e905)), S21) + b; + d = RotateLeft((d + G(a, b, c) + this._hashValue[2] + unchecked((int)0xfcefa3f8)), S22) + a; + c = RotateLeft((c + G(d, a, b) + this._hashValue[7] + unchecked((int)0x676f02d9)), S23) + d; + b = RotateLeft((b + G(c, d, a) + this._hashValue[12] + unchecked((int)0x8d2a4c8a)), S24) + c; + + // + // Round 3 - H cycle, 16 times. + // + a = RotateLeft((a + H(b, c, d) + this._hashValue[5] + unchecked((int)0xfffa3942)), S31) + b; + d = RotateLeft((d + H(a, b, c) + this._hashValue[8] + unchecked((int)0x8771f681)), S32) + a; + c = RotateLeft((c + H(d, a, b) + this._hashValue[11] + unchecked((int)0x6d9d6122)), S33) + d; + b = RotateLeft((b + H(c, d, a) + this._hashValue[14] + unchecked((int)0xfde5380c)), S34) + c; + a = RotateLeft((a + H(b, c, d) + this._hashValue[1] + unchecked((int)0xa4beea44)), S31) + b; + d = RotateLeft((d + H(a, b, c) + this._hashValue[4] + unchecked((int)0x4bdecfa9)), S32) + a; + c = RotateLeft((c + H(d, a, b) + this._hashValue[7] + unchecked((int)0xf6bb4b60)), S33) + d; + b = RotateLeft((b + H(c, d, a) + this._hashValue[10] + unchecked((int)0xbebfbc70)), S34) + c; + a = RotateLeft((a + H(b, c, d) + this._hashValue[13] + unchecked((int)0x289b7ec6)), S31) + b; + d = RotateLeft((d + H(a, b, c) + this._hashValue[0] + unchecked((int)0xeaa127fa)), S32) + a; + c = RotateLeft((c + H(d, a, b) + this._hashValue[3] + unchecked((int)0xd4ef3085)), S33) + d; + b = RotateLeft((b + H(c, d, a) + this._hashValue[6] + unchecked((int)0x04881d05)), S34) + c; + a = RotateLeft((a + H(b, c, d) + this._hashValue[9] + unchecked((int)0xd9d4d039)), S31) + b; + d = RotateLeft((d + H(a, b, c) + this._hashValue[12] + unchecked((int)0xe6db99e5)), S32) + a; + c = RotateLeft((c + H(d, a, b) + this._hashValue[15] + unchecked((int)0x1fa27cf8)), S33) + d; + b = RotateLeft((b + H(c, d, a) + this._hashValue[2] + unchecked((int)0xc4ac5665)), S34) + c; + + // + // Round 4 - K cycle, 16 times. + // + a = RotateLeft((a + K(b, c, d) + this._hashValue[0] + unchecked((int)0xf4292244)), S41) + b; + d = RotateLeft((d + K(a, b, c) + this._hashValue[7] + unchecked((int)0x432aff97)), S42) + a; + c = RotateLeft((c + K(d, a, b) + this._hashValue[14] + unchecked((int)0xab9423a7)), S43) + d; + b = RotateLeft((b + K(c, d, a) + this._hashValue[5] + unchecked((int)0xfc93a039)), S44) + c; + a = RotateLeft((a + K(b, c, d) + this._hashValue[12] + unchecked((int)0x655b59c3)), S41) + b; + d = RotateLeft((d + K(a, b, c) + this._hashValue[3] + unchecked((int)0x8f0ccc92)), S42) + a; + c = RotateLeft((c + K(d, a, b) + this._hashValue[10] + unchecked((int)0xffeff47d)), S43) + d; + b = RotateLeft((b + K(c, d, a) + this._hashValue[1] + unchecked((int)0x85845dd1)), S44) + c; + a = RotateLeft((a + K(b, c, d) + this._hashValue[8] + unchecked((int)0x6fa87e4f)), S41) + b; + d = RotateLeft((d + K(a, b, c) + this._hashValue[15] + unchecked((int)0xfe2ce6e0)), S42) + a; + c = RotateLeft((c + K(d, a, b) + this._hashValue[6] + unchecked((int)0xa3014314)), S43) + d; + b = RotateLeft((b + K(c, d, a) + this._hashValue[13] + unchecked((int)0x4e0811a1)), S44) + c; + a = RotateLeft((a + K(b, c, d) + this._hashValue[4] + unchecked((int)0xf7537e82)), S41) + b; + d = RotateLeft((d + K(a, b, c) + this._hashValue[11] + unchecked((int)0xbd3af235)), S42) + a; + c = RotateLeft((c + K(d, a, b) + this._hashValue[2] + unchecked((int)0x2ad7d2bb)), S43) + d; + b = RotateLeft((b + K(c, d, a) + this._hashValue[9] + unchecked((int)0xeb86d391)), S44) + c; + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + this._offset = 0; + for (int i = 0; i != this._hashValue.Length; i++) + { + this._hashValue[i] = 0; + } + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs index 64e15d2..def23f7 100644 --- a/Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs +++ b/Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs @@ -1,522 +1,518 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// - /// - public sealed class RIPEMD160Hash : HashAlgorithm - { - private const int DIGEST_SIZE = 20; - - private byte[] _buffer; - private int _bufferOffset; - private long _byteCount; - private int _offset; - private int H0, H1, H2, H3, H4; // IV's - private int[] X = new int[16]; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // - // fill the current word - // - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // - // process whole words. - // - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // - // load in the remainder. - // - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - long bitLength = (this._byteCount << 3); - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - Update((byte)0); - ProcessLength(bitLength); - ProcessBlock(); - - UnpackWord(H0, output, 0); - UnpackWord(H1, output, 4); - UnpackWord(H2, output, 8); - UnpackWord(H3, output, 12); - UnpackWord(H4, output, 16); - - this.InternalInitialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - /// - /// Initializes a new instance of the class. - /// - public RIPEMD160Hash() - { - this._buffer = new byte[4]; - this.InternalInitialize(); - } - - private void ProcessWord(byte[] input, int inOff) - { - this.X[this._offset++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) - | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); - - if (this._offset == 16) - { - ProcessBlock(); - } - } - - private void ProcessLength(long bitLength) - { - if (this._offset > 14) - { - ProcessBlock(); - } - - this.X[14] = (int)(bitLength & 0xffffffff); - this.X[15] = (int)((ulong)bitLength >> 32); - } - - private void UnpackWord(int word, byte[] outBytes, int outOff) - { - outBytes[outOff] = (byte)word; - outBytes[outOff + 1] = (byte)((uint)word >> 8); - outBytes[outOff + 2] = (byte)((uint)word >> 16); - outBytes[outOff + 3] = (byte)((uint)word >> 24); - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - /// - /// Reset the chaining variables to the IV values. - /// - private void InternalInitialize() - { - this._byteCount = 0; - this._bufferOffset = 0; - for (int i = 0; i < _buffer.Length; i++) - { - this._buffer[i] = 0; - } - - H0 = unchecked((int)0x67452301); - H1 = unchecked((int)0xefcdab89); - H2 = unchecked((int)0x98badcfe); - H3 = unchecked((int)0x10325476); - H4 = unchecked((int)0xc3d2e1f0); - - this._offset = 0; - - for (int i = 0; i != X.Length; i++) - { - this.X[i] = 0; - } - } - - private int RL(int x, int n) - { - return (x << n) | (int)((uint)x >> (32 - n)); - } - - /// - /// Rounds 0-15 - /// - /// The x. - /// The y. - /// The z. - /// - private int F1(int x, int y, int z) - { - return x ^ y ^ z; - } - - /// - /// Rounds 16-31 - /// - /// The x. - /// The y. - /// The z. - /// - private int F2(int x, int y, int z) - { - return (x & y) | (~x & z); - } - - /// - /// ounds 32-47 - /// - /// The x. - /// The y. - /// The z. - /// - private int F3(int x, int y, int z) - { - return (x | ~y) ^ z; - } - - /// - /// Rounds 48-63 - /// - /// The x. - /// The y. - /// The z. - /// - private int F4(int x, int y, int z) - { - return (x & z) | (y & ~z); - } - - /// - /// ounds 64-79 - /// - /// The x. - /// The y. - /// The z. - /// - private int F5(int x, int y, int z) - { - return x ^ (y | ~z); - } - - private void ProcessBlock() - { - int a, aa; - int b, bb; - int c, cc; - int d, dd; - int e, ee; - - a = aa = H0; - b = bb = H1; - c = cc = H2; - d = dd = H3; - e = ee = H4; - - // - // Rounds 1 - 16 - // - // left - a = RL(a + F1(b, c, d) + this.X[0], 11) + e; c = RL(c, 10); - e = RL(e + F1(a, b, c) + this.X[1], 14) + d; b = RL(b, 10); - d = RL(d + F1(e, a, b) + this.X[2], 15) + c; a = RL(a, 10); - c = RL(c + F1(d, e, a) + this.X[3], 12) + b; e = RL(e, 10); - b = RL(b + F1(c, d, e) + this.X[4], 5) + a; d = RL(d, 10); - a = RL(a + F1(b, c, d) + this.X[5], 8) + e; c = RL(c, 10); - e = RL(e + F1(a, b, c) + this.X[6], 7) + d; b = RL(b, 10); - d = RL(d + F1(e, a, b) + this.X[7], 9) + c; a = RL(a, 10); - c = RL(c + F1(d, e, a) + this.X[8], 11) + b; e = RL(e, 10); - b = RL(b + F1(c, d, e) + this.X[9], 13) + a; d = RL(d, 10); - a = RL(a + F1(b, c, d) + this.X[10], 14) + e; c = RL(c, 10); - e = RL(e + F1(a, b, c) + this.X[11], 15) + d; b = RL(b, 10); - d = RL(d + F1(e, a, b) + this.X[12], 6) + c; a = RL(a, 10); - c = RL(c + F1(d, e, a) + this.X[13], 7) + b; e = RL(e, 10); - b = RL(b + F1(c, d, e) + this.X[14], 9) + a; d = RL(d, 10); - a = RL(a + F1(b, c, d) + this.X[15], 8) + e; c = RL(c, 10); - - // right - aa = RL(aa + F5(bb, cc, dd) + this.X[5] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); - ee = RL(ee + F5(aa, bb, cc) + this.X[14] + unchecked((int)0x50a28be6), 9) + dd; bb = RL(bb, 10); - dd = RL(dd + F5(ee, aa, bb) + this.X[7] + unchecked((int)0x50a28be6), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F5(dd, ee, aa) + this.X[0] + unchecked((int)0x50a28be6), 11) + bb; ee = RL(ee, 10); - bb = RL(bb + F5(cc, dd, ee) + this.X[9] + unchecked((int)0x50a28be6), 13) + aa; dd = RL(dd, 10); - aa = RL(aa + F5(bb, cc, dd) + this.X[2] + unchecked((int)0x50a28be6), 15) + ee; cc = RL(cc, 10); - ee = RL(ee + F5(aa, bb, cc) + this.X[11] + unchecked((int)0x50a28be6), 15) + dd; bb = RL(bb, 10); - dd = RL(dd + F5(ee, aa, bb) + this.X[4] + unchecked((int)0x50a28be6), 5) + cc; aa = RL(aa, 10); - cc = RL(cc + F5(dd, ee, aa) + this.X[13] + unchecked((int)0x50a28be6), 7) + bb; ee = RL(ee, 10); - bb = RL(bb + F5(cc, dd, ee) + this.X[6] + unchecked((int)0x50a28be6), 7) + aa; dd = RL(dd, 10); - aa = RL(aa + F5(bb, cc, dd) + this.X[15] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); - ee = RL(ee + F5(aa, bb, cc) + this.X[8] + unchecked((int)0x50a28be6), 11) + dd; bb = RL(bb, 10); - dd = RL(dd + F5(ee, aa, bb) + this.X[1] + unchecked((int)0x50a28be6), 14) + cc; aa = RL(aa, 10); - cc = RL(cc + F5(dd, ee, aa) + this.X[10] + unchecked((int)0x50a28be6), 14) + bb; ee = RL(ee, 10); - bb = RL(bb + F5(cc, dd, ee) + this.X[3] + unchecked((int)0x50a28be6), 12) + aa; dd = RL(dd, 10); - aa = RL(aa + F5(bb, cc, dd) + this.X[12] + unchecked((int)0x50a28be6), 6) + ee; cc = RL(cc, 10); - - // - // Rounds 16-31 - // - // left - e = RL(e + F2(a, b, c) + this.X[7] + unchecked((int)0x5a827999), 7) + d; b = RL(b, 10); - d = RL(d + F2(e, a, b) + this.X[4] + unchecked((int)0x5a827999), 6) + c; a = RL(a, 10); - c = RL(c + F2(d, e, a) + this.X[13] + unchecked((int)0x5a827999), 8) + b; e = RL(e, 10); - b = RL(b + F2(c, d, e) + this.X[1] + unchecked((int)0x5a827999), 13) + a; d = RL(d, 10); - a = RL(a + F2(b, c, d) + this.X[10] + unchecked((int)0x5a827999), 11) + e; c = RL(c, 10); - e = RL(e + F2(a, b, c) + this.X[6] + unchecked((int)0x5a827999), 9) + d; b = RL(b, 10); - d = RL(d + F2(e, a, b) + this.X[15] + unchecked((int)0x5a827999), 7) + c; a = RL(a, 10); - c = RL(c + F2(d, e, a) + this.X[3] + unchecked((int)0x5a827999), 15) + b; e = RL(e, 10); - b = RL(b + F2(c, d, e) + this.X[12] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); - a = RL(a + F2(b, c, d) + this.X[0] + unchecked((int)0x5a827999), 12) + e; c = RL(c, 10); - e = RL(e + F2(a, b, c) + this.X[9] + unchecked((int)0x5a827999), 15) + d; b = RL(b, 10); - d = RL(d + F2(e, a, b) + this.X[5] + unchecked((int)0x5a827999), 9) + c; a = RL(a, 10); - c = RL(c + F2(d, e, a) + this.X[2] + unchecked((int)0x5a827999), 11) + b; e = RL(e, 10); - b = RL(b + F2(c, d, e) + this.X[14] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); - a = RL(a + F2(b, c, d) + this.X[11] + unchecked((int)0x5a827999), 13) + e; c = RL(c, 10); - e = RL(e + F2(a, b, c) + this.X[8] + unchecked((int)0x5a827999), 12) + d; b = RL(b, 10); - - // right - ee = RL(ee + F4(aa, bb, cc) + this.X[6] + unchecked((int)0x5c4dd124), 9) + dd; bb = RL(bb, 10); - dd = RL(dd + F4(ee, aa, bb) + this.X[11] + unchecked((int)0x5c4dd124), 13) + cc; aa = RL(aa, 10); - cc = RL(cc + F4(dd, ee, aa) + this.X[3] + unchecked((int)0x5c4dd124), 15) + bb; ee = RL(ee, 10); - bb = RL(bb + F4(cc, dd, ee) + this.X[7] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); - aa = RL(aa + F4(bb, cc, dd) + this.X[0] + unchecked((int)0x5c4dd124), 12) + ee; cc = RL(cc, 10); - ee = RL(ee + F4(aa, bb, cc) + this.X[13] + unchecked((int)0x5c4dd124), 8) + dd; bb = RL(bb, 10); - dd = RL(dd + F4(ee, aa, bb) + this.X[5] + unchecked((int)0x5c4dd124), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F4(dd, ee, aa) + this.X[10] + unchecked((int)0x5c4dd124), 11) + bb; ee = RL(ee, 10); - bb = RL(bb + F4(cc, dd, ee) + this.X[14] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); - aa = RL(aa + F4(bb, cc, dd) + this.X[15] + unchecked((int)0x5c4dd124), 7) + ee; cc = RL(cc, 10); - ee = RL(ee + F4(aa, bb, cc) + this.X[8] + unchecked((int)0x5c4dd124), 12) + dd; bb = RL(bb, 10); - dd = RL(dd + F4(ee, aa, bb) + this.X[12] + unchecked((int)0x5c4dd124), 7) + cc; aa = RL(aa, 10); - cc = RL(cc + F4(dd, ee, aa) + this.X[4] + unchecked((int)0x5c4dd124), 6) + bb; ee = RL(ee, 10); - bb = RL(bb + F4(cc, dd, ee) + this.X[9] + unchecked((int)0x5c4dd124), 15) + aa; dd = RL(dd, 10); - aa = RL(aa + F4(bb, cc, dd) + this.X[1] + unchecked((int)0x5c4dd124), 13) + ee; cc = RL(cc, 10); - ee = RL(ee + F4(aa, bb, cc) + this.X[2] + unchecked((int)0x5c4dd124), 11) + dd; bb = RL(bb, 10); - - // - // Rounds 32-47 - // - // left - d = RL(d + F3(e, a, b) + this.X[3] + unchecked((int)0x6ed9eba1), 11) + c; a = RL(a, 10); - c = RL(c + F3(d, e, a) + this.X[10] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); - b = RL(b + F3(c, d, e) + this.X[14] + unchecked((int)0x6ed9eba1), 6) + a; d = RL(d, 10); - a = RL(a + F3(b, c, d) + this.X[4] + unchecked((int)0x6ed9eba1), 7) + e; c = RL(c, 10); - e = RL(e + F3(a, b, c) + this.X[9] + unchecked((int)0x6ed9eba1), 14) + d; b = RL(b, 10); - d = RL(d + F3(e, a, b) + this.X[15] + unchecked((int)0x6ed9eba1), 9) + c; a = RL(a, 10); - c = RL(c + F3(d, e, a) + this.X[8] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); - b = RL(b + F3(c, d, e) + this.X[1] + unchecked((int)0x6ed9eba1), 15) + a; d = RL(d, 10); - a = RL(a + F3(b, c, d) + this.X[2] + unchecked((int)0x6ed9eba1), 14) + e; c = RL(c, 10); - e = RL(e + F3(a, b, c) + this.X[7] + unchecked((int)0x6ed9eba1), 8) + d; b = RL(b, 10); - d = RL(d + F3(e, a, b) + this.X[0] + unchecked((int)0x6ed9eba1), 13) + c; a = RL(a, 10); - c = RL(c + F3(d, e, a) + this.X[6] + unchecked((int)0x6ed9eba1), 6) + b; e = RL(e, 10); - b = RL(b + F3(c, d, e) + this.X[13] + unchecked((int)0x6ed9eba1), 5) + a; d = RL(d, 10); - a = RL(a + F3(b, c, d) + this.X[11] + unchecked((int)0x6ed9eba1), 12) + e; c = RL(c, 10); - e = RL(e + F3(a, b, c) + this.X[5] + unchecked((int)0x6ed9eba1), 7) + d; b = RL(b, 10); - d = RL(d + F3(e, a, b) + this.X[12] + unchecked((int)0x6ed9eba1), 5) + c; a = RL(a, 10); - - // right - dd = RL(dd + F3(ee, aa, bb) + this.X[15] + unchecked((int)0x6d703ef3), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F3(dd, ee, aa) + this.X[5] + unchecked((int)0x6d703ef3), 7) + bb; ee = RL(ee, 10); - bb = RL(bb + F3(cc, dd, ee) + this.X[1] + unchecked((int)0x6d703ef3), 15) + aa; dd = RL(dd, 10); - aa = RL(aa + F3(bb, cc, dd) + this.X[3] + unchecked((int)0x6d703ef3), 11) + ee; cc = RL(cc, 10); - ee = RL(ee + F3(aa, bb, cc) + this.X[7] + unchecked((int)0x6d703ef3), 8) + dd; bb = RL(bb, 10); - dd = RL(dd + F3(ee, aa, bb) + this.X[14] + unchecked((int)0x6d703ef3), 6) + cc; aa = RL(aa, 10); - cc = RL(cc + F3(dd, ee, aa) + this.X[6] + unchecked((int)0x6d703ef3), 6) + bb; ee = RL(ee, 10); - bb = RL(bb + F3(cc, dd, ee) + this.X[9] + unchecked((int)0x6d703ef3), 14) + aa; dd = RL(dd, 10); - aa = RL(aa + F3(bb, cc, dd) + this.X[11] + unchecked((int)0x6d703ef3), 12) + ee; cc = RL(cc, 10); - ee = RL(ee + F3(aa, bb, cc) + this.X[8] + unchecked((int)0x6d703ef3), 13) + dd; bb = RL(bb, 10); - dd = RL(dd + F3(ee, aa, bb) + this.X[12] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); - cc = RL(cc + F3(dd, ee, aa) + this.X[2] + unchecked((int)0x6d703ef3), 14) + bb; ee = RL(ee, 10); - bb = RL(bb + F3(cc, dd, ee) + this.X[10] + unchecked((int)0x6d703ef3), 13) + aa; dd = RL(dd, 10); - aa = RL(aa + F3(bb, cc, dd) + this.X[0] + unchecked((int)0x6d703ef3), 13) + ee; cc = RL(cc, 10); - ee = RL(ee + F3(aa, bb, cc) + this.X[4] + unchecked((int)0x6d703ef3), 7) + dd; bb = RL(bb, 10); - dd = RL(dd + F3(ee, aa, bb) + this.X[13] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); - - // - // Rounds 48-63 - // - // left - c = RL(c + F4(d, e, a) + this.X[1] + unchecked((int)0x8f1bbcdc), 11) + b; e = RL(e, 10); - b = RL(b + F4(c, d, e) + this.X[9] + unchecked((int)0x8f1bbcdc), 12) + a; d = RL(d, 10); - a = RL(a + F4(b, c, d) + this.X[11] + unchecked((int)0x8f1bbcdc), 14) + e; c = RL(c, 10); - e = RL(e + F4(a, b, c) + this.X[10] + unchecked((int)0x8f1bbcdc), 15) + d; b = RL(b, 10); - d = RL(d + F4(e, a, b) + this.X[0] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); - c = RL(c + F4(d, e, a) + this.X[8] + unchecked((int)0x8f1bbcdc), 15) + b; e = RL(e, 10); - b = RL(b + F4(c, d, e) + this.X[12] + unchecked((int)0x8f1bbcdc), 9) + a; d = RL(d, 10); - a = RL(a + F4(b, c, d) + this.X[4] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); - e = RL(e + F4(a, b, c) + this.X[13] + unchecked((int)0x8f1bbcdc), 9) + d; b = RL(b, 10); - d = RL(d + F4(e, a, b) + this.X[3] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); - c = RL(c + F4(d, e, a) + this.X[7] + unchecked((int)0x8f1bbcdc), 5) + b; e = RL(e, 10); - b = RL(b + F4(c, d, e) + this.X[15] + unchecked((int)0x8f1bbcdc), 6) + a; d = RL(d, 10); - a = RL(a + F4(b, c, d) + this.X[14] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); - e = RL(e + F4(a, b, c) + this.X[5] + unchecked((int)0x8f1bbcdc), 6) + d; b = RL(b, 10); - d = RL(d + F4(e, a, b) + this.X[6] + unchecked((int)0x8f1bbcdc), 5) + c; a = RL(a, 10); - c = RL(c + F4(d, e, a) + this.X[2] + unchecked((int)0x8f1bbcdc), 12) + b; e = RL(e, 10); - - // right - cc = RL(cc + F2(dd, ee, aa) + this.X[8] + unchecked((int)0x7a6d76e9), 15) + bb; ee = RL(ee, 10); - bb = RL(bb + F2(cc, dd, ee) + this.X[6] + unchecked((int)0x7a6d76e9), 5) + aa; dd = RL(dd, 10); - aa = RL(aa + F2(bb, cc, dd) + this.X[4] + unchecked((int)0x7a6d76e9), 8) + ee; cc = RL(cc, 10); - ee = RL(ee + F2(aa, bb, cc) + this.X[1] + unchecked((int)0x7a6d76e9), 11) + dd; bb = RL(bb, 10); - dd = RL(dd + F2(ee, aa, bb) + this.X[3] + unchecked((int)0x7a6d76e9), 14) + cc; aa = RL(aa, 10); - cc = RL(cc + F2(dd, ee, aa) + this.X[11] + unchecked((int)0x7a6d76e9), 14) + bb; ee = RL(ee, 10); - bb = RL(bb + F2(cc, dd, ee) + this.X[15] + unchecked((int)0x7a6d76e9), 6) + aa; dd = RL(dd, 10); - aa = RL(aa + F2(bb, cc, dd) + this.X[0] + unchecked((int)0x7a6d76e9), 14) + ee; cc = RL(cc, 10); - ee = RL(ee + F2(aa, bb, cc) + this.X[5] + unchecked((int)0x7a6d76e9), 6) + dd; bb = RL(bb, 10); - dd = RL(dd + F2(ee, aa, bb) + this.X[12] + unchecked((int)0x7a6d76e9), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F2(dd, ee, aa) + this.X[2] + unchecked((int)0x7a6d76e9), 12) + bb; ee = RL(ee, 10); - bb = RL(bb + F2(cc, dd, ee) + this.X[13] + unchecked((int)0x7a6d76e9), 9) + aa; dd = RL(dd, 10); - aa = RL(aa + F2(bb, cc, dd) + this.X[9] + unchecked((int)0x7a6d76e9), 12) + ee; cc = RL(cc, 10); - ee = RL(ee + F2(aa, bb, cc) + this.X[7] + unchecked((int)0x7a6d76e9), 5) + dd; bb = RL(bb, 10); - dd = RL(dd + F2(ee, aa, bb) + this.X[10] + unchecked((int)0x7a6d76e9), 15) + cc; aa = RL(aa, 10); - cc = RL(cc + F2(dd, ee, aa) + this.X[14] + unchecked((int)0x7a6d76e9), 8) + bb; ee = RL(ee, 10); - - // - // Rounds 64-79 - // - // left - b = RL(b + F5(c, d, e) + this.X[4] + unchecked((int)0xa953fd4e), 9) + a; d = RL(d, 10); - a = RL(a + F5(b, c, d) + this.X[0] + unchecked((int)0xa953fd4e), 15) + e; c = RL(c, 10); - e = RL(e + F5(a, b, c) + this.X[5] + unchecked((int)0xa953fd4e), 5) + d; b = RL(b, 10); - d = RL(d + F5(e, a, b) + this.X[9] + unchecked((int)0xa953fd4e), 11) + c; a = RL(a, 10); - c = RL(c + F5(d, e, a) + this.X[7] + unchecked((int)0xa953fd4e), 6) + b; e = RL(e, 10); - b = RL(b + F5(c, d, e) + this.X[12] + unchecked((int)0xa953fd4e), 8) + a; d = RL(d, 10); - a = RL(a + F5(b, c, d) + this.X[2] + unchecked((int)0xa953fd4e), 13) + e; c = RL(c, 10); - e = RL(e + F5(a, b, c) + this.X[10] + unchecked((int)0xa953fd4e), 12) + d; b = RL(b, 10); - d = RL(d + F5(e, a, b) + this.X[14] + unchecked((int)0xa953fd4e), 5) + c; a = RL(a, 10); - c = RL(c + F5(d, e, a) + this.X[1] + unchecked((int)0xa953fd4e), 12) + b; e = RL(e, 10); - b = RL(b + F5(c, d, e) + this.X[3] + unchecked((int)0xa953fd4e), 13) + a; d = RL(d, 10); - a = RL(a + F5(b, c, d) + this.X[8] + unchecked((int)0xa953fd4e), 14) + e; c = RL(c, 10); - e = RL(e + F5(a, b, c) + this.X[11] + unchecked((int)0xa953fd4e), 11) + d; b = RL(b, 10); - d = RL(d + F5(e, a, b) + this.X[6] + unchecked((int)0xa953fd4e), 8) + c; a = RL(a, 10); - c = RL(c + F5(d, e, a) + this.X[15] + unchecked((int)0xa953fd4e), 5) + b; e = RL(e, 10); - b = RL(b + F5(c, d, e) + this.X[13] + unchecked((int)0xa953fd4e), 6) + a; d = RL(d, 10); - - // right - bb = RL(bb + F1(cc, dd, ee) + this.X[12], 8) + aa; dd = RL(dd, 10); - aa = RL(aa + F1(bb, cc, dd) + this.X[15], 5) + ee; cc = RL(cc, 10); - ee = RL(ee + F1(aa, bb, cc) + this.X[10], 12) + dd; bb = RL(bb, 10); - dd = RL(dd + F1(ee, aa, bb) + this.X[4], 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F1(dd, ee, aa) + this.X[1], 12) + bb; ee = RL(ee, 10); - bb = RL(bb + F1(cc, dd, ee) + this.X[5], 5) + aa; dd = RL(dd, 10); - aa = RL(aa + F1(bb, cc, dd) + this.X[8], 14) + ee; cc = RL(cc, 10); - ee = RL(ee + F1(aa, bb, cc) + this.X[7], 6) + dd; bb = RL(bb, 10); - dd = RL(dd + F1(ee, aa, bb) + this.X[6], 8) + cc; aa = RL(aa, 10); - cc = RL(cc + F1(dd, ee, aa) + this.X[2], 13) + bb; ee = RL(ee, 10); - bb = RL(bb + F1(cc, dd, ee) + this.X[13], 6) + aa; dd = RL(dd, 10); - aa = RL(aa + F1(bb, cc, dd) + this.X[14], 5) + ee; cc = RL(cc, 10); - ee = RL(ee + F1(aa, bb, cc) + this.X[0], 15) + dd; bb = RL(bb, 10); - dd = RL(dd + F1(ee, aa, bb) + this.X[3], 13) + cc; aa = RL(aa, 10); - cc = RL(cc + F1(dd, ee, aa) + this.X[9], 11) + bb; ee = RL(ee, 10); - bb = RL(bb + F1(cc, dd, ee) + this.X[11], 11) + aa; dd = RL(dd, 10); - - dd += c + H1; - H1 = H2 + d + ee; - H2 = H3 + e + aa; - H3 = H4 + a + bb; - H4 = H0 + b + cc; - H0 = dd; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i < X.Length; i++) - { - this.X[i] = 0; - } - } - } +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// + /// + public sealed class RIPEMD160Hash : HashAlgorithm + { + private const int DIGEST_SIZE = 20; + + private readonly byte[] _buffer; + private int _bufferOffset; + private long _byteCount; + private int _offset; + private int H0, H1, H2, H3, H4; // IV's + private readonly int[] X = new int[16]; + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// The size, in bits, of the computed hash code. + public override int HashSize + { + get + { + return DIGEST_SIZE * 8; + } + } + + /// + /// Gets the input block size. + /// + /// The input block size. + public override int InputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets the output block size. + /// + /// The output block size. + public override int OutputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + /// Always true. + public override bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + /// true if multiple blocks can be transformed; otherwise, false. + public override bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + // + // fill the current word + // + while ((this._bufferOffset != 0) && (cbSize > 0)) + { + this.Update(array[ibStart]); + ibStart++; + cbSize--; + } + + // + // process whole words. + // + while (cbSize > this._buffer.Length) + { + this.ProcessWord(array, ibStart); + + ibStart += this._buffer.Length; + cbSize -= this._buffer.Length; + this._byteCount += this._buffer.Length; + } + + // + // load in the remainder. + // + while (cbSize > 0) + { + this.Update(array[ibStart]); + + ibStart++; + cbSize--; + } + } + + protected override byte[] HashFinal() + { + var output = new byte[DIGEST_SIZE]; + long bitLength = (this._byteCount << 3); + + // + // add the pad bytes. + // + this.Update((byte)128); + + while (this._bufferOffset != 0) + Update((byte)0); + ProcessLength(bitLength); + ProcessBlock(); + + UnpackWord(H0, output, 0); + UnpackWord(H1, output, 4); + UnpackWord(H2, output, 8); + UnpackWord(H3, output, 12); + UnpackWord(H4, output, 16); + + this.InternalInitialize(); + + return output; + } + + /// + /// Initializes an implementation of the class. + /// + public override void Initialize() + { + this.InternalInitialize(); + } + + /// + /// Initializes a new instance of the class. + /// + public RIPEMD160Hash() + { + this._buffer = new byte[4]; + this.InternalInitialize(); + } + + private void ProcessWord(byte[] input, int inOff) + { + this.X[this._offset++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (this._offset == 16) + { + ProcessBlock(); + } + } + + private void ProcessLength(long bitLength) + { + if (this._offset > 14) + { + ProcessBlock(); + } + + this.X[14] = (int)(bitLength & 0xffffffff); + this.X[15] = (int)((ulong)bitLength >> 32); + } + + private void UnpackWord(int word, byte[] outBytes, int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint)word >> 8); + outBytes[outOff + 2] = (byte)((uint)word >> 16); + outBytes[outOff + 3] = (byte)((uint)word >> 24); + } + + private void Update(byte input) + { + this._buffer[this._bufferOffset++] = input; + + if (this._bufferOffset == this._buffer.Length) + { + ProcessWord(this._buffer, 0); + this._bufferOffset = 0; + } + + this._byteCount++; + } + + /// + /// Reset the chaining variables to the IV values. + /// + private void InternalInitialize() + { + this._byteCount = 0; + this._bufferOffset = 0; + for (int i = 0; i < _buffer.Length; i++) + { + this._buffer[i] = 0; + } + + H0 = unchecked((int)0x67452301); + H1 = unchecked((int)0xefcdab89); + H2 = unchecked((int)0x98badcfe); + H3 = unchecked((int)0x10325476); + H4 = unchecked((int)0xc3d2e1f0); + + this._offset = 0; + + for (int i = 0; i != X.Length; i++) + { + this.X[i] = 0; + } + } + + private int RL(int x, int n) + { + return (x << n) | (int)((uint)x >> (32 - n)); + } + + /// + /// Rounds 0-15 + /// + /// The x. + /// The y. + /// The z. + /// + private int F1(int x, int y, int z) + { + return x ^ y ^ z; + } + + /// + /// Rounds 16-31 + /// + /// The x. + /// The y. + /// The z. + /// + private int F2(int x, int y, int z) + { + return (x & y) | (~x & z); + } + + /// + /// ounds 32-47 + /// + /// The x. + /// The y. + /// The z. + /// + private int F3(int x, int y, int z) + { + return (x | ~y) ^ z; + } + + /// + /// Rounds 48-63 + /// + /// The x. + /// The y. + /// The z. + /// + private int F4(int x, int y, int z) + { + return (x & z) | (y & ~z); + } + + /// + /// ounds 64-79 + /// + /// The x. + /// The y. + /// The z. + /// + private int F5(int x, int y, int z) + { + return x ^ (y | ~z); + } + + private void ProcessBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + e = ee = H4; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + F1(b, c, d) + this.X[0], 11) + e; c = RL(c, 10); + e = RL(e + F1(a, b, c) + this.X[1], 14) + d; b = RL(b, 10); + d = RL(d + F1(e, a, b) + this.X[2], 15) + c; a = RL(a, 10); + c = RL(c + F1(d, e, a) + this.X[3], 12) + b; e = RL(e, 10); + b = RL(b + F1(c, d, e) + this.X[4], 5) + a; d = RL(d, 10); + a = RL(a + F1(b, c, d) + this.X[5], 8) + e; c = RL(c, 10); + e = RL(e + F1(a, b, c) + this.X[6], 7) + d; b = RL(b, 10); + d = RL(d + F1(e, a, b) + this.X[7], 9) + c; a = RL(a, 10); + c = RL(c + F1(d, e, a) + this.X[8], 11) + b; e = RL(e, 10); + b = RL(b + F1(c, d, e) + this.X[9], 13) + a; d = RL(d, 10); + a = RL(a + F1(b, c, d) + this.X[10], 14) + e; c = RL(c, 10); + e = RL(e + F1(a, b, c) + this.X[11], 15) + d; b = RL(b, 10); + d = RL(d + F1(e, a, b) + this.X[12], 6) + c; a = RL(a, 10); + c = RL(c + F1(d, e, a) + this.X[13], 7) + b; e = RL(e, 10); + b = RL(b + F1(c, d, e) + this.X[14], 9) + a; d = RL(d, 10); + a = RL(a + F1(b, c, d) + this.X[15], 8) + e; c = RL(c, 10); + + // right + aa = RL(aa + F5(bb, cc, dd) + this.X[5] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + this.X[14] + unchecked((int)0x50a28be6), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + this.X[7] + unchecked((int)0x50a28be6), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + this.X[0] + unchecked((int)0x50a28be6), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + this.X[9] + unchecked((int)0x50a28be6), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + this.X[2] + unchecked((int)0x50a28be6), 15) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + this.X[11] + unchecked((int)0x50a28be6), 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + this.X[4] + unchecked((int)0x50a28be6), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + this.X[13] + unchecked((int)0x50a28be6), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + this.X[6] + unchecked((int)0x50a28be6), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + this.X[15] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + this.X[8] + unchecked((int)0x50a28be6), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + this.X[1] + unchecked((int)0x50a28be6), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + this.X[10] + unchecked((int)0x50a28be6), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + this.X[3] + unchecked((int)0x50a28be6), 12) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + this.X[12] + unchecked((int)0x50a28be6), 6) + ee; cc = RL(cc, 10); + + // + // Rounds 16-31 + // + // left + e = RL(e + F2(a, b, c) + this.X[7] + unchecked((int)0x5a827999), 7) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + this.X[4] + unchecked((int)0x5a827999), 6) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + this.X[13] + unchecked((int)0x5a827999), 8) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + this.X[1] + unchecked((int)0x5a827999), 13) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + this.X[10] + unchecked((int)0x5a827999), 11) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + this.X[6] + unchecked((int)0x5a827999), 9) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + this.X[15] + unchecked((int)0x5a827999), 7) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + this.X[3] + unchecked((int)0x5a827999), 15) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + this.X[12] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + this.X[0] + unchecked((int)0x5a827999), 12) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + this.X[9] + unchecked((int)0x5a827999), 15) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + this.X[5] + unchecked((int)0x5a827999), 9) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + this.X[2] + unchecked((int)0x5a827999), 11) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + this.X[14] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + this.X[11] + unchecked((int)0x5a827999), 13) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + this.X[8] + unchecked((int)0x5a827999), 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + F4(aa, bb, cc) + this.X[6] + unchecked((int)0x5c4dd124), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + this.X[11] + unchecked((int)0x5c4dd124), 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + this.X[3] + unchecked((int)0x5c4dd124), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + this.X[7] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + this.X[0] + unchecked((int)0x5c4dd124), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + this.X[13] + unchecked((int)0x5c4dd124), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + this.X[5] + unchecked((int)0x5c4dd124), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + this.X[10] + unchecked((int)0x5c4dd124), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + this.X[14] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + this.X[15] + unchecked((int)0x5c4dd124), 7) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + this.X[8] + unchecked((int)0x5c4dd124), 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + this.X[12] + unchecked((int)0x5c4dd124), 7) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + this.X[4] + unchecked((int)0x5c4dd124), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + this.X[9] + unchecked((int)0x5c4dd124), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + this.X[1] + unchecked((int)0x5c4dd124), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + this.X[2] + unchecked((int)0x5c4dd124), 11) + dd; bb = RL(bb, 10); + + // + // Rounds 32-47 + // + // left + d = RL(d + F3(e, a, b) + this.X[3] + unchecked((int)0x6ed9eba1), 11) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + this.X[10] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + this.X[14] + unchecked((int)0x6ed9eba1), 6) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + this.X[4] + unchecked((int)0x6ed9eba1), 7) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + this.X[9] + unchecked((int)0x6ed9eba1), 14) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + this.X[15] + unchecked((int)0x6ed9eba1), 9) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + this.X[8] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + this.X[1] + unchecked((int)0x6ed9eba1), 15) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + this.X[2] + unchecked((int)0x6ed9eba1), 14) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + this.X[7] + unchecked((int)0x6ed9eba1), 8) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + this.X[0] + unchecked((int)0x6ed9eba1), 13) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + this.X[6] + unchecked((int)0x6ed9eba1), 6) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + this.X[13] + unchecked((int)0x6ed9eba1), 5) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + this.X[11] + unchecked((int)0x6ed9eba1), 12) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + this.X[5] + unchecked((int)0x6ed9eba1), 7) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + this.X[12] + unchecked((int)0x6ed9eba1), 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + F3(ee, aa, bb) + this.X[15] + unchecked((int)0x6d703ef3), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + this.X[5] + unchecked((int)0x6d703ef3), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + this.X[1] + unchecked((int)0x6d703ef3), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + this.X[3] + unchecked((int)0x6d703ef3), 11) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + this.X[7] + unchecked((int)0x6d703ef3), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + this.X[14] + unchecked((int)0x6d703ef3), 6) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + this.X[6] + unchecked((int)0x6d703ef3), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + this.X[9] + unchecked((int)0x6d703ef3), 14) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + this.X[11] + unchecked((int)0x6d703ef3), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + this.X[8] + unchecked((int)0x6d703ef3), 13) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + this.X[12] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + this.X[2] + unchecked((int)0x6d703ef3), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + this.X[10] + unchecked((int)0x6d703ef3), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + this.X[0] + unchecked((int)0x6d703ef3), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + this.X[4] + unchecked((int)0x6d703ef3), 7) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + this.X[13] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); + + // + // Rounds 48-63 + // + // left + c = RL(c + F4(d, e, a) + this.X[1] + unchecked((int)0x8f1bbcdc), 11) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + this.X[9] + unchecked((int)0x8f1bbcdc), 12) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + this.X[11] + unchecked((int)0x8f1bbcdc), 14) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + this.X[10] + unchecked((int)0x8f1bbcdc), 15) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + this.X[0] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + this.X[8] + unchecked((int)0x8f1bbcdc), 15) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + this.X[12] + unchecked((int)0x8f1bbcdc), 9) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + this.X[4] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + this.X[13] + unchecked((int)0x8f1bbcdc), 9) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + this.X[3] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + this.X[7] + unchecked((int)0x8f1bbcdc), 5) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + this.X[15] + unchecked((int)0x8f1bbcdc), 6) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + this.X[14] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + this.X[5] + unchecked((int)0x8f1bbcdc), 6) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + this.X[6] + unchecked((int)0x8f1bbcdc), 5) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + this.X[2] + unchecked((int)0x8f1bbcdc), 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + F2(dd, ee, aa) + this.X[8] + unchecked((int)0x7a6d76e9), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + this.X[6] + unchecked((int)0x7a6d76e9), 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + this.X[4] + unchecked((int)0x7a6d76e9), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + this.X[1] + unchecked((int)0x7a6d76e9), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + this.X[3] + unchecked((int)0x7a6d76e9), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + this.X[11] + unchecked((int)0x7a6d76e9), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + this.X[15] + unchecked((int)0x7a6d76e9), 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + this.X[0] + unchecked((int)0x7a6d76e9), 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + this.X[5] + unchecked((int)0x7a6d76e9), 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + this.X[12] + unchecked((int)0x7a6d76e9), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + this.X[2] + unchecked((int)0x7a6d76e9), 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + this.X[13] + unchecked((int)0x7a6d76e9), 9) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + this.X[9] + unchecked((int)0x7a6d76e9), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + this.X[7] + unchecked((int)0x7a6d76e9), 5) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + this.X[10] + unchecked((int)0x7a6d76e9), 15) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + this.X[14] + unchecked((int)0x7a6d76e9), 8) + bb; ee = RL(ee, 10); + + // + // Rounds 64-79 + // + // left + b = RL(b + F5(c, d, e) + this.X[4] + unchecked((int)0xa953fd4e), 9) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + this.X[0] + unchecked((int)0xa953fd4e), 15) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + this.X[5] + unchecked((int)0xa953fd4e), 5) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + this.X[9] + unchecked((int)0xa953fd4e), 11) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + this.X[7] + unchecked((int)0xa953fd4e), 6) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + this.X[12] + unchecked((int)0xa953fd4e), 8) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + this.X[2] + unchecked((int)0xa953fd4e), 13) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + this.X[10] + unchecked((int)0xa953fd4e), 12) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + this.X[14] + unchecked((int)0xa953fd4e), 5) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + this.X[1] + unchecked((int)0xa953fd4e), 12) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + this.X[3] + unchecked((int)0xa953fd4e), 13) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + this.X[8] + unchecked((int)0xa953fd4e), 14) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + this.X[11] + unchecked((int)0xa953fd4e), 11) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + this.X[6] + unchecked((int)0xa953fd4e), 8) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + this.X[15] + unchecked((int)0xa953fd4e), 5) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + this.X[13] + unchecked((int)0xa953fd4e), 6) + a; d = RL(d, 10); + + // right + bb = RL(bb + F1(cc, dd, ee) + this.X[12], 8) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb, cc, dd) + this.X[15], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa, bb, cc) + this.X[10], 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee, aa, bb) + this.X[4], 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd, ee, aa) + this.X[1], 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc, dd, ee) + this.X[5], 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb, cc, dd) + this.X[8], 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa, bb, cc) + this.X[7], 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee, aa, bb) + this.X[6], 8) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd, ee, aa) + this.X[2], 13) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc, dd, ee) + this.X[13], 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F1(bb, cc, dd) + this.X[14], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + F1(aa, bb, cc) + this.X[0], 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F1(ee, aa, bb) + this.X[3], 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F1(dd, ee, aa) + this.X[9], 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F1(cc, dd, ee) + this.X[11], 11) + aa; dd = RL(dd, 10); + + dd += c + H1; + H1 = H2 + d + ee; + H2 = H3 + e + aa; + H3 = H4 + a + bb; + H4 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + this._offset = 0; + for (int i = 0; i < X.Length; i++) + { + this.X[i] = 0; + } + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA1Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA1Hash.cs index 268ae0f..ad2aa66 100644 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA1Hash.cs +++ b/Renci.SshNet/Security/Cryptography/Hashes/SHA1Hash.cs @@ -1,573 +1,571 @@ -using System.Security.Cryptography; -using System; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// SHA1 algorithm implementation - /// - public sealed class SHA1Hash : HashAlgorithm - { - private const int DIGEST_SIZE = 20; - - private const uint Y1 = 0x5a827999; - - private const uint Y2 = 0x6ed9eba1; - - private const uint Y3 = 0x8f1bbcdc; - - private const uint Y4 = 0xca62c1d6; - - private uint H1, H2, H3, H4, H5; - - private uint[] _hashValue = new uint[80]; - - private int _offset; - - private byte[] _buffer; - - private int _bufferOffset; - - private long _byteCount; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public SHA1Hash() - { - this._buffer = new byte[4]; - this.InternalInitialize(); - } - - /// - /// Routes data written to the object into the hash algorithm for computing the hash. - /// - /// The input to compute the hash code for. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - long bitLength = (this._byteCount << 3); - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - this.Update((byte)0); - - if (this._offset > 14) - { - this.ProcessBlock(); - } - - this._hashValue[14] = (uint)((ulong)bitLength >> 32); - this._hashValue[15] = (uint)((ulong)bitLength); - - - this.ProcessBlock(); - - UInt32ToBigEndian(H1, output, 0); - UInt32ToBigEndian(H2, output, 4); - UInt32ToBigEndian(H3, output, 8); - UInt32ToBigEndian(H4, output, 12); - UInt32ToBigEndian(H5, output, 16); - - this.Initialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - private void InternalInitialize() - { - var i = 0; - this._byteCount = 0; - this._bufferOffset = 0; - for (i = 0; i < 4; i++) - { - this._buffer[i] = 0; - } - - H1 = 0x67452301; - H2 = 0xefcdab89; - H3 = 0x98badcfe; - H4 = 0x10325476; - H5 = 0xc3d2e1f0; - - this._offset = 0; - for (i = 0; i != this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - private void ProcessWord(byte[] input, int inOff) - { - this._hashValue[this._offset] = BigEndianToUInt32(input, inOff); - - if (++this._offset == 16) - { - this.ProcessBlock(); - } - } - - private static uint F(uint u, uint v, uint w) - { - return (u & v) | (~u & w); - } - - private static uint H(uint u, uint v, uint w) - { - return u ^ v ^ w; - } - - private static uint G(uint u, uint v, uint w) - { - return (u & v) | (u & w) | (v & w); - } - - private void ProcessBlock() - { - // - // expand 16 word block into 80 word block. - // - for (int i = 16; i < 80; i++) - { - uint t = _hashValue[i - 3] ^ _hashValue[i - 8] ^ _hashValue[i - 14] ^ _hashValue[i - 16]; - _hashValue[i] = t << 1 | t >> 31; - } - - // - // set up working variables. - // - uint A = H1; - uint B = H2; - uint C = H3; - uint D = H4; - uint E = H5; - - // - // round 1 - // - int idx = 0; - - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // - // round 2 - // - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - - // - // round 3 - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - - // - // round 4 - // - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - - H1 += A; - H2 += B; - H3 += C; - H4 += D; - H5 += E; - - // - // reset start of the buffer. - // - this._offset = 0; - for (int i = 0; i < this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - - private static uint BigEndianToUInt32(byte[] bs, int off) - { - uint n = (uint)bs[off] << 24; - n |= (uint)bs[++off] << 16; - n |= (uint)bs[++off] << 8; - n |= (uint)bs[++off]; - return n; - } - - private static void UInt32ToBigEndian(uint n, byte[] bs, int off) - { - bs[off] = (byte)(n >> 24); - bs[++off] = (byte)(n >> 16); - bs[++off] = (byte)(n >> 8); - bs[++off] = (byte)(n); - } - } -} +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// SHA1 algorithm implementation + /// + public sealed class SHA1Hash : HashAlgorithm + { + private const int DIGEST_SIZE = 20; + + private const uint Y1 = 0x5a827999; + + private const uint Y2 = 0x6ed9eba1; + + private const uint Y3 = 0x8f1bbcdc; + + private const uint Y4 = 0xca62c1d6; + + private uint H1, H2, H3, H4, H5; + + private readonly uint[] _hashValue = new uint[80]; + + private int _offset; + + private readonly byte[] _buffer; + + private int _bufferOffset; + + private long _byteCount; + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// The size, in bits, of the computed hash code. + public override int HashSize + { + get + { + return DIGEST_SIZE * 8; + } + } + + /// + /// Gets the input block size. + /// + /// The input block size. + public override int InputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets the output block size. + /// + /// The output block size. + public override int OutputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + /// Always true. + public override bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + /// true if multiple blocks can be transformed; otherwise, false. + public override bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + /// + /// Initializes a new instance of the class. + /// + public SHA1Hash() + { + this._buffer = new byte[4]; + this.InternalInitialize(); + } + + /// + /// Routes data written to the object into the hash algorithm for computing the hash. + /// + /// The input to compute the hash code for. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the byte array to use as data. + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + // Fill the current word + while ((this._bufferOffset != 0) && (cbSize > 0)) + { + this.Update(array[ibStart]); + ibStart++; + cbSize--; + } + + // Process whole words. + while (cbSize > this._buffer.Length) + { + this.ProcessWord(array, ibStart); + + ibStart += this._buffer.Length; + cbSize -= this._buffer.Length; + this._byteCount += this._buffer.Length; + } + + // Load in the remainder. + while (cbSize > 0) + { + this.Update(array[ibStart]); + + ibStart++; + cbSize--; + } + } + + /// + /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. + /// + /// + /// The computed hash code. + /// + protected override byte[] HashFinal() + { + var output = new byte[DIGEST_SIZE]; + long bitLength = (this._byteCount << 3); + + // + // add the pad bytes. + // + this.Update((byte)128); + + while (this._bufferOffset != 0) + this.Update((byte)0); + + if (this._offset > 14) + { + this.ProcessBlock(); + } + + this._hashValue[14] = (uint)((ulong)bitLength >> 32); + this._hashValue[15] = (uint)((ulong)bitLength); + + + this.ProcessBlock(); + + UInt32ToBigEndian(H1, output, 0); + UInt32ToBigEndian(H2, output, 4); + UInt32ToBigEndian(H3, output, 8); + UInt32ToBigEndian(H4, output, 12); + UInt32ToBigEndian(H5, output, 16); + + this.Initialize(); + + return output; + } + + /// + /// Initializes an implementation of the class. + /// + public override void Initialize() + { + this.InternalInitialize(); + } + + private void InternalInitialize() + { + this._byteCount = 0; + this._bufferOffset = 0; + for (var i = 0; i < 4; i++) + { + this._buffer[i] = 0; + } + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + H5 = 0xc3d2e1f0; + + this._offset = 0; + for (var i = 0; i != this._hashValue.Length; i++) + { + this._hashValue[i] = 0; + } + } + + private void Update(byte input) + { + this._buffer[this._bufferOffset++] = input; + + if (this._bufferOffset == this._buffer.Length) + { + this.ProcessWord(this._buffer, 0); + this._bufferOffset = 0; + } + + this._byteCount++; + } + + private void ProcessWord(byte[] input, int inOff) + { + this._hashValue[this._offset] = BigEndianToUInt32(input, inOff); + + if (++this._offset == 16) + { + this.ProcessBlock(); + } + } + + private static uint F(uint u, uint v, uint w) + { + return (u & v) | (~u & w); + } + + private static uint H(uint u, uint v, uint w) + { + return u ^ v ^ w; + } + + private static uint G(uint u, uint v, uint w) + { + return (u & v) | (u & w) | (v & w); + } + + private void ProcessBlock() + { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i < 80; i++) + { + uint t = _hashValue[i - 3] ^ _hashValue[i - 8] ^ _hashValue[i - 14] ^ _hashValue[i - 16]; + _hashValue[i] = t << 1 | t >> 31; + } + + // + // set up working variables. + // + uint A = H1; + uint B = H2; + uint C = H3; + uint D = H4; + uint E = H5; + + // + // round 1 + // + int idx = 0; + + // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; + C = C << 30 | (C >> 2); + // + // round 2 + // + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; + C = C << 30 | (C >> 2); + + // + // round 3 + // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; + C = C << 30 | (C >> 2); + + // + // round 4 + // + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; + C = C << 30 | (C >> 2); + // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; + B = B << 30 | (B >> 2); + + D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; + A = A << 30 | (A >> 2); + + C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; + E = E << 30 | (E >> 2); + + B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; + D = D << 30 | (D >> 2); + + A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; + C = C << 30 | (C >> 2); + + H1 += A; + H2 += B; + H3 += C; + H4 += D; + H5 += E; + + // + // reset start of the buffer. + // + this._offset = 0; + for (int i = 0; i < this._hashValue.Length; i++) + { + this._hashValue[i] = 0; + } + } + + private static uint BigEndianToUInt32(byte[] bs, int off) + { + uint n = (uint)bs[off] << 24; + n |= (uint)bs[++off] << 16; + n |= (uint)bs[++off] << 8; + n |= (uint)bs[++off]; + return n; + } + + private static void UInt32ToBigEndian(uint n, byte[] bs, int off) + { + bs[off] = (byte)(n >> 24); + bs[++off] = (byte)(n >> 16); + bs[++off] = (byte)(n >> 8); + bs[++off] = (byte)(n); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA256Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA256Hash.cs index d0fc15c..85cbdda 100644 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA256Hash.cs +++ b/Renci.SshNet/Security/Cryptography/Hashes/SHA256Hash.cs @@ -1,400 +1,396 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// SHA256 algorithm implementation. - /// - public class SHA256Hash : HashAlgorithm - { - private const int DIGEST_SIZE = 32; - - private uint H1, H2, H3, H4, H5, H6, H7, H8; - - private uint[] X = new uint[64]; - - private int _offset; - - private byte[] _buffer; - - private int _bufferOffset; - - private long _byteCount; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public SHA256Hash() - { - this._buffer = new byte[4]; - this.InternalInitialize(); - } - - /// - /// Routes data written to the object into the hash algorithm for computing the hash. - /// - /// The input to compute the hash code for. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - long bitLength = (this._byteCount << 3); - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - this.Update((byte)0); - - if (this._offset > 14) - { - this.ProcessBlock(); - } - - X[14] = (uint)((ulong)bitLength >> 32); - X[15] = (uint)((ulong)bitLength); - - - this.ProcessBlock(); - - UInt32_To_BE((uint)H1, output, 0); - UInt32_To_BE((uint)H2, output, 0 + 4); - UInt32_To_BE((uint)H3, output, 0 + 8); - UInt32_To_BE((uint)H4, output, 0 + 12); - UInt32_To_BE((uint)H5, output, 0 + 16); - UInt32_To_BE((uint)H6, output, 0 + 20); - UInt32_To_BE((uint)H7, output, 0 + 24); - UInt32_To_BE((uint)H8, output, 0 + 28); - - this.Initialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - private void InternalInitialize() - { - this._byteCount = 0; - this._bufferOffset = 0; - for (int i = 0; i < this._buffer.Length; i++) - { - this._buffer[i] = 0; - } - - H1 = 0x6a09e667; - H2 = 0xbb67ae85; - H3 = 0x3c6ef372; - H4 = 0xa54ff53a; - H5 = 0x510e527f; - H6 = 0x9b05688c; - H7 = 0x1f83d9ab; - H8 = 0x5be0cd19; - - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - private static uint BE_To_UInt32(byte[] bs, int off) - { - uint n = (uint)bs[off] << 24; - n |= (uint)bs[++off] << 16; - n |= (uint)bs[++off] << 8; - n |= (uint)bs[++off]; - return n; - } - - private static void UInt32_To_BE(uint n, byte[] bs, int off) - { - bs[off] = (byte)(n >> 24); - bs[++off] = (byte)(n >> 16); - bs[++off] = (byte)(n >> 8); - bs[++off] = (byte)(n); - } - - private void ProcessWord(byte[] input, int inOff) - { - X[this._offset] = BE_To_UInt32(input, inOff); - - if (++this._offset == 16) - { - ProcessBlock(); - } - } - - private void ProcessLength(long bitLength) - { - if (this._offset > 14) - { - ProcessBlock(); - } - - X[14] = (uint)((ulong)bitLength >> 32); - X[15] = (uint)((ulong)bitLength); - } - - private void ProcessBlock() - { - // - // expand 16 word block into 64 word blocks. - // - for (int ti = 16; ti <= 63; ti++) - { - X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16]; - } - - // - // set up working variables. - // - uint a = H1; - uint b = H2; - uint c = H3; - uint d = H4; - uint e = H5; - uint f = H6; - uint g = H7; - uint h = H8; - - int t = 0; - for (int i = 0; i < 8; ++i) - { - // t = 8 * i - h += Sum1Ch(e, f, g) + K[t] + X[t]; - d += h; - h += Sum0Maj(a, b, c); - ++t; - - // t = 8 * i + 1 - g += Sum1Ch(d, e, f) + K[t] + X[t]; - c += g; - g += Sum0Maj(h, a, b); - ++t; - - // t = 8 * i + 2 - f += Sum1Ch(c, d, e) + K[t] + X[t]; - b += f; - f += Sum0Maj(g, h, a); - ++t; - - // t = 8 * i + 3 - e += Sum1Ch(b, c, d) + K[t] + X[t]; - a += e; - e += Sum0Maj(f, g, h); - ++t; - - // t = 8 * i + 4 - d += Sum1Ch(a, b, c) + K[t] + X[t]; - h += d; - d += Sum0Maj(e, f, g); - ++t; - - // t = 8 * i + 5 - c += Sum1Ch(h, a, b) + K[t] + X[t]; - g += c; - c += Sum0Maj(d, e, f); - ++t; - - // t = 8 * i + 6 - b += Sum1Ch(g, h, a) + K[t] + X[t]; - f += b; - b += Sum0Maj(c, d, e); - ++t; - - // t = 8 * i + 7 - a += Sum1Ch(f, g, h) + K[t] + X[t]; - e += a; - a += Sum0Maj(b, c, d); - ++t; - } - - H1 += a; - H2 += b; - H3 += c; - H4 += d; - H5 += e; - H6 += f; - H7 += g; - H8 += h; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - private static uint Sum1Ch(uint x, uint y, uint z) - { - // return Sum1(x) + Ch(x, y, z); - return (((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7))) - + ((x & y) ^ ((~x) & z)); - } - - private static uint Sum0Maj(uint x, uint y, uint z) - { - // return Sum0(x) + Maj(x, y, z); - return (((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10))) - + ((x & y) ^ (x & z) ^ (y & z)); - } - - private static uint Theta0(uint x) - { - return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3); - } - - private static uint Theta1(uint x) - { - return ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10); - } - - /// - /// The SHA-256 Constants (represent the first 32 bits of the fractional parts of the cube roots of the first sixty-four prime numbers) - /// - private static readonly uint[] K = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; - } -} +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// SHA256 algorithm implementation. + /// + public class SHA256Hash : HashAlgorithm + { + private const int DIGEST_SIZE = 32; + + private uint H1, H2, H3, H4, H5, H6, H7, H8; + + private readonly uint[] X = new uint[64]; + + private int _offset; + + private readonly byte[] _buffer; + + private int _bufferOffset; + + private long _byteCount; + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// The size, in bits, of the computed hash code. + public override int HashSize + { + get + { + return DIGEST_SIZE * 8; + } + } + + /// + /// Gets the input block size. + /// + /// The input block size. + public override int InputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets the output block size. + /// + /// The output block size. + public override int OutputBlockSize + { + get + { + return 64; + } + } + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + /// Always true. + public override bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + /// true if multiple blocks can be transformed; otherwise, false. + public override bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + /// + /// Initializes a new instance of the class. + /// + public SHA256Hash() + { + this._buffer = new byte[4]; + this.InternalInitialize(); + } + + /// + /// Routes data written to the object into the hash algorithm for computing the hash. + /// + /// The input to compute the hash code for. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the byte array to use as data. + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + // Fill the current word + while ((this._bufferOffset != 0) && (cbSize > 0)) + { + this.Update(array[ibStart]); + ibStart++; + cbSize--; + } + + // Process whole words. + while (cbSize > this._buffer.Length) + { + this.ProcessWord(array, ibStart); + + ibStart += this._buffer.Length; + cbSize -= this._buffer.Length; + this._byteCount += this._buffer.Length; + } + + // Load in the remainder. + while (cbSize > 0) + { + this.Update(array[ibStart]); + + ibStart++; + cbSize--; + } + } + + /// + /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. + /// + /// + /// The computed hash code. + /// + protected override byte[] HashFinal() + { + var output = new byte[DIGEST_SIZE]; + long bitLength = (this._byteCount << 3); + + // + // add the pad bytes. + // + this.Update((byte)128); + + while (this._bufferOffset != 0) + this.Update((byte)0); + + if (this._offset > 14) + { + this.ProcessBlock(); + } + + X[14] = (uint)((ulong)bitLength >> 32); + X[15] = (uint)((ulong)bitLength); + + + this.ProcessBlock(); + + UInt32_To_BE((uint)H1, output, 0); + UInt32_To_BE((uint)H2, output, 0 + 4); + UInt32_To_BE((uint)H3, output, 0 + 8); + UInt32_To_BE((uint)H4, output, 0 + 12); + UInt32_To_BE((uint)H5, output, 0 + 16); + UInt32_To_BE((uint)H6, output, 0 + 20); + UInt32_To_BE((uint)H7, output, 0 + 24); + UInt32_To_BE((uint)H8, output, 0 + 28); + + this.Initialize(); + + return output; + } + + /// + /// Initializes an implementation of the class. + /// + public override void Initialize() + { + this.InternalInitialize(); + } + + private void InternalInitialize() + { + this._byteCount = 0; + this._bufferOffset = 0; + for (int i = 0; i < this._buffer.Length; i++) + { + this._buffer[i] = 0; + } + + H1 = 0x6a09e667; + H2 = 0xbb67ae85; + H3 = 0x3c6ef372; + H4 = 0xa54ff53a; + H5 = 0x510e527f; + H6 = 0x9b05688c; + H7 = 0x1f83d9ab; + H8 = 0x5be0cd19; + + this._offset = 0; + for (int i = 0; i < this.X.Length; i++) + { + this.X[i] = 0; + } + } + + private void Update(byte input) + { + this._buffer[this._bufferOffset++] = input; + + if (this._bufferOffset == this._buffer.Length) + { + this.ProcessWord(this._buffer, 0); + this._bufferOffset = 0; + } + + this._byteCount++; + } + + private static uint BE_To_UInt32(byte[] bs, int off) + { + uint n = (uint)bs[off] << 24; + n |= (uint)bs[++off] << 16; + n |= (uint)bs[++off] << 8; + n |= (uint)bs[++off]; + return n; + } + + private static void UInt32_To_BE(uint n, byte[] bs, int off) + { + bs[off] = (byte)(n >> 24); + bs[++off] = (byte)(n >> 16); + bs[++off] = (byte)(n >> 8); + bs[++off] = (byte)(n); + } + + private void ProcessWord(byte[] input, int inOff) + { + X[this._offset] = BE_To_UInt32(input, inOff); + + if (++this._offset == 16) + { + ProcessBlock(); + } + } + + private void ProcessLength(long bitLength) + { + if (this._offset > 14) + { + ProcessBlock(); + } + + X[14] = (uint)((ulong)bitLength >> 32); + X[15] = (uint)((ulong)bitLength); + } + + private void ProcessBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int ti = 16; ti <= 63; ti++) + { + X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16]; + } + + // + // set up working variables. + // + uint a = H1; + uint b = H2; + uint c = H3; + uint d = H4; + uint e = H5; + uint f = H6; + uint g = H7; + uint h = H8; + + int t = 0; + for (int i = 0; i < 8; ++i) + { + // t = 8 * i + h += Sum1Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0Maj(b, c, d); + ++t; + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + this._offset = 0; + for (int i = 0; i < this.X.Length; i++) + { + this.X[i] = 0; + } + } + + private static uint Sum1Ch(uint x, uint y, uint z) + { + // return Sum1(x) + Ch(x, y, z); + return (((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7))) + + ((x & y) ^ ((~x) & z)); + } + + private static uint Sum0Maj(uint x, uint y, uint z) + { + // return Sum0(x) + Maj(x, y, z); + return (((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10))) + + ((x & y) ^ (x & z) ^ (y & z)); + } + + private static uint Theta0(uint x) + { + return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3); + } + + private static uint Theta1(uint x) + { + return ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10); + } + + /// + /// The SHA-256 Constants (represent the first 32 bits of the fractional parts of the cube roots of the first sixty-four prime numbers) + /// + private static readonly uint[] K = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + } +} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs index 74293de..e45115a 100644 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs +++ b/Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs @@ -1,366 +1,362 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// SHA256 algorithm implementation. - /// - public abstract class SHA2HashBase : HashAlgorithm - { - protected ulong H1, H2, H3, H4, H5, H6, H7, H8; - - private ulong[] X = new ulong[80]; - - private int _offset; - - private byte[] _buffer; - - private int _bufferOffset; - - private long _byteCount1; - - private long _byteCount2; - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public SHA2HashBase() - { - this._buffer = new byte[8]; - - this.Initialize(); - } - - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount1 += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - public override void Initialize() - { - this._byteCount1 = 0; - this._byteCount2 = 0; - - this._bufferOffset = 0; - for (int i = 0; i < this._buffer.Length; i++) - { - this._buffer[i] = 0; - } - - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - protected void Finish() - { - this.AdjustByteCounts(); - - long lowBitLength = this._byteCount1 << 3; - long hiBitLength = this._byteCount2; - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - { - this.Update((byte)0); - } - - this.ProcessLength(lowBitLength, hiBitLength); - - this.ProcessBlock(); - } - - private void Update(byte input) - { - this._buffer[_bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount1++; - } - - private void ProcessWord(byte[] input, int inOff) - { - this.X[_offset] = SHA512Hash.BE_To_UInt64(input, inOff); - - if (++_offset == 16) - { - ProcessBlock(); - } - } - - internal void ProcessLength(long lowW, long hiW) - { - if (_offset > 14) - { - this.ProcessBlock(); - } - - this.X[14] = (ulong)hiW; - this.X[15] = (ulong)lowW; - } - - private void ProcessBlock() - { - this.AdjustByteCounts(); - - // - // expand 16 word block into 80 word blocks. - // - for (int ti = 16; ti <= 79; ++ti) - { - X[ti] = Sigma1(X[ti - 2]) + X[ti - 7] + Sigma0(X[ti - 15]) + X[ti - 16]; - } - - // - // set up working variables. - // - ulong a = H1; - ulong b = H2; - ulong c = H3; - ulong d = H4; - ulong e = H5; - ulong f = H6; - ulong g = H7; - ulong h = H8; - - int t = 0; - for (int i = 0; i < 10; i++) - { - // t = 8 * i - h += Sum1(e) + Ch(e, f, g) + K[t] + X[t++]; - d += h; - h += Sum0(a) + Maj(a, b, c); - - // t = 8 * i + 1 - g += Sum1(d) + Ch(d, e, f) + K[t] + X[t++]; - c += g; - g += Sum0(h) + Maj(h, a, b); - - // t = 8 * i + 2 - f += Sum1(c) + Ch(c, d, e) + K[t] + X[t++]; - b += f; - f += Sum0(g) + Maj(g, h, a); - - // t = 8 * i + 3 - e += Sum1(b) + Ch(b, c, d) + K[t] + X[t++]; - a += e; - e += Sum0(f) + Maj(f, g, h); - - // t = 8 * i + 4 - d += Sum1(a) + Ch(a, b, c) + K[t] + X[t++]; - h += d; - d += Sum0(e) + Maj(e, f, g); - - // t = 8 * i + 5 - c += Sum1(h) + Ch(h, a, b) + K[t] + X[t++]; - g += c; - c += Sum0(d) + Maj(d, e, f); - - // t = 8 * i + 6 - b += Sum1(g) + Ch(g, h, a) + K[t] + X[t++]; - f += b; - b += Sum0(c) + Maj(c, d, e); - - // t = 8 * i + 7 - a += Sum1(f) + Ch(f, g, h) + K[t] + X[t++]; - e += a; - a += Sum0(b) + Maj(b, c, d); - } - - H1 += a; - H2 += b; - H3 += c; - H4 += d; - H5 += e; - H6 += f; - H7 += g; - H8 += h; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - /// - /// Adjust the byte counts so that byteCount2 represents the upper long (less 3 bits) word of the byte count. - /// - private void AdjustByteCounts() - { - if (this._byteCount1 > 0x1fffffffffffffffL) - { - this._byteCount2 += (long)((ulong)this._byteCount1 >> 61); - this._byteCount1 &= 0x1fffffffffffffffL; - } - } - - /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ - private static ulong Ch(ulong x, ulong y, ulong z) - { - return (x & y) ^ (~x & z); - } - - private static ulong Maj(ulong x, ulong y, ulong z) - { - return (x & y) ^ (x & z) ^ (y & z); - } - - private static ulong Sum0(ulong x) - { - return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); - } - - private static ulong Sum1(ulong x) - { - return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); - } - - private static ulong Sigma0(ulong x) - { - return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); - } - - private static ulong Sigma1(ulong x) - { - return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6); - } - - /* SHA-384 and SHA-512 Constants - * (represent the first 64 bits of the fractional parts of the - * cube roots of the first sixty-four prime numbers) - */ - private static readonly ulong[] K = - { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 - }; - - protected static void UInt32_To_BE(uint n, byte[] bs, int off) - { - bs[off] = (byte)(n >> 24); - bs[++off] = (byte)(n >> 16); - bs[++off] = (byte)(n >> 8); - bs[++off] = (byte)(n); - } - protected static void UInt64_To_BE(ulong n, byte[] bs, int off) - { - UInt32_To_BE((uint)(n >> 32), bs, off); - UInt32_To_BE((uint)(n), bs, off + 4); - } - protected static ulong BE_To_UInt64(byte[] bs) - { - uint hi = BE_To_UInt32(bs); - uint lo = BE_To_UInt32(bs, 4); - return ((ulong)hi << 32) | (ulong)lo; - } - protected static ulong BE_To_UInt64(byte[] bs, int off) - { - uint hi = BE_To_UInt32(bs, off); - uint lo = BE_To_UInt32(bs, off + 4); - return ((ulong)hi << 32) | (ulong)lo; - } - protected static uint BE_To_UInt32(byte[] bs, int off) - { - uint n = (uint)bs[off] << 24; - n |= (uint)bs[++off] << 16; - n |= (uint)bs[++off] << 8; - n |= (uint)bs[++off]; - return n; - } - protected static uint BE_To_UInt32(byte[] bs) - { - uint n = (uint)bs[0] << 24; - n |= (uint)bs[1] << 16; - n |= (uint)bs[2] << 8; - n |= (uint)bs[3]; - return n; - } - } -} +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// SHA256 algorithm implementation. + /// + public abstract class SHA2HashBase : HashAlgorithm + { + protected ulong H1, H2, H3, H4, H5, H6, H7, H8; + + private readonly ulong[] X = new ulong[80]; + + private int _offset; + + private readonly byte[] _buffer; + + private int _bufferOffset; + + private long _byteCount1; + + private long _byteCount2; + + /// + /// Gets a value indicating whether the current transform can be reused. + /// + /// Always true. + public override bool CanReuseTransform + { + get + { + return true; + } + } + + /// + /// Gets a value indicating whether multiple blocks can be transformed. + /// + /// true if multiple blocks can be transformed; otherwise, false. + public override bool CanTransformMultipleBlocks + { + get + { + return true; + } + } + + /// + /// Initializes a new instance of the class. + /// + public SHA2HashBase() + { + this._buffer = new byte[8]; + + this.Initialize(); + } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + // Fill the current word + while ((this._bufferOffset != 0) && (cbSize > 0)) + { + this.Update(array[ibStart]); + + ibStart++; + cbSize--; + } + + // Process whole words. + while (cbSize > this._buffer.Length) + { + ProcessWord(array, ibStart); + + ibStart += this._buffer.Length; + cbSize -= this._buffer.Length; + this._byteCount1 += this._buffer.Length; + } + + // Load in the remainder. + while (cbSize > 0) + { + this.Update(array[ibStart]); + + ibStart++; + cbSize--; + } + } + + public override void Initialize() + { + this._byteCount1 = 0; + this._byteCount2 = 0; + + this._bufferOffset = 0; + for (int i = 0; i < this._buffer.Length; i++) + { + this._buffer[i] = 0; + } + + this._offset = 0; + for (int i = 0; i < this.X.Length; i++) + { + this.X[i] = 0; + } + } + + protected void Finish() + { + this.AdjustByteCounts(); + + long lowBitLength = this._byteCount1 << 3; + long hiBitLength = this._byteCount2; + + // + // add the pad bytes. + // + this.Update((byte)128); + + while (this._bufferOffset != 0) + { + this.Update((byte)0); + } + + this.ProcessLength(lowBitLength, hiBitLength); + + this.ProcessBlock(); + } + + private void Update(byte input) + { + this._buffer[_bufferOffset++] = input; + + if (this._bufferOffset == this._buffer.Length) + { + this.ProcessWord(this._buffer, 0); + this._bufferOffset = 0; + } + + this._byteCount1++; + } + + private void ProcessWord(byte[] input, int inOff) + { + this.X[_offset] = SHA512Hash.BE_To_UInt64(input, inOff); + + if (++_offset == 16) + { + ProcessBlock(); + } + } + + internal void ProcessLength(long lowW, long hiW) + { + if (_offset > 14) + { + this.ProcessBlock(); + } + + this.X[14] = (ulong)hiW; + this.X[15] = (ulong)lowW; + } + + private void ProcessBlock() + { + this.AdjustByteCounts(); + + // + // expand 16 word block into 80 word blocks. + // + for (int ti = 16; ti <= 79; ++ti) + { + X[ti] = Sigma1(X[ti - 2]) + X[ti - 7] + Sigma0(X[ti - 15]) + X[ti - 16]; + } + + // + // set up working variables. + // + ulong a = H1; + ulong b = H2; + ulong c = H3; + ulong d = H4; + ulong e = H5; + ulong f = H6; + ulong g = H7; + ulong h = H8; + + int t = 0; + for (int i = 0; i < 10; i++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t++]; + d += h; + h += Sum0(a) + Maj(a, b, c); + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t++]; + c += g; + g += Sum0(h) + Maj(h, a, b); + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t++]; + b += f; + f += Sum0(g) + Maj(g, h, a); + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t++]; + a += e; + e += Sum0(f) + Maj(f, g, h); + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t++]; + h += d; + d += Sum0(e) + Maj(e, f, g); + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t++]; + g += c; + c += Sum0(d) + Maj(d, e, f); + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t++]; + f += b; + b += Sum0(c) + Maj(c, d, e); + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t++]; + e += a; + a += Sum0(b) + Maj(b, c, d); + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + this._offset = 0; + for (int i = 0; i < this.X.Length; i++) + { + this.X[i] = 0; + } + } + + /// + /// Adjust the byte counts so that byteCount2 represents the upper long (less 3 bits) word of the byte count. + /// + private void AdjustByteCounts() + { + if (this._byteCount1 > 0x1fffffffffffffffL) + { + this._byteCount2 += (long)((ulong)this._byteCount1 >> 61); + this._byteCount1 &= 0x1fffffffffffffffL; + } + } + + /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ + private static ulong Ch(ulong x, ulong y, ulong z) + { + return (x & y) ^ (~x & z); + } + + private static ulong Maj(ulong x, ulong y, ulong z) + { + return (x & y) ^ (x & z) ^ (y & z); + } + + private static ulong Sum0(ulong x) + { + return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); + } + + private static ulong Sum1(ulong x) + { + return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); + } + + private static ulong Sigma0(ulong x) + { + return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); + } + + private static ulong Sigma1(ulong x) + { + return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6); + } + + /* SHA-384 and SHA-512 Constants + * (represent the first 64 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + private static readonly ulong[] K = + { + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 + }; + + protected static void UInt32_To_BE(uint n, byte[] bs, int off) + { + bs[off] = (byte)(n >> 24); + bs[++off] = (byte)(n >> 16); + bs[++off] = (byte)(n >> 8); + bs[++off] = (byte)(n); + } + protected static void UInt64_To_BE(ulong n, byte[] bs, int off) + { + UInt32_To_BE((uint)(n >> 32), bs, off); + UInt32_To_BE((uint)(n), bs, off + 4); + } + protected static ulong BE_To_UInt64(byte[] bs) + { + uint hi = BE_To_UInt32(bs); + uint lo = BE_To_UInt32(bs, 4); + return ((ulong)hi << 32) | (ulong)lo; + } + protected static ulong BE_To_UInt64(byte[] bs, int off) + { + uint hi = BE_To_UInt32(bs, off); + uint lo = BE_To_UInt32(bs, off + 4); + return ((ulong)hi << 32) | (ulong)lo; + } + protected static uint BE_To_UInt32(byte[] bs, int off) + { + uint n = (uint)bs[off] << 24; + n |= (uint)bs[++off] << 16; + n |= (uint)bs[++off] << 8; + n |= (uint)bs[++off]; + return n; + } + protected static uint BE_To_UInt32(byte[] bs) + { + uint n = (uint)bs[0] << 24; + n |= (uint)bs[1] << 16; + n |= (uint)bs[2] << 8; + n |= (uint)bs[3]; + return n; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs index 116e300..1c5d2fe 100644 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs +++ b/Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs @@ -1,85 +1,79 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - public class SHA384Hash : SHA2HashBase - { - private const int DIGEST_SIZE = 48; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// When overridden in a derived class, gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - /// - /// When overridden in a derived class, gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - - this.Finish(); - - SHA2HashBase.UInt64_To_BE(H1, output, 0); - SHA2HashBase.UInt64_To_BE(H2, output, 8); - SHA2HashBase.UInt64_To_BE(H3, output, 16); - SHA2HashBase.UInt64_To_BE(H4, output, 24); - SHA2HashBase.UInt64_To_BE(H5, output, 32); - SHA2HashBase.UInt64_To_BE(H6, output, 40); - - this.Initialize(); - - return output; - } - - public override void Initialize() - { - base.Initialize(); - - /* SHA-384 initial hash value - * The first 64 bits of the fractional parts of the square roots - * of the 9th through 16th prime numbers - */ - H1 = 0xcbbb9d5dc1059ed8; - H2 = 0x629a292a367cd507; - H3 = 0x9159015a3070dd17; - H4 = 0x152fecd8f70e5939; - H5 = 0x67332667ffc00b31; - H6 = 0x8eb44a8768581511; - H7 = 0xdb0c2e0d64f98fa7; - H8 = 0x47b5481dbefa4fa4; - } - } -} +namespace Renci.SshNet.Security.Cryptography +{ + public class SHA384Hash : SHA2HashBase + { + private const int DIGEST_SIZE = 48; + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// The size, in bits, of the computed hash code. + public override int HashSize + { + get + { + return DIGEST_SIZE * 8; + } + } + + /// + /// When overridden in a derived class, gets the input block size. + /// + /// The input block size. + public override int InputBlockSize + { + get + { + return DIGEST_SIZE * 2; + } + } + + /// + /// When overridden in a derived class, gets the output block size. + /// + /// The output block size. + public override int OutputBlockSize + { + get + { + return DIGEST_SIZE * 2; + } + } + + protected override byte[] HashFinal() + { + var output = new byte[DIGEST_SIZE]; + + this.Finish(); + + SHA2HashBase.UInt64_To_BE(H1, output, 0); + SHA2HashBase.UInt64_To_BE(H2, output, 8); + SHA2HashBase.UInt64_To_BE(H3, output, 16); + SHA2HashBase.UInt64_To_BE(H4, output, 24); + SHA2HashBase.UInt64_To_BE(H5, output, 32); + SHA2HashBase.UInt64_To_BE(H6, output, 40); + + this.Initialize(); + + return output; + } + + public override void Initialize() + { + base.Initialize(); + + /* SHA-384 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the 9th through 16th prime numbers + */ + H1 = 0xcbbb9d5dc1059ed8; + H2 = 0x629a292a367cd507; + H3 = 0x9159015a3070dd17; + H4 = 0x152fecd8f70e5939; + H5 = 0x67332667ffc00b31; + H6 = 0x8eb44a8768581511; + H7 = 0xdb0c2e0d64f98fa7; + H8 = 0x47b5481dbefa4fa4; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs index a21947b..cf5f7df 100644 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs +++ b/Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs @@ -1,87 +1,81 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - public class SHA512Hash : SHA2HashBase - { - private const int DIGEST_SIZE = 64; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// When overridden in a derived class, gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - /// - /// When overridden in a derived class, gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - - this.Finish(); - - SHA2HashBase.UInt64_To_BE(H1, output, 0); - SHA2HashBase.UInt64_To_BE(H2, output, 8); - SHA2HashBase.UInt64_To_BE(H3, output, 16); - SHA2HashBase.UInt64_To_BE(H4, output, 24); - SHA2HashBase.UInt64_To_BE(H5, output, 32); - SHA2HashBase.UInt64_To_BE(H6, output, 40); - SHA2HashBase.UInt64_To_BE(H7, output, 48); - SHA2HashBase.UInt64_To_BE(H8, output, 56); - - this.Initialize(); - - return output; - } - - public override void Initialize() - { - base.Initialize(); - - /* SHA-512 initial hash value - * The first 64 bits of the fractional parts of the square roots - * of the first eight prime numbers - */ - H1 = 0x6a09e667f3bcc908; - H2 = 0xbb67ae8584caa73b; - H3 = 0x3c6ef372fe94f82b; - H4 = 0xa54ff53a5f1d36f1; - H5 = 0x510e527fade682d1; - H6 = 0x9b05688c2b3e6c1f; - H7 = 0x1f83d9abfb41bd6b; - H8 = 0x5be0cd19137e2179; - } - } -} +namespace Renci.SshNet.Security.Cryptography +{ + public class SHA512Hash : SHA2HashBase + { + private const int DIGEST_SIZE = 64; + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// The size, in bits, of the computed hash code. + public override int HashSize + { + get + { + return DIGEST_SIZE * 8; + } + } + + /// + /// When overridden in a derived class, gets the input block size. + /// + /// The input block size. + public override int InputBlockSize + { + get + { + return DIGEST_SIZE * 2; + } + } + + /// + /// When overridden in a derived class, gets the output block size. + /// + /// The output block size. + public override int OutputBlockSize + { + get + { + return DIGEST_SIZE * 2; + } + } + + protected override byte[] HashFinal() + { + var output = new byte[DIGEST_SIZE]; + + this.Finish(); + + SHA2HashBase.UInt64_To_BE(H1, output, 0); + SHA2HashBase.UInt64_To_BE(H2, output, 8); + SHA2HashBase.UInt64_To_BE(H3, output, 16); + SHA2HashBase.UInt64_To_BE(H4, output, 24); + SHA2HashBase.UInt64_To_BE(H5, output, 32); + SHA2HashBase.UInt64_To_BE(H6, output, 40); + SHA2HashBase.UInt64_To_BE(H7, output, 48); + SHA2HashBase.UInt64_To_BE(H8, output, 56); + + this.Initialize(); + + return output; + } + + public override void Initialize() + { + base.Initialize(); + + /* SHA-512 initial hash value + * The first 64 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + H1 = 0x6a09e667f3bcc908; + H2 = 0xbb67ae8584caa73b; + H3 = 0x3c6ef372fe94f82b; + H4 = 0xa54ff53a5f1d36f1; + H5 = 0x510e527fade682d1; + H6 = 0x9b05688c2b3e6c1f; + H7 = 0x1f83d9abfb41bd6b; + H8 = 0x5be0cd19137e2179; + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/Key.cs b/Renci.SshNet/Security/Cryptography/Key.cs index 4f0a100..c13ede0 100644 --- a/Renci.SshNet/Security/Cryptography/Key.cs +++ b/Renci.SshNet/Security/Cryptography/Key.cs @@ -1,94 +1,90 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Base class for asymmetric cipher algorithms - /// - public abstract class Key - { - /// - /// Specifies array of big integers that represent private key - /// - protected BigInteger[] _privateKey; - - /// - /// Gets the key specific digital signature. - /// - protected abstract DigitalSignature DigitalSignature { get; } - - /// - /// Gets or sets the public key. - /// - /// - /// The public. - /// - public abstract BigInteger[] Public { get; set; } - - /// - /// Gets the length of the key. - /// - /// - /// The length of the key. - /// - public abstract int KeyLength { get; } - - /// - /// Initializes a new instance of the class. - /// - /// DER encoded private key data. - public Key(byte[] data) - { - if (data == null) - throw new ArgumentNullException("data"); - - var der = new DerData(data); - var version = der.ReadBigInteger(); - - var keys = new List(); - while (!der.IsEndOfData) - { - keys.Add(der.ReadBigInteger()); - } - - this._privateKey = keys.ToArray(); - } - - /// - /// Initializes a new instance of the class. - /// - public Key() - : base() - { - - } - - /// - /// Signs the specified data with the key. - /// - /// The data to sign. - /// - /// Signed data. - /// - public byte[] Sign(byte[] data) - { - return this.DigitalSignature.Sign(data); - } - - /// - /// Verifies the signature. - /// - /// The data to verify. - /// The signature to verify against. - /// True is signature was successfully verifies; otherwise false. - public bool VerifySignature(byte[] data, byte[] signature) - { - return this.DigitalSignature.Verify(data, signature); - } - } -} +using System; +using System.Collections.Generic; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Security +{ + /// + /// Base class for asymmetric cipher algorithms + /// + public abstract class Key + { + /// + /// Specifies array of big integers that represent private key + /// + protected BigInteger[] _privateKey; + + /// + /// Gets the key specific digital signature. + /// + protected abstract DigitalSignature DigitalSignature { get; } + + /// + /// Gets or sets the public key. + /// + /// + /// The public. + /// + public abstract BigInteger[] Public { get; set; } + + /// + /// Gets the length of the key. + /// + /// + /// The length of the key. + /// + public abstract int KeyLength { get; } + + /// + /// Initializes a new instance of the class. + /// + /// DER encoded private key data. + public Key(byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + + var der = new DerData(data); + var version = der.ReadBigInteger(); + + var keys = new List(); + while (!der.IsEndOfData) + { + keys.Add(der.ReadBigInteger()); + } + + this._privateKey = keys.ToArray(); + } + + /// + /// Initializes a new instance of the class. + /// + public Key() + { + } + + /// + /// Signs the specified data with the key. + /// + /// The data to sign. + /// + /// Signed data. + /// + public byte[] Sign(byte[] data) + { + return this.DigitalSignature.Sign(data); + } + + /// + /// Verifies the signature. + /// + /// The data to verify. + /// The signature to verify against. + /// True is signature was successfully verifies; otherwise false. + public bool VerifySignature(byte[] data, byte[] signature) + { + return this.DigitalSignature.Verify(data, signature); + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs b/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs index 5f9f05f..bf3dedc 100644 --- a/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs +++ b/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs @@ -1,94 +1,91 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography.Ciphers; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Implements RSA digital signature algorithm. - /// - public class RsaDigitalSignature : CipherDigitalSignature, IDisposable - { - private HashAlgorithm _hash; - - /// - /// Initializes a new instance of the class. - /// - /// The RSA key. - public RsaDigitalSignature(RsaKey rsaKey) - : base(new ObjectIdentifier(1, 3, 14, 3, 2, 26), new RsaCipher(rsaKey)) - { - this._hash = new SHA1Hash(); - } - - /// - /// Hashes the specified input. - /// - /// The input. - /// - /// Hashed data. - /// - protected override byte[] Hash(byte[] input) - { - return this._hash.ComputeHash(input); - } - - #region IDisposable Members - - private bool _isDisposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._hash != null) - { - this._hash.Clear(); - this._hash = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~RsaDigitalSignature() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} +using System; +using System.Security.Cryptography; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Cryptography.Ciphers; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Implements RSA digital signature algorithm. + /// + public class RsaDigitalSignature : CipherDigitalSignature, IDisposable + { + private HashAlgorithm _hash; + + /// + /// Initializes a new instance of the class. + /// + /// The RSA key. + public RsaDigitalSignature(RsaKey rsaKey) + : base(new ObjectIdentifier(1, 3, 14, 3, 2, 26), new RsaCipher(rsaKey)) + { + this._hash = new SHA1Hash(); + } + + /// + /// Hashes the specified input. + /// + /// The input. + /// + /// Hashed data. + /// + protected override byte[] Hash(byte[] input) + { + return this._hash.ComputeHash(input); + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._hash != null) + { + this._hash.Clear(); + this._hash = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~RsaDigitalSignature() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Security/Cryptography/RsaKey.cs b/Renci.SshNet/Security/Cryptography/RsaKey.cs index dae8a44..c293800 100644 --- a/Renci.SshNet/Security/Cryptography/RsaKey.cs +++ b/Renci.SshNet/Security/Cryptography/RsaKey.cs @@ -1,273 +1,264 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Contains RSA private and public key - /// - public class RsaKey : Key, IDisposable - { - /// - /// Gets the modulus. - /// - public BigInteger Modulus - { - get - { - return this._privateKey[0]; - } - } - - /// - /// Gets the exponent. - /// - public BigInteger Exponent - { - get - { - return this._privateKey[1]; - } - } - - /// - /// Gets the D. - /// - public BigInteger D - { - get - { - if (this._privateKey.Length > 2) - return this._privateKey[2]; - else - return BigInteger.Zero; - } - } - - /// - /// Gets the P. - /// - public BigInteger P - { - get - { - if (this._privateKey.Length > 3) - return this._privateKey[3]; - else - return BigInteger.Zero; - } - } - - /// - /// Gets the Q. - /// - public BigInteger Q - { - get - { - if (this._privateKey.Length > 4) - return this._privateKey[4]; - else - return BigInteger.Zero; - } - } - - /// - /// Gets the DP. - /// - public BigInteger DP - { - get - { - if (this._privateKey.Length > 5) - return this._privateKey[5]; - else - return BigInteger.Zero; - } - } - - /// - /// Gets the DQ. - /// - public BigInteger DQ - { - get - { - if (this._privateKey.Length > 6) - return this._privateKey[6]; - else - return BigInteger.Zero; - } - } - - /// - /// Gets the inverse Q. - /// - public BigInteger InverseQ - { - get - { - if (this._privateKey.Length > 7) - return this._privateKey[7]; - else - return BigInteger.Zero; - } - } - - /// - /// Gets the length of the key. - /// - /// - /// The length of the key. - /// - public override int KeyLength - { - get - { - return this.Modulus.BitLength; - } - } - - private RsaDigitalSignature _digitalSignature; - /// - /// Gets the digital signature. - /// - protected override DigitalSignature DigitalSignature - { - get - { - if (this._digitalSignature == null) - { - this._digitalSignature = new RsaDigitalSignature(this); - } - return this._digitalSignature; - } - } - - /// - /// Gets or sets the public. - /// - /// - /// The public. - /// - public override BigInteger[] Public - { - get - { - return new BigInteger[] { this.Exponent, this.Modulus }; - } - set - { - if (value.Length != 2) - throw new InvalidOperationException("Invalid private key."); - - this._privateKey = new BigInteger[] { value[1], value[0] }; - } - } - - /// - /// Initializes a new instance of the class. - /// - public RsaKey() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// DER encoded private key data. - public RsaKey(byte[] data) - : base(data) - { - if (this._privateKey.Length != 8) - throw new InvalidOperationException("Invalid private key."); - } - - /// - /// Initializes a new instance of the class. - /// - /// The modulus. - /// The exponent. - /// The d. - /// The p. - /// The q. - /// The inverse Q. - public RsaKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q, BigInteger inverseQ) - { - this._privateKey = new BigInteger[8]; - this._privateKey[0] = modulus; - this._privateKey[1] = exponent; - this._privateKey[2] = d; - this._privateKey[3] = p; - this._privateKey[4] = q; - this._privateKey[5] = PrimeExponent(d, p); - this._privateKey[6] = PrimeExponent(d, q); - this._privateKey[7] = inverseQ; - } - - private static BigInteger PrimeExponent(BigInteger privateExponent, BigInteger prime) - { - BigInteger pe = prime - new BigInteger(1); - return privateExponent % pe; - } - - #region IDisposable Members - - private bool _isDisposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._digitalSignature != null) - { - this._digitalSignature.Dispose(); - this._digitalSignature = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~RsaKey() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Security +{ + /// + /// Contains RSA private and public key + /// + public class RsaKey : Key, IDisposable + { + /// + /// Gets the modulus. + /// + public BigInteger Modulus + { + get + { + return this._privateKey[0]; + } + } + + /// + /// Gets the exponent. + /// + public BigInteger Exponent + { + get + { + return this._privateKey[1]; + } + } + + /// + /// Gets the D. + /// + public BigInteger D + { + get + { + if (this._privateKey.Length > 2) + return this._privateKey[2]; + return BigInteger.Zero; + } + } + + /// + /// Gets the P. + /// + public BigInteger P + { + get + { + if (this._privateKey.Length > 3) + return this._privateKey[3]; + return BigInteger.Zero; + } + } + + /// + /// Gets the Q. + /// + public BigInteger Q + { + get + { + if (this._privateKey.Length > 4) + return this._privateKey[4]; + return BigInteger.Zero; + } + } + + /// + /// Gets the DP. + /// + public BigInteger DP + { + get + { + if (this._privateKey.Length > 5) + return this._privateKey[5]; + return BigInteger.Zero; + } + } + + /// + /// Gets the DQ. + /// + public BigInteger DQ + { + get + { + if (this._privateKey.Length > 6) + return this._privateKey[6]; + return BigInteger.Zero; + } + } + + /// + /// Gets the inverse Q. + /// + public BigInteger InverseQ + { + get + { + if (this._privateKey.Length > 7) + return this._privateKey[7]; + return BigInteger.Zero; + } + } + + /// + /// Gets the length of the key. + /// + /// + /// The length of the key. + /// + public override int KeyLength + { + get + { + return this.Modulus.BitLength; + } + } + + private RsaDigitalSignature _digitalSignature; + /// + /// Gets the digital signature. + /// + protected override DigitalSignature DigitalSignature + { + get + { + if (this._digitalSignature == null) + { + this._digitalSignature = new RsaDigitalSignature(this); + } + return this._digitalSignature; + } + } + + /// + /// Gets or sets the public. + /// + /// + /// The public. + /// + public override BigInteger[] Public + { + get + { + return new BigInteger[] { this.Exponent, this.Modulus }; + } + set + { + if (value.Length != 2) + throw new InvalidOperationException("Invalid private key."); + + this._privateKey = new BigInteger[] { value[1], value[0] }; + } + } + + /// + /// Initializes a new instance of the class. + /// + public RsaKey() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// DER encoded private key data. + public RsaKey(byte[] data) + : base(data) + { + if (this._privateKey.Length != 8) + throw new InvalidOperationException("Invalid private key."); + } + + /// + /// Initializes a new instance of the class. + /// + /// The modulus. + /// The exponent. + /// The d. + /// The p. + /// The q. + /// The inverse Q. + public RsaKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q, BigInteger inverseQ) + { + this._privateKey = new BigInteger[8]; + this._privateKey[0] = modulus; + this._privateKey[1] = exponent; + this._privateKey[2] = d; + this._privateKey[3] = p; + this._privateKey[4] = q; + this._privateKey[5] = PrimeExponent(d, p); + this._privateKey[6] = PrimeExponent(d, q); + this._privateKey[7] = inverseQ; + } + + private static BigInteger PrimeExponent(BigInteger privateExponent, BigInteger prime) + { + BigInteger pe = prime - new BigInteger(1); + return privateExponent % pe; + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + // Dispose managed ResourceMessages. + if (this._digitalSignature != null) + { + this._digitalSignature.Dispose(); + this._digitalSignature = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~RsaKey() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Security/Cryptography/StreamCipher.cs b/Renci.SshNet/Security/Cryptography/StreamCipher.cs index ed5386e..a0f3442 100644 --- a/Renci.SshNet/Security/Cryptography/StreamCipher.cs +++ b/Renci.SshNet/Security/Cryptography/StreamCipher.cs @@ -1,23 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class of stream cipher algorithms. - /// - public abstract class StreamCipher : SymmetricCipher - { - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// is null. - protected StreamCipher(byte[] key) - : base(key) - { - } - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Base class of stream cipher algorithms. + /// + public abstract class StreamCipher : SymmetricCipher + { + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// is null. + protected StreamCipher(byte[] key) + : base(key) + { + } + } +} diff --git a/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs b/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs index be62410..4eb3396 100644 --- a/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs +++ b/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs @@ -1,57 +1,54 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for symmetric cipher implementations. - /// - public abstract class SymmetricCipher : Cipher - { - /// - /// Gets the key. - /// - protected byte[] Key { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// is null. - protected SymmetricCipher(byte[] key) - { - if (key == null) - throw new ArgumentNullException("key"); - - this.Key = key; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - } -} +using System; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Base class for symmetric cipher implementations. + /// + public abstract class SymmetricCipher : Cipher + { + /// + /// Gets the key. + /// + protected byte[] Key { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The key. + /// is null. + protected SymmetricCipher(byte[] key) + { + if (key == null) + throw new ArgumentNullException("key"); + + this.Key = key; + } + + /// + /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. + /// + /// The input data to encrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write encrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes encrypted. + /// + public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); + + /// + /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. + /// + /// The input data to decrypt. + /// The offset into the input byte array from which to begin using data. + /// The number of bytes in the input byte array to use as data. + /// The output to which to write decrypted data. + /// The offset into the output byte array from which to begin writing data. + /// + /// The number of bytes decrypted. + /// + public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); + } +} diff --git a/Renci.SshNet/Security/HostAlgorithm.cs b/Renci.SshNet/Security/HostAlgorithm.cs index 9dcfb36..14474d9 100644 --- a/Renci.SshNet/Security/HostAlgorithm.cs +++ b/Renci.SshNet/Security/HostAlgorithm.cs @@ -1,49 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Base class for SSH host algorithms. - /// - public abstract class HostAlgorithm - { - /// - /// Gets the host key name. - /// - public string Name { get; private set; } - - /// - /// Gets the host key data. - /// - public abstract byte[] Data { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The host key name. - public HostAlgorithm(string name) - { - this.Name = name; - } - - /// - /// Signs the specified data. - /// - /// The data. - /// Signed data. - public abstract byte[] Sign(byte[] data); - - /// - /// Verifies the signature. - /// - /// The data. - /// The signature. - /// True is signature was successfully verifies; otherwise false. - public abstract bool VerifySignature(byte[] data, byte[] signature); - } -} +namespace Renci.SshNet.Security +{ + /// + /// Base class for SSH host algorithms. + /// + public abstract class HostAlgorithm + { + /// + /// Gets the host key name. + /// + public string Name { get; private set; } + + /// + /// Gets the host key data. + /// + public abstract byte[] Data { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The host key name. + public HostAlgorithm(string name) + { + this.Name = name; + } + + /// + /// Signs the specified data. + /// + /// The data. + /// Signed data. + public abstract byte[] Sign(byte[] data); + + /// + /// Verifies the signature. + /// + /// The data. + /// The signature. + /// True is signature was successfully verifies; otherwise false. + public abstract bool VerifySignature(byte[] data, byte[] signature); + } +} diff --git a/Renci.SshNet/Security/KeyExchange.cs b/Renci.SshNet/Security/KeyExchange.cs index 88759c3..a2bbe70 100644 --- a/Renci.SshNet/Security/KeyExchange.cs +++ b/Renci.SshNet/Security/KeyExchange.cs @@ -1,457 +1,456 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Compression; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Security.Cryptography.Ciphers; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Represents base class for different key exchange algorithm implementations - /// - public abstract class KeyExchange : Algorithm, IDisposable - { - private CipherInfo _clientCipherInfo; - - private CipherInfo _serverCipherInfo; - - private HashInfo _clientHashInfo; - - private HashInfo _serverHashInfo; - - private Type _compressionType; - - private Type _decompressionType; - - /// - /// Gets or sets the session. - /// - /// - /// The session. - /// - protected Session Session { get; private set; } - - /// - /// Gets or sets key exchange shared key. - /// - /// - /// The shared key. - /// - public BigInteger SharedKey { get; protected set; } - - private byte[] _exchangeHash; - /// - /// Gets the exchange hash. - /// - /// The exchange hash. - public byte[] ExchangeHash - { - get - { - if (this._exchangeHash == null) - { - this._exchangeHash = this.CalculateHash(); - } - return this._exchangeHash; - } - } - - /// - /// Occurs when host key received. - /// - public event EventHandler HostKeyReceived; - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public virtual void Start(Session session, KeyExchangeInitMessage message) - { - this.Session = session; - - this.SendMessage(session.ClientInitMessage); - - // Determine encryption algorithm - var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys - from a in message.EncryptionAlgorithmsClientToServer - where a == b - select a).FirstOrDefault(); - - if (string.IsNullOrEmpty(clientEncryptionAlgorithmName)) - { - throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentClientEncryption = clientEncryptionAlgorithmName; - - // Determine encryption algorithm - var serverDecryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys - from a in message.EncryptionAlgorithmsServerToClient - where a == b - select a).FirstOrDefault(); - if (string.IsNullOrEmpty(serverDecryptionAlgorithmName)) - { - throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentServerEncryption = serverDecryptionAlgorithmName; - - // Determine client hmac algorithm - var clientHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys - from a in message.MacAlgorithmsClientToServer - where a == b - select a).FirstOrDefault(); - if (string.IsNullOrEmpty(clientHmacAlgorithmName)) - { - throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentClientHmacAlgorithm = clientHmacAlgorithmName; - - // Determine server hmac algorithm - var serverHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys - from a in message.MacAlgorithmsServerToClient - where a == b - select a).FirstOrDefault(); - if (string.IsNullOrEmpty(serverHmacAlgorithmName)) - { - throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentServerHmacAlgorithm = serverHmacAlgorithmName; - - // Determine compression algorithm - var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys - from a in message.CompressionAlgorithmsClientToServer - where a == b - select a).LastOrDefault(); - if (string.IsNullOrEmpty(compressionAlgorithmName)) - { - throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentClientCompressionAlgorithm = compressionAlgorithmName; - - // Determine decompression algorithm - var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys - from a in message.CompressionAlgorithmsServerToClient - where a == b - select a).LastOrDefault(); - if (string.IsNullOrEmpty(decompressionAlgorithmName)) - { - throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentServerCompressionAlgorithm = decompressionAlgorithmName; - - this._clientCipherInfo = session.ConnectionInfo.Encryptions[clientEncryptionAlgorithmName]; - this._serverCipherInfo = session.ConnectionInfo.Encryptions[clientEncryptionAlgorithmName]; - this._clientHashInfo = session.ConnectionInfo.HmacAlgorithms[clientHmacAlgorithmName]; - this._serverHashInfo = session.ConnectionInfo.HmacAlgorithms[serverHmacAlgorithmName]; - this._compressionType = session.ConnectionInfo.CompressionAlgorithms[compressionAlgorithmName]; - this._decompressionType = session.ConnectionInfo.CompressionAlgorithms[decompressionAlgorithmName]; - } - - /// - /// Finishes key exchange algorithm. - /// - public virtual void Finish() - { - // Validate hash - if (this.ValidateExchangeHash()) - { - this.SendMessage(new NewKeysMessage()); - } - else - { - throw new SshConnectionException("Key exchange negotiation failed.", DisconnectReason.KeyExchangeFailed); - } - } - - /// - /// Creates the server side cipher to use. - /// - /// Server cipher. - public Cipher CreateServerCipher() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - // Calculate server to client initial IV - var serverVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', sessionId)); - - // Calculate server to client encryption - var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', sessionId)); - - serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, this._serverCipherInfo.KeySize / 8); - - // Create server cipher - return this._serverCipherInfo.Cipher(serverKey, serverVector); - } - - /// - /// Creates the client side cipher to use. - /// - /// Client cipher. - public Cipher CreateClientCipher() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - // Calculate client to server initial IV - var clientVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', sessionId)); - - // Calculate client to server encryption - var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', sessionId)); - - clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, this._clientCipherInfo.KeySize / 8); - - // Create client cipher - return this._clientCipherInfo.Cipher(clientKey, clientVector); - } - - /// - /// Creates the server side hash algorithm to use. - /// - /// Hash algorithm - public HashAlgorithm CreateServerHash() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', sessionId)); - - serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, this._serverHashInfo.KeySize / 8); - - //return serverHMac; - return this._serverHashInfo.HashAlgorithm(serverKey); - } - - /// - /// Creates the client side hash algorithm to use. - /// - /// Hash algorithm - public HashAlgorithm CreateClientHash() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', sessionId)); - - clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, this._clientHashInfo.KeySize / 8); - - //return clientHMac; - return this._clientHashInfo.HashAlgorithm(clientKey); - } - - /// - /// Creates the compression algorithm to use to deflate data. - /// - /// Compression method. - public Compressor CreateCompressor() - { - if (this._compressionType == null) - return null; - - var compressor = this._compressionType.CreateInstance(); - - compressor.Init(this.Session); - - return compressor; - } - - /// - /// Creates the compression algorithm to use to inflate data. - /// - /// Compression method. - public Compressor CreateDecompressor() - { - if (this._compressionType == null) - return null; - - var decompressor = this._decompressionType.CreateInstance(); - - decompressor.Init(this.Session); - - return decompressor; - } - - /// - /// Determines whether the specified host key can be trusted. - /// - /// The host algorithm. - /// - /// true if the specified host can be trusted; otherwise, false. - /// - protected bool CanTrustHostKey(KeyHostAlgorithm host) - { - var args = new HostKeyEventArgs(host); - - if (this.HostKeyReceived != null) - { - this.HostKeyReceived(this, args); - } - - return args.CanTrust; - } - - /// - /// Validates the exchange hash. - /// - /// true if exchange hash is valid; otherwise false. - protected abstract bool ValidateExchangeHash(); - - /// - /// Calculates key exchange hash value. - /// - /// Key exchange hash. - protected abstract byte[] CalculateHash(); - - /// - /// Hashes the specified data bytes. - /// - /// The hash data. - /// - /// Hashed bytes - /// - protected virtual byte[] Hash(byte[] hashData) - { - using (var sha1 = new Renci.SshNet.Security.Cryptography.SHA1Hash()) - { - return sha1.ComputeHash(hashData, 0, hashData.Length); - } - } - - /// - /// Sends SSH message to the server - /// - /// The message. - protected void SendMessage(Message message) - { - this.Session.SendMessage(message); - } - - /// - /// Generates the session key. - /// - /// The shared key. - /// The exchange hash. - /// The key. - /// The size. - /// - private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, byte[] key, int size) - { - var result = new List(key); - while (size > result.Count) - { - result.AddRange(this.Hash(new _SessionKeyAdjustment - { - SharedKey = sharedKey, - ExcahngeHash = exchangeHash, - Key = key, - }.GetBytes())); - } - - return result.ToArray(); - } - - /// - /// Generates the session key. - /// - /// The shared key. - /// The exchange hash. - /// The p. - /// The session id. - /// - private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, char p, byte[] sessionId) - { - return new _SessionKeyGeneration - { - SharedKey = sharedKey, - ExchangeHash = exchangeHash, - Char = p, - SessionId = sessionId, - }.GetBytes(); - } - - private class _SessionKeyGeneration : SshData - { - public BigInteger SharedKey { get; set; } - public byte[] ExchangeHash { get; set; } - public char Char { get; set; } - public byte[] SessionId { get; set; } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.SharedKey); - this.Write(this.ExchangeHash); - this.Write((byte)this.Char); - this.Write(this.SessionId); - } - } - - private class _SessionKeyAdjustment : SshData - { - public BigInteger SharedKey { get; set; } - public byte[] ExcahngeHash { get; set; } - public byte[] Key { get; set; } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.SharedKey); - this.Write(this.ExcahngeHash); - this.Write(this.Key); - } - } - - #region IDisposable Members - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~KeyExchange() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using Renci.SshNet.Common; +using Renci.SshNet.Compression; +using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Security +{ + /// + /// Represents base class for different key exchange algorithm implementations + /// + public abstract class KeyExchange : Algorithm, IDisposable + { + private CipherInfo _clientCipherInfo; + + private CipherInfo _serverCipherInfo; + + private HashInfo _clientHashInfo; + + private HashInfo _serverHashInfo; + + private Type _compressionType; + + private Type _decompressionType; + + /// + /// Gets or sets the session. + /// + /// + /// The session. + /// + protected Session Session { get; private set; } + + /// + /// Gets or sets key exchange shared key. + /// + /// + /// The shared key. + /// + public BigInteger SharedKey { get; protected set; } + + private byte[] _exchangeHash; + /// + /// Gets the exchange hash. + /// + /// The exchange hash. + public byte[] ExchangeHash + { + get + { + if (this._exchangeHash == null) + { + this._exchangeHash = this.CalculateHash(); + } + return this._exchangeHash; + } + } + + /// + /// Occurs when host key received. + /// + public event EventHandler HostKeyReceived; + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public virtual void Start(Session session, KeyExchangeInitMessage message) + { + this.Session = session; + + this.SendMessage(session.ClientInitMessage); + + // Determine encryption algorithm + var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys + from a in message.EncryptionAlgorithmsClientToServer + where a == b + select a).FirstOrDefault(); + + if (string.IsNullOrEmpty(clientEncryptionAlgorithmName)) + { + throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed); + } + + session.ConnectionInfo.CurrentClientEncryption = clientEncryptionAlgorithmName; + + // Determine encryption algorithm + var serverDecryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys + from a in message.EncryptionAlgorithmsServerToClient + where a == b + select a).FirstOrDefault(); + if (string.IsNullOrEmpty(serverDecryptionAlgorithmName)) + { + throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed); + } + + session.ConnectionInfo.CurrentServerEncryption = serverDecryptionAlgorithmName; + + // Determine client hmac algorithm + var clientHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys + from a in message.MacAlgorithmsClientToServer + where a == b + select a).FirstOrDefault(); + if (string.IsNullOrEmpty(clientHmacAlgorithmName)) + { + throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); + } + + session.ConnectionInfo.CurrentClientHmacAlgorithm = clientHmacAlgorithmName; + + // Determine server hmac algorithm + var serverHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys + from a in message.MacAlgorithmsServerToClient + where a == b + select a).FirstOrDefault(); + if (string.IsNullOrEmpty(serverHmacAlgorithmName)) + { + throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); + } + + session.ConnectionInfo.CurrentServerHmacAlgorithm = serverHmacAlgorithmName; + + // Determine compression algorithm + var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys + from a in message.CompressionAlgorithmsClientToServer + where a == b + select a).LastOrDefault(); + if (string.IsNullOrEmpty(compressionAlgorithmName)) + { + throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed); + } + + session.ConnectionInfo.CurrentClientCompressionAlgorithm = compressionAlgorithmName; + + // Determine decompression algorithm + var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys + from a in message.CompressionAlgorithmsServerToClient + where a == b + select a).LastOrDefault(); + if (string.IsNullOrEmpty(decompressionAlgorithmName)) + { + throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed); + } + + session.ConnectionInfo.CurrentServerCompressionAlgorithm = decompressionAlgorithmName; + + this._clientCipherInfo = session.ConnectionInfo.Encryptions[clientEncryptionAlgorithmName]; + this._serverCipherInfo = session.ConnectionInfo.Encryptions[serverDecryptionAlgorithmName]; + this._clientHashInfo = session.ConnectionInfo.HmacAlgorithms[clientHmacAlgorithmName]; + this._serverHashInfo = session.ConnectionInfo.HmacAlgorithms[serverHmacAlgorithmName]; + this._compressionType = session.ConnectionInfo.CompressionAlgorithms[compressionAlgorithmName]; + this._decompressionType = session.ConnectionInfo.CompressionAlgorithms[decompressionAlgorithmName]; + } + + /// + /// Finishes key exchange algorithm. + /// + public virtual void Finish() + { + // Validate hash + if (this.ValidateExchangeHash()) + { + this.SendMessage(new NewKeysMessage()); + } + else + { + throw new SshConnectionException("Key exchange negotiation failed.", DisconnectReason.KeyExchangeFailed); + } + } + + /// + /// Creates the server side cipher to use. + /// + /// Server cipher. + public Cipher CreateServerCipher() + { + // Resolve Session ID + var sessionId = this.Session.SessionId ?? this.ExchangeHash; + + // Calculate server to client initial IV + var serverVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', sessionId)); + + // Calculate server to client encryption + var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', sessionId)); + + serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, this._serverCipherInfo.KeySize / 8); + + // Create server cipher + return this._serverCipherInfo.Cipher(serverKey, serverVector); + } + + /// + /// Creates the client side cipher to use. + /// + /// Client cipher. + public Cipher CreateClientCipher() + { + // Resolve Session ID + var sessionId = this.Session.SessionId ?? this.ExchangeHash; + + // Calculate client to server initial IV + var clientVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', sessionId)); + + // Calculate client to server encryption + var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', sessionId)); + + clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, this._clientCipherInfo.KeySize / 8); + + // Create client cipher + return this._clientCipherInfo.Cipher(clientKey, clientVector); + } + + /// + /// Creates the server side hash algorithm to use. + /// + /// Hash algorithm + public HashAlgorithm CreateServerHash() + { + // Resolve Session ID + var sessionId = this.Session.SessionId ?? this.ExchangeHash; + + var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', sessionId)); + + serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, this._serverHashInfo.KeySize / 8); + + //return serverHMac; + return this._serverHashInfo.HashAlgorithm(serverKey); + } + + /// + /// Creates the client side hash algorithm to use. + /// + /// Hash algorithm + public HashAlgorithm CreateClientHash() + { + // Resolve Session ID + var sessionId = this.Session.SessionId ?? this.ExchangeHash; + + var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', sessionId)); + + clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, this._clientHashInfo.KeySize / 8); + + //return clientHMac; + return this._clientHashInfo.HashAlgorithm(clientKey); + } + + /// + /// Creates the compression algorithm to use to deflate data. + /// + /// Compression method. + public Compressor CreateCompressor() + { + if (this._compressionType == null) + return null; + + var compressor = this._compressionType.CreateInstance(); + + compressor.Init(this.Session); + + return compressor; + } + + /// + /// Creates the compression algorithm to use to inflate data. + /// + /// Compression method. + public Compressor CreateDecompressor() + { + if (this._compressionType == null) + return null; + + var decompressor = this._decompressionType.CreateInstance(); + + decompressor.Init(this.Session); + + return decompressor; + } + + /// + /// Determines whether the specified host key can be trusted. + /// + /// The host algorithm. + /// + /// true if the specified host can be trusted; otherwise, false. + /// + protected bool CanTrustHostKey(KeyHostAlgorithm host) + { + var args = new HostKeyEventArgs(host); + + if (this.HostKeyReceived != null) + { + this.HostKeyReceived(this, args); + } + + return args.CanTrust; + } + + /// + /// Validates the exchange hash. + /// + /// true if exchange hash is valid; otherwise false. + protected abstract bool ValidateExchangeHash(); + + /// + /// Calculates key exchange hash value. + /// + /// Key exchange hash. + protected abstract byte[] CalculateHash(); + + /// + /// Hashes the specified data bytes. + /// + /// The hash data. + /// + /// Hashed bytes + /// + protected virtual byte[] Hash(byte[] hashData) + { + using (var sha1 = new SHA1Hash()) + { + return sha1.ComputeHash(hashData, 0, hashData.Length); + } + } + + /// + /// Sends SSH message to the server + /// + /// The message. + protected void SendMessage(Message message) + { + this.Session.SendMessage(message); + } + + /// + /// Generates the session key. + /// + /// The shared key. + /// The exchange hash. + /// The key. + /// The size. + /// + private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, byte[] key, int size) + { + var result = new List(key); + while (size > result.Count) + { + result.AddRange(this.Hash(new _SessionKeyAdjustment + { + SharedKey = sharedKey, + ExcahngeHash = exchangeHash, + Key = key, + }.GetBytes())); + } + + return result.ToArray(); + } + + /// + /// Generates the session key. + /// + /// The shared key. + /// The exchange hash. + /// The p. + /// The session id. + /// + private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, char p, byte[] sessionId) + { + return new _SessionKeyGeneration + { + SharedKey = sharedKey, + ExchangeHash = exchangeHash, + Char = p, + SessionId = sessionId, + }.GetBytes(); + } + + private class _SessionKeyGeneration : SshData + { + public BigInteger SharedKey { get; set; } + public byte[] ExchangeHash { get; set; } + public char Char { get; set; } + public byte[] SessionId { get; set; } + + protected override void LoadData() + { + throw new NotImplementedException(); + } + + protected override void SaveData() + { + this.Write(this.SharedKey); + this.Write(this.ExchangeHash); + this.Write((byte)this.Char); + this.Write(this.SessionId); + } + } + + private class _SessionKeyAdjustment : SshData + { + public BigInteger SharedKey { get; set; } + public byte[] ExcahngeHash { get; set; } + public byte[] Key { get; set; } + + protected override void LoadData() + { + throw new NotImplementedException(); + } + + protected override void SaveData() + { + this.Write(this.SharedKey); + this.Write(this.ExcahngeHash); + this.Write(this.Key); + } + } + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~KeyExchange() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs index 65db8cc..20b2f1c 100644 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs +++ b/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs @@ -1,142 +1,134 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using Renci.SshNet.Messages.Transport; -using System.Diagnostics; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Represents base class for Diffie Hellman key exchange algorithm - /// - public abstract class KeyExchangeDiffieHellman : KeyExchange - { - /// - /// Specifies key exchange group number. - /// - protected BigInteger _group; - - /// - /// Specifies key exchange prime number. - /// - protected BigInteger _prime; - - /// - /// Specifies client payload - /// - protected byte[] _clientPayload; - - /// - /// Specifies server payload - /// - protected byte[] _serverPayload; - - /// - /// Specifies client exchange number. - /// - protected BigInteger _clientExchangeValue; - - /// - /// Specifies server exchange number. - /// - protected BigInteger _serverExchangeValue; - - /// - /// Specifies random generated number. - /// - protected BigInteger _randomValue; - - /// - /// Specifies host key data. - /// - protected byte[] _hostKey; - - /// - /// Specifies signature data. - /// - protected byte[] _signature; - - /// - /// Validates the exchange hash. - /// - /// - /// true if exchange hash is valid; otherwise false. - /// - protected override bool ValidateExchangeHash() - { - var exchangeHash = this.CalculateHash(); - - var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]); - - var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length); - - var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey); - - this.Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; - - if (this.CanTrustHostKey(key)) - { - - return key.VerifySignature(exchangeHash, this._signature); - } - else - { - return false; - } - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this._serverPayload = message.GetBytes().ToArray(); - this._clientPayload = this.Session.ClientInitMessage.GetBytes().ToArray(); - } - - /// - /// Populates the client exchange value. - /// - protected void PopulateClientExchangeValue() - { - if (this._group.IsZero) - throw new ArgumentNullException("_group"); - - if (this._prime.IsZero) - throw new ArgumentNullException("_prime"); - - var bitLength = this._prime.BitLength; - - do - { - this._randomValue = BigInteger.Random(bitLength); - - this._clientExchangeValue = BigInteger.ModPow(this._group, this._randomValue, this._prime); - - } while (this._clientExchangeValue < 1 || this._clientExchangeValue > ((this._prime - 1))); - } - - /// - /// Handles the server DH reply message. - /// - /// The host key. - /// The server exchange value. - /// The signature. - protected virtual void HandleServerDhReply(byte[] hostKey, BigInteger serverExchangeValue, byte[] signature) - { - this._serverExchangeValue = serverExchangeValue; - this._hostKey = hostKey; - this.SharedKey = BigInteger.ModPow(serverExchangeValue, this._randomValue, this._prime); - this._signature = signature; - } - } -} +using System; +using System.Linq; +using System.Text; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Security +{ + /// + /// Represents base class for Diffie Hellman key exchange algorithm + /// + public abstract class KeyExchangeDiffieHellman : KeyExchange + { + /// + /// Specifies key exchange group number. + /// + protected BigInteger _group; + + /// + /// Specifies key exchange prime number. + /// + protected BigInteger _prime; + + /// + /// Specifies client payload + /// + protected byte[] _clientPayload; + + /// + /// Specifies server payload + /// + protected byte[] _serverPayload; + + /// + /// Specifies client exchange number. + /// + protected BigInteger _clientExchangeValue; + + /// + /// Specifies server exchange number. + /// + protected BigInteger _serverExchangeValue; + + /// + /// Specifies random generated number. + /// + protected BigInteger _randomValue; + + /// + /// Specifies host key data. + /// + protected byte[] _hostKey; + + /// + /// Specifies signature data. + /// + protected byte[] _signature; + + /// + /// Validates the exchange hash. + /// + /// + /// true if exchange hash is valid; otherwise false. + /// + protected override bool ValidateExchangeHash() + { + var exchangeHash = this.CalculateHash(); + + var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]); + + var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length); + + var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey); + + this.Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; + + if (this.CanTrustHostKey(key)) + { + + return key.VerifySignature(exchangeHash, this._signature); + } + return false; + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + this._serverPayload = message.GetBytes().ToArray(); + this._clientPayload = this.Session.ClientInitMessage.GetBytes().ToArray(); + } + + /// + /// Populates the client exchange value. + /// + protected void PopulateClientExchangeValue() + { + if (this._group.IsZero) + throw new ArgumentNullException("_group"); + + if (this._prime.IsZero) + throw new ArgumentNullException("_prime"); + + var bitLength = this._prime.BitLength; + + do + { + this._randomValue = BigInteger.Random(bitLength); + + this._clientExchangeValue = BigInteger.ModPow(this._group, this._randomValue, this._prime); + + } while (this._clientExchangeValue < 1 || this._clientExchangeValue > ((this._prime - 1))); + } + + /// + /// Handles the server DH reply message. + /// + /// The host key. + /// The server exchange value. + /// The signature. + protected virtual void HandleServerDhReply(byte[] hostKey, BigInteger serverExchangeValue, byte[] signature) + { + this._serverExchangeValue = serverExchangeValue; + this._hostKey = hostKey; + this.SharedKey = BigInteger.ModPow(serverExchangeValue, this._randomValue, this._prime); + this._signature = signature; + } + } +} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs index b99671f..a64efbf 100644 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs +++ b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs @@ -1,42 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Transport; -using System.Globalization; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group14-sha1" algorithm implementation. - /// - internal class KeyExchangeDiffieHellmanGroup14Sha1 : KeyExchangeDiffieHellmanGroupSha1 - { - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group14-sha1"; } - } - - /// - /// Gets the group prime. - /// - /// - /// The group prime. - /// - public override BigInteger GroupPrime - { - get - { - BigInteger prime; - var secondOkleyGroup = "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"; - BigInteger.TryParse(secondOkleyGroup, System.Globalization.NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out prime); - return prime; - } - } - } -} +using Renci.SshNet.Common; +using System.Globalization; + +namespace Renci.SshNet.Security +{ + /// + /// Represents "diffie-hellman-group14-sha1" algorithm implementation. + /// + internal class KeyExchangeDiffieHellmanGroup14Sha1 : KeyExchangeDiffieHellmanGroupSha1 + { + private const string SecondOkleyGroup = "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"; + + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "diffie-hellman-group14-sha1"; } + } + + /// + /// Gets the group prime. + /// + /// + /// The group prime. + /// + public override BigInteger GroupPrime + { + get + { + BigInteger prime; + BigInteger.TryParse(SecondOkleyGroup, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out prime); + return prime; + } + } + } +} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs index ae908d6..25613a4 100644 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs +++ b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs @@ -1,42 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Transport; -using System.Globalization; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group1-sha1" algorithm implementation. - /// - public class KeyExchangeDiffieHellmanGroup1Sha1 : KeyExchangeDiffieHellmanGroupSha1 - { - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group1-sha1"; } - } - - /// - /// Gets the group prime. - /// - /// - /// The group prime. - /// - public override BigInteger GroupPrime - { - get - { - BigInteger prime; - var secondOkleyGroup = @"00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"; - BigInteger.TryParse(secondOkleyGroup, System.Globalization.NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out prime); - return prime; - } - } - } -} +using Renci.SshNet.Common; +using System.Globalization; + +namespace Renci.SshNet.Security +{ + /// + /// Represents "diffie-hellman-group1-sha1" algorithm implementation. + /// + public class KeyExchangeDiffieHellmanGroup1Sha1 : KeyExchangeDiffieHellmanGroupSha1 + { + private const string SecondOkleyGroup = @"00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"; + + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "diffie-hellman-group1-sha1"; } + } + + /// + /// Gets the group prime. + /// + /// + /// The group prime. + /// + public override BigInteger GroupPrime + { + get + { + BigInteger prime; + BigInteger.TryParse(SecondOkleyGroup, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out prime); + return prime; + } + } + } +} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs index ddd2a84..2e91396 100644 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs +++ b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs @@ -1,166 +1,161 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Messages.Transport; -using System.Security.Cryptography; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using System.Diagnostics; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group-exchange-sha1" algorithm implementation. - /// - internal class KeyExchangeDiffieHellmanGroupExchangeSha1 : KeyExchangeDiffieHellman - { - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group-exchange-sha1"; } - } - - /// - /// Calculates key exchange hash value. - /// - /// - /// Key exchange hash. - /// - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - MinimumGroupSize = 1024, - PreferredGroupSize = 1024, - MaximumGroupSize = 1024, - Prime = this._prime, - SubGroup = this._group, - ClientExchangeValue = this._clientExchangeValue, - ServerExchangeValue = this._serverExchangeValue, - SharedKey = this.SharedKey, - }.GetBytes(); - - return this.Hash(hashData); - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - // 1. send SSH_MSG_KEY_DH_GEX_REQUEST - this.SendMessage(new KeyExchangeDhGroupExchangeRequest(1024, 1024, 1024)); - } - - /// - /// Finishes key exchange algorithm. - /// - public override void Finish() - { - base.Finish(); - - this.Session.MessageReceived -= Session_MessageReceived; - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var groupMessage = e.Message as KeyExchangeDhGroupExchangeGroup; - - if (groupMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - - // 2. Receive SSH_MSG_KEX_DH_GEX_GROUP - this._prime = groupMessage.SafePrime; - this._group = groupMessage.SubGroup; - - this.PopulateClientExchangeValue(); - - // 3. Send SSH_MSG_KEX_DH_GEX_INIT - this.SendMessage(new KeyExchangeDhGroupExchangeInit(this._clientExchangeValue)); - - } - var replyMessage = e.Message as KeyExchangeDhGroupExchangeReply; - - if (replyMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.HandleServerDhReply(replyMessage.HostKey, replyMessage.F, replyMessage.Signature); - - // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed - this.Finish(); - } - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public UInt32 MinimumGroupSize { get; set; } - - public UInt32 PreferredGroupSize { get; set; } - - public UInt32 MaximumGroupSize { get; set; } - - public BigInteger Prime { get; set; } - - public BigInteger SubGroup { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new System.NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.MinimumGroupSize); - this.Write(this.PreferredGroupSize); - this.Write(this.MaximumGroupSize); - this.Write(this.Prime); - this.Write(this.SubGroup); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - } -} +using System; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Messages; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Security +{ + /// + /// Represents "diffie-hellman-group-exchange-sha1" algorithm implementation. + /// + internal class KeyExchangeDiffieHellmanGroupExchangeSha1 : KeyExchangeDiffieHellman + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "diffie-hellman-group-exchange-sha1"; } + } + + /// + /// Calculates key exchange hash value. + /// + /// + /// Key exchange hash. + /// + protected override byte[] CalculateHash() + { + var hashData = new _ExchangeHashData + { + ClientVersion = this.Session.ClientVersion, + ServerVersion = this.Session.ServerVersion, + ClientPayload = this._clientPayload, + ServerPayload = this._serverPayload, + HostKey = this._hostKey, + MinimumGroupSize = 1024, + PreferredGroupSize = 1024, + MaximumGroupSize = 1024, + Prime = this._prime, + SubGroup = this._group, + ClientExchangeValue = this._clientExchangeValue, + ServerExchangeValue = this._serverExchangeValue, + SharedKey = this.SharedKey, + }.GetBytes(); + + return this.Hash(hashData); + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); + this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); + + this.Session.MessageReceived += Session_MessageReceived; + + // 1. send SSH_MSG_KEY_DH_GEX_REQUEST + this.SendMessage(new KeyExchangeDhGroupExchangeRequest(1024, 1024, 1024)); + } + + /// + /// Finishes key exchange algorithm. + /// + public override void Finish() + { + base.Finish(); + + this.Session.MessageReceived -= Session_MessageReceived; + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + var groupMessage = e.Message as KeyExchangeDhGroupExchangeGroup; + + if (groupMessage != null) + { + // Unregister message once received + this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); + + // 2. Receive SSH_MSG_KEX_DH_GEX_GROUP + this._prime = groupMessage.SafePrime; + this._group = groupMessage.SubGroup; + + this.PopulateClientExchangeValue(); + + // 3. Send SSH_MSG_KEX_DH_GEX_INIT + this.SendMessage(new KeyExchangeDhGroupExchangeInit(this._clientExchangeValue)); + + } + var replyMessage = e.Message as KeyExchangeDhGroupExchangeReply; + + if (replyMessage != null) + { + // Unregister message once received + this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); + + this.HandleServerDhReply(replyMessage.HostKey, replyMessage.F, replyMessage.Signature); + + // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed + this.Finish(); + } + } + + private class _ExchangeHashData : SshData + { + public string ServerVersion { get; set; } + + public string ClientVersion { get; set; } + + public byte[] ClientPayload { get; set; } + + public byte[] ServerPayload { get; set; } + + public byte[] HostKey { get; set; } + + public UInt32 MinimumGroupSize { get; set; } + + public UInt32 PreferredGroupSize { get; set; } + + public UInt32 MaximumGroupSize { get; set; } + + public BigInteger Prime { get; set; } + + public BigInteger SubGroup { get; set; } + + public BigInteger ClientExchangeValue { get; set; } + + public BigInteger ServerExchangeValue { get; set; } + + public BigInteger SharedKey { get; set; } + + protected override void LoadData() + { + throw new NotImplementedException(); + } + + protected override void SaveData() + { + this.Write(this.ClientVersion); + this.Write(this.ServerVersion); + this.WriteBinaryString(this.ClientPayload); + this.WriteBinaryString(this.ServerPayload); + this.WriteBinaryString(this.HostKey); + this.Write(this.MinimumGroupSize); + this.Write(this.PreferredGroupSize); + this.Write(this.MaximumGroupSize); + this.Write(this.Prime); + this.Write(this.SubGroup); + this.Write(this.ClientExchangeValue); + this.Write(this.ServerExchangeValue); + this.Write(this.SharedKey); + } + } + } +} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs index b936e5e..5cc9c23 100644 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs +++ b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs @@ -1,182 +1,177 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Messages.Transport; -using System.Security.Cryptography; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using System.Diagnostics; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group-exchange-sha256" algorithm implementation. - /// - public class KeyExchangeDiffieHellmanGroupExchangeSha256 : KeyExchangeDiffieHellman - { - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group-exchange-sha256"; } - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - // 1. send SSH_MSG_KEY_DH_GEX_REQUEST - this.SendMessage(new KeyExchangeDhGroupExchangeRequest(1024,1024,1024)); - - } - - /// - /// Finishes key exchange algorithm. - /// - public override void Finish() - { - base.Finish(); - - this.Session.MessageReceived -= Session_MessageReceived; - } - - /// - /// Calculates key exchange hash value. - /// - /// - /// Key exchange hash. - /// - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - MinimumGroupSize = 1024, - PreferredGroupSize = 1024, - MaximumGroupSize = 1024, - Prime = this._prime, - SubGroup = this._group, - ClientExchangeValue = this._clientExchangeValue, - ServerExchangeValue = this._serverExchangeValue, - SharedKey = this.SharedKey, - }.GetBytes(); - - return this.Hash(hashData); - } - - /// - /// Hashes the specified data bytes. - /// - /// Data to hash. - /// - /// Hashed bytes - /// - protected override byte[] Hash(byte[] hashBytes) - { - using (var md = new SHA256Hash()) - { - return md.ComputeHash(hashBytes); - } - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var groupMessage = e.Message as KeyExchangeDhGroupExchangeGroup; - - if (groupMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - - // 2. Receive SSH_MSG_KEX_DH_GEX_GROUP - this._prime = groupMessage.SafePrime; - this._group = groupMessage.SubGroup; - - this.PopulateClientExchangeValue(); - - // 3. Send SSH_MSG_KEX_DH_GEX_INIT - this.SendMessage(new KeyExchangeDhGroupExchangeInit(this._clientExchangeValue)); - } - var replyMessage = e.Message as KeyExchangeDhGroupExchangeReply; - - if (replyMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.HandleServerDhReply(replyMessage.HostKey, replyMessage.F, replyMessage.Signature); - - // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed - this.Finish(); - } - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public UInt32 MinimumGroupSize { get; set; } - - public UInt32 PreferredGroupSize { get; set; } - - public UInt32 MaximumGroupSize { get; set; } - - public BigInteger Prime { get; set; } - - public BigInteger SubGroup { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new System.NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.MinimumGroupSize); - this.Write(this.PreferredGroupSize); - this.Write(this.MaximumGroupSize); - this.Write(this.Prime); - this.Write(this.SubGroup); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - } -} +using System; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Messages; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Security +{ + /// + /// Represents "diffie-hellman-group-exchange-sha256" algorithm implementation. + /// + public class KeyExchangeDiffieHellmanGroupExchangeSha256 : KeyExchangeDiffieHellman + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "diffie-hellman-group-exchange-sha256"; } + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); + this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); + + this.Session.MessageReceived += Session_MessageReceived; + + // 1. send SSH_MSG_KEY_DH_GEX_REQUEST + this.SendMessage(new KeyExchangeDhGroupExchangeRequest(1024,1024,1024)); + + } + + /// + /// Finishes key exchange algorithm. + /// + public override void Finish() + { + base.Finish(); + + this.Session.MessageReceived -= Session_MessageReceived; + } + + /// + /// Calculates key exchange hash value. + /// + /// + /// Key exchange hash. + /// + protected override byte[] CalculateHash() + { + var hashData = new _ExchangeHashData + { + ClientVersion = this.Session.ClientVersion, + ServerVersion = this.Session.ServerVersion, + ClientPayload = this._clientPayload, + ServerPayload = this._serverPayload, + HostKey = this._hostKey, + MinimumGroupSize = 1024, + PreferredGroupSize = 1024, + MaximumGroupSize = 1024, + Prime = this._prime, + SubGroup = this._group, + ClientExchangeValue = this._clientExchangeValue, + ServerExchangeValue = this._serverExchangeValue, + SharedKey = this.SharedKey, + }.GetBytes(); + + return this.Hash(hashData); + } + + /// + /// Hashes the specified data bytes. + /// + /// Data to hash. + /// + /// Hashed bytes + /// + protected override byte[] Hash(byte[] hashBytes) + { + using (var md = new SHA256Hash()) + { + return md.ComputeHash(hashBytes); + } + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + var groupMessage = e.Message as KeyExchangeDhGroupExchangeGroup; + + if (groupMessage != null) + { + // Unregister message once received + this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); + + // 2. Receive SSH_MSG_KEX_DH_GEX_GROUP + this._prime = groupMessage.SafePrime; + this._group = groupMessage.SubGroup; + + this.PopulateClientExchangeValue(); + + // 3. Send SSH_MSG_KEX_DH_GEX_INIT + this.SendMessage(new KeyExchangeDhGroupExchangeInit(this._clientExchangeValue)); + } + var replyMessage = e.Message as KeyExchangeDhGroupExchangeReply; + + if (replyMessage != null) + { + // Unregister message once received + this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); + + this.HandleServerDhReply(replyMessage.HostKey, replyMessage.F, replyMessage.Signature); + + // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed + this.Finish(); + } + } + + private class _ExchangeHashData : SshData + { + public string ServerVersion { get; set; } + + public string ClientVersion { get; set; } + + public byte[] ClientPayload { get; set; } + + public byte[] ServerPayload { get; set; } + + public byte[] HostKey { get; set; } + + public UInt32 MinimumGroupSize { get; set; } + + public UInt32 PreferredGroupSize { get; set; } + + public UInt32 MaximumGroupSize { get; set; } + + public BigInteger Prime { get; set; } + + public BigInteger SubGroup { get; set; } + + public BigInteger ClientExchangeValue { get; set; } + + public BigInteger ServerExchangeValue { get; set; } + + public BigInteger SharedKey { get; set; } + + protected override void LoadData() + { + throw new NotImplementedException(); + } + + protected override void SaveData() + { + this.Write(this.ClientVersion); + this.Write(this.ServerVersion); + this.WriteBinaryString(this.ClientPayload); + this.WriteBinaryString(this.ServerPayload); + this.WriteBinaryString(this.HostKey); + this.Write(this.MinimumGroupSize); + this.Write(this.PreferredGroupSize); + this.Write(this.MaximumGroupSize); + this.Write(this.Prime); + this.Write(this.SubGroup); + this.Write(this.ClientExchangeValue); + this.Write(this.ServerExchangeValue); + this.Write(this.SharedKey); + } + } + } +} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs index e72fab1..d66bff5 100644 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs +++ b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs @@ -1,132 +1,127 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Transport; -using System.Globalization; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group1-sha1" algorithm implementation. - /// - public abstract class KeyExchangeDiffieHellmanGroupSha1 : KeyExchangeDiffieHellman - { - /// - /// Gets the group prime. - /// - /// - /// The group prime. - /// - public abstract BigInteger GroupPrime { get; } - - /// - /// Calculates key exchange hash value. - /// - /// - /// Key exchange hash. - /// - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - ClientExchangeValue = this._clientExchangeValue, - ServerExchangeValue = this._serverExchangeValue, - SharedKey = this.SharedKey, - }.GetBytes(); - - return this.Hash(hashData); - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this.Session.RegisterMessage("SSH_MSG_KEXDH_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - this._prime = this.GroupPrime; - - this._group = new BigInteger(new byte[] { 2 }); - - this.PopulateClientExchangeValue(); - - this.SendMessage(new KeyExchangeDhInitMessage(this._clientExchangeValue)); - - } - - /// - /// Finishes key exchange algorithm. - /// - public override void Finish() - { - base.Finish(); - - this.Session.MessageReceived -= Session_MessageReceived; - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var message = e.Message as KeyExchangeDhReplyMessage; - if (message != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEXDH_REPLY"); - - this.HandleServerDhReply(message.HostKey, message.F, message.Signature); - - // When SSH_MSG_KEXDH_REPLY received key exchange is completed - this.Finish(); - } - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new System.NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - } -} +using Renci.SshNet.Common; +using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Transport; + +namespace Renci.SshNet.Security +{ + /// + /// Represents "diffie-hellman-group1-sha1" algorithm implementation. + /// + public abstract class KeyExchangeDiffieHellmanGroupSha1 : KeyExchangeDiffieHellman + { + /// + /// Gets the group prime. + /// + /// + /// The group prime. + /// + public abstract BigInteger GroupPrime { get; } + + /// + /// Calculates key exchange hash value. + /// + /// + /// Key exchange hash. + /// + protected override byte[] CalculateHash() + { + var hashData = new _ExchangeHashData + { + ClientVersion = this.Session.ClientVersion, + ServerVersion = this.Session.ServerVersion, + ClientPayload = this._clientPayload, + ServerPayload = this._serverPayload, + HostKey = this._hostKey, + ClientExchangeValue = this._clientExchangeValue, + ServerExchangeValue = this._serverExchangeValue, + SharedKey = this.SharedKey, + }.GetBytes(); + + return this.Hash(hashData); + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + this.Session.RegisterMessage("SSH_MSG_KEXDH_REPLY"); + + this.Session.MessageReceived += Session_MessageReceived; + + this._prime = this.GroupPrime; + + this._group = new BigInteger(new byte[] { 2 }); + + this.PopulateClientExchangeValue(); + + this.SendMessage(new KeyExchangeDhInitMessage(this._clientExchangeValue)); + + } + + /// + /// Finishes key exchange algorithm. + /// + public override void Finish() + { + base.Finish(); + + this.Session.MessageReceived -= Session_MessageReceived; + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + var message = e.Message as KeyExchangeDhReplyMessage; + if (message != null) + { + // Unregister message once received + this.Session.UnRegisterMessage("SSH_MSG_KEXDH_REPLY"); + + this.HandleServerDhReply(message.HostKey, message.F, message.Signature); + + // When SSH_MSG_KEXDH_REPLY received key exchange is completed + this.Finish(); + } + } + + private class _ExchangeHashData : SshData + { + public string ServerVersion { get; set; } + + public string ClientVersion { get; set; } + + public byte[] ClientPayload { get; set; } + + public byte[] ServerPayload { get; set; } + + public byte[] HostKey { get; set; } + + public BigInteger ClientExchangeValue { get; set; } + + public BigInteger ServerExchangeValue { get; set; } + + public BigInteger SharedKey { get; set; } + + protected override void LoadData() + { + throw new System.NotImplementedException(); + } + + protected override void SaveData() + { + this.Write(this.ClientVersion); + this.Write(this.ServerVersion); + this.WriteBinaryString(this.ClientPayload); + this.WriteBinaryString(this.ServerPayload); + this.WriteBinaryString(this.HostKey); + this.Write(this.ClientExchangeValue); + this.Write(this.ServerExchangeValue); + this.Write(this.SharedKey); + } + } + } +} diff --git a/Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs b/Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs index 1f6d85c..50a2cb5 100644 --- a/Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs +++ b/Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs @@ -1,284 +1,279 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using Renci.SshNet.Messages.Transport; -using System.Diagnostics; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; -using System.Globalization; - -namespace Renci.SshNet.Security -{ - /// - /// Represents base class for Diffie Hellman key exchange algorithm - /// - public class KeyExchangeEllipticCurveDiffieHellman : KeyExchange - { - /// - /// Specifies client payload - /// - protected byte[] _clientPayload; - - /// - /// Specifies server payload - /// - protected byte[] _serverPayload; - - /// - /// Specifies client exchange number. - /// - protected BigInteger _clientExchangeValue; - - /// - /// Specifies server exchange number. - /// - protected BigInteger _serverExchangeValue; - - /// - /// Specifies random generated number. - /// - protected BigInteger _randomValue; - - /// - /// Specifies host key data. - /// - protected byte[] _hostKey; - - /// - /// Specifies signature data. - /// - protected byte[] _signature; - - //// 256 - //p = FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF - //a = FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFC - //b = 5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 3BCE3C3E 27D2604B - //S = C49D3608 86E70493 6A6678E1 139D26B7 819F7E90 - //The base point G in compressed form is: - //G = 03 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 F4A13945 D898C296 - //and in uncompressed form is: - //G = 04 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 F4A13945 D898C296 4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE CBB64068 37BF51F5 - //n = FFFFFFFF 00000000 FFFFFFFF FFFFFFFF BCE6FAAD A7179E84 F3B9CAC2 FC632551 - //h = 01 - - //// 384 - //p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF 00000000 00000000 FFFFFFFF - //a = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF 00000000 00000000 FFFFFFFC - //b = B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF - //S = A335926A A319A27A 1D00896A 6773A482 7ACDAC73 - //The base point G in compressed form is: - //G = 03 AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7 - //and in uncompressed form is: - //G = 04 AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7 3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F - //n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF C7634D81 F4372DDF 581A0DB2 48B0A77A ECEC196A CCC52973 - //h = 01 - - public override string Name - { - get { return "ecdh-sha2-nistp256"; } - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this._serverPayload = message.GetBytes().ToArray(); - this._clientPayload = this.Session.ClientInitMessage.GetBytes().ToArray(); - - this.Session.RegisterMessage("SSH_MSG_KEXECDH_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - //3.2.1 Elliptic Curve Key Pair Generation Primitive - //Elliptic curve key pairs should be generated as follows: - //Input: Valid elliptic curve domain parameters T = (p, a, b, G, n, h) or (m, f(x), a, b,G, n, h). - //Output: An elliptic curve key pair (d,Q) associated with T. - //Actions: Generate an elliptic curve key pair as follows: - //1. Randomly or pseudorandomly select an integer d in the interval [1, n − 1]. - //2. Compute Q = dG. - //3. Output (d,Q). - - BigInteger p; - BigInteger.TryParse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", System.Globalization.NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out p); - - - - BigInteger n; - BigInteger.TryParse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", System.Globalization.NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out n); - BigInteger G; - BigInteger.TryParse("00036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", System.Globalization.NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out G); - - BigInteger d; - - do - { - d = BigInteger.Random(n.BitLength); - } while (d < 1 || d > n); - - var Q = d * G; - - - this.SendMessage(new KeyExchangeEcdhInitMessage(d, Q)); - - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var message = e.Message as KeyExchangeEcdhReplyMessage; - if (message != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEXECDH_REPLY"); - - this.HandleServerEcdhReply(); - - // When SSH_MSG_KEXDH_REPLY received key exchange is completed - this.Finish(); - } - } - - /// - /// Validates the exchange hash. - /// - /// - /// true if exchange hash is valid; otherwise false. - /// - protected override bool ValidateExchangeHash() - { - //var exchangeHash = this.CalculateHash(); - - //var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]); - - //var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length); - - //var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey); - - //this.Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; - - //if (this.CanTrustHostKey(key)) - //{ - - // return key.VerifySignature(exchangeHash, this._signature); - //} - //else - //{ - // return false; - //} - - return false; - } - - /// - /// Populates the client exchange value. - /// - //protected void PopulateClientExchangeValue() - //{ - // if (this._group.IsZero) - // throw new ArgumentNullException("_group"); - - // if (this._prime.IsZero) - // throw new ArgumentNullException("_prime"); - - // var bitLength = this._prime.BitLength; - - // do - // { - // this._randomValue = BigInteger.Random(bitLength); - - // this._clientExchangeValue = BigInteger.ModPow(this._group, this._randomValue, this._prime); - - // } while (this._clientExchangeValue < 1 || this._clientExchangeValue > ((this._prime - 1))); - //} - - protected virtual void HandleServerEcdhReply() - { - //this._serverExchangeValue = serverExchangeValue; - //this._hostKey = hostKey; - //this.SharedKey = BigInteger.ModPow(serverExchangeValue, this._randomValue, this._prime); - //this._signature = signature; - } - - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - SharedKey = this.SharedKey, - }.GetBytes(); - - //string V_C, client's identification string (CR and LF excluded) - //string V_S, server's identification string (CR and LF excluded) - //string I_C, payload of the client's SSH_MSG_KEXINIT - //string I_S, payload of the server's SSH_MSG_KEXINIT - //string K_S, server's public host key - //string Q_C, client's ephemeral public key octet string - //string Q_S, server's ephemeral public key octet string - //mpint K, shared secret - return this.Hash(hashData); - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public UInt32 MinimumGroupSize { get; set; } - - public UInt32 PreferredGroupSize { get; set; } - - public UInt32 MaximumGroupSize { get; set; } - - public BigInteger Prime { get; set; } - - public BigInteger SubGroup { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new System.NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.MinimumGroupSize); - this.Write(this.PreferredGroupSize); - this.Write(this.MaximumGroupSize); - this.Write(this.Prime); - this.Write(this.SubGroup); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - - } -} +using System; +using System.Linq; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Messages; +using Renci.SshNet.Common; +using System.Globalization; + +namespace Renci.SshNet.Security +{ + /// + /// Represents base class for Diffie Hellman key exchange algorithm + /// + public class KeyExchangeEllipticCurveDiffieHellman : KeyExchange + { + /// + /// Specifies client payload + /// + protected byte[] _clientPayload; + + /// + /// Specifies server payload + /// + protected byte[] _serverPayload; + + /// + /// Specifies client exchange number. + /// + protected BigInteger _clientExchangeValue; + + /// + /// Specifies server exchange number. + /// + protected BigInteger _serverExchangeValue; + + /// + /// Specifies random generated number. + /// + protected BigInteger _randomValue; + + /// + /// Specifies host key data. + /// + protected byte[] _hostKey; + + /// + /// Specifies signature data. + /// + protected byte[] _signature; + + //// 256 + //p = FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF + //a = FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFC + //b = 5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 3BCE3C3E 27D2604B + //S = C49D3608 86E70493 6A6678E1 139D26B7 819F7E90 + //The base point G in compressed form is: + //G = 03 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 F4A13945 D898C296 + //and in uncompressed form is: + //G = 04 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 F4A13945 D898C296 4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE CBB64068 37BF51F5 + //n = FFFFFFFF 00000000 FFFFFFFF FFFFFFFF BCE6FAAD A7179E84 F3B9CAC2 FC632551 + //h = 01 + + //// 384 + //p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF 00000000 00000000 FFFFFFFF + //a = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF 00000000 00000000 FFFFFFFC + //b = B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF + //S = A335926A A319A27A 1D00896A 6773A482 7ACDAC73 + //The base point G in compressed form is: + //G = 03 AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7 + //and in uncompressed form is: + //G = 04 AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7 3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F + //n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF C7634D81 F4372DDF 581A0DB2 48B0A77A ECEC196A CCC52973 + //h = 01 + + public override string Name + { + get { return "ecdh-sha2-nistp256"; } + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + this._serverPayload = message.GetBytes().ToArray(); + this._clientPayload = this.Session.ClientInitMessage.GetBytes().ToArray(); + + this.Session.RegisterMessage("SSH_MSG_KEXECDH_REPLY"); + + this.Session.MessageReceived += Session_MessageReceived; + + //3.2.1 Elliptic Curve Key Pair Generation Primitive + //Elliptic curve key pairs should be generated as follows: + //Input: Valid elliptic curve domain parameters T = (p, a, b, G, n, h) or (m, f(x), a, b,G, n, h). + //Output: An elliptic curve key pair (d,Q) associated with T. + //Actions: Generate an elliptic curve key pair as follows: + //1. Randomly or pseudorandomly select an integer d in the interval [1, n − 1]. + //2. Compute Q = dG. + //3. Output (d,Q). + + BigInteger p; + BigInteger.TryParse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out p); + + + + BigInteger n; + BigInteger.TryParse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out n); + BigInteger G; + BigInteger.TryParse("00036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out G); + + BigInteger d; + + do + { + d = BigInteger.Random(n.BitLength); + } while (d < 1 || d > n); + + var Q = d * G; + + + this.SendMessage(new KeyExchangeEcdhInitMessage(d, Q)); + + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + var message = e.Message as KeyExchangeEcdhReplyMessage; + if (message != null) + { + // Unregister message once received + this.Session.UnRegisterMessage("SSH_MSG_KEXECDH_REPLY"); + + this.HandleServerEcdhReply(); + + // When SSH_MSG_KEXDH_REPLY received key exchange is completed + this.Finish(); + } + } + + /// + /// Validates the exchange hash. + /// + /// + /// true if exchange hash is valid; otherwise false. + /// + protected override bool ValidateExchangeHash() + { + //var exchangeHash = this.CalculateHash(); + + //var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]); + + //var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length); + + //var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey); + + //this.Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; + + //if (this.CanTrustHostKey(key)) + //{ + + // return key.VerifySignature(exchangeHash, this._signature); + //} + //else + //{ + // return false; + //} + + return false; + } + + /// + /// Populates the client exchange value. + /// + //protected void PopulateClientExchangeValue() + //{ + // if (this._group.IsZero) + // throw new ArgumentNullException("_group"); + + // if (this._prime.IsZero) + // throw new ArgumentNullException("_prime"); + + // var bitLength = this._prime.BitLength; + + // do + // { + // this._randomValue = BigInteger.Random(bitLength); + + // this._clientExchangeValue = BigInteger.ModPow(this._group, this._randomValue, this._prime); + + // } while (this._clientExchangeValue < 1 || this._clientExchangeValue > ((this._prime - 1))); + //} + + protected virtual void HandleServerEcdhReply() + { + //this._serverExchangeValue = serverExchangeValue; + //this._hostKey = hostKey; + //this.SharedKey = BigInteger.ModPow(serverExchangeValue, this._randomValue, this._prime); + //this._signature = signature; + } + + protected override byte[] CalculateHash() + { + var hashData = new _ExchangeHashData + { + ClientVersion = this.Session.ClientVersion, + ServerVersion = this.Session.ServerVersion, + ClientPayload = this._clientPayload, + ServerPayload = this._serverPayload, + HostKey = this._hostKey, + SharedKey = this.SharedKey, + }.GetBytes(); + + //string V_C, client's identification string (CR and LF excluded) + //string V_S, server's identification string (CR and LF excluded) + //string I_C, payload of the client's SSH_MSG_KEXINIT + //string I_S, payload of the server's SSH_MSG_KEXINIT + //string K_S, server's public host key + //string Q_C, client's ephemeral public key octet string + //string Q_S, server's ephemeral public key octet string + //mpint K, shared secret + return this.Hash(hashData); + } + + private class _ExchangeHashData : SshData + { + public string ServerVersion { get; set; } + + public string ClientVersion { get; set; } + + public byte[] ClientPayload { get; set; } + + public byte[] ServerPayload { get; set; } + + public byte[] HostKey { get; set; } + + public UInt32 MinimumGroupSize { get; set; } + + public UInt32 PreferredGroupSize { get; set; } + + public UInt32 MaximumGroupSize { get; set; } + + public BigInteger Prime { get; set; } + + public BigInteger SubGroup { get; set; } + + public BigInteger ClientExchangeValue { get; set; } + + public BigInteger ServerExchangeValue { get; set; } + + public BigInteger SharedKey { get; set; } + + protected override void LoadData() + { + throw new NotImplementedException(); + } + + protected override void SaveData() + { + this.Write(this.ClientVersion); + this.Write(this.ServerVersion); + this.WriteBinaryString(this.ClientPayload); + this.WriteBinaryString(this.ServerPayload); + this.WriteBinaryString(this.HostKey); + this.Write(this.MinimumGroupSize); + this.Write(this.PreferredGroupSize); + this.Write(this.MaximumGroupSize); + this.Write(this.Prime); + this.Write(this.SubGroup); + this.Write(this.ClientExchangeValue); + this.Write(this.ServerExchangeValue); + this.Write(this.SharedKey); + } + } + + } +} diff --git a/Renci.SshNet/Security/KeyHostAlgorithm.cs b/Renci.SshNet/Security/KeyHostAlgorithm.cs index 74f90c6..261b882 100644 --- a/Renci.SshNet/Security/KeyHostAlgorithm.cs +++ b/Renci.SshNet/Security/KeyHostAlgorithm.cs @@ -1,173 +1,170 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Implements key support for host algorithm. - /// - public class KeyHostAlgorithm : HostAlgorithm - { - - /// - /// Gets the key. - /// - public Key Key { get; private set; } - - /// - /// Gets the public key data. - /// - public override byte[] Data - { - get - { - return new SshKeyData(this.Name, this.Key.Public).GetBytes(); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// Host key name. - /// Host key. - public KeyHostAlgorithm(string name, Key key) - : base(name) - { - this.Key = key; - } - - /// - /// Initializes a new instance of the class. - /// - /// Host key name. - /// Host key. - /// Host key encoded data. - public KeyHostAlgorithm(string name, Key key, byte[] data) - : base(name) - { - this.Key = key; - - var sshKey = new SshKeyData(); - sshKey.Load(data); - this.Key.Public = sshKey.Keys; - } - - /// - /// Signs the specified data. - /// - /// The data. - /// - /// Signed data. - /// - public override byte[] Sign(byte[] data) - { - return new SignatureKeyData(this.Name, this.Key.Sign(data)).GetBytes().ToArray(); - } - - /// - /// Verifies the signature. - /// - /// The data. - /// The signature. - /// - /// True is signature was successfully verifies; otherwise false. - /// - public override bool VerifySignature(byte[] data, byte[] signature) - { - var signatureData = new SignatureKeyData(); - signatureData.Load(signature); - - return this.Key.VerifySignature(data, signatureData.Signature); - } - - private class SshKeyData : SshData - { - public BigInteger[] Keys { get; private set; } - - public string Name { get; private set; } - - public SshKeyData() - { - - } - - public SshKeyData(string name, params BigInteger[] keys) - { - this.Name = name; - this.Keys = keys; - } - - protected override void LoadData() - { - this.Name = this.ReadString(); - var keys = new List(); - while (!this.IsEndOfData) - { - keys.Add(this.ReadBigInt()); - } - this.Keys = keys.ToArray(); - } - - protected override void SaveData() - { - this.Write(this.Name); - foreach (var key in this.Keys) - { - this.Write(key); - } - } - } - - private class SignatureKeyData : SshData - { - /// - /// Gets or sets the name of the algorithm. - /// - /// - /// The name of the algorithm. - /// - public string AlgorithmName { get; private set; } - - /// - /// Gets or sets the signature. - /// - /// - /// The signature. - /// - public byte[] Signature { get; private set; } - - public SignatureKeyData() - { - - } - - public SignatureKeyData(string name, byte[] signature) - { - this.AlgorithmName = name; - this.Signature = signature; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.AlgorithmName = this.ReadString(); - this.Signature = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.AlgorithmName); - this.WriteBinaryString(this.Signature); - } - } - } -} +using System.Collections.Generic; +using System.Linq; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Security +{ + /// + /// Implements key support for host algorithm. + /// + public class KeyHostAlgorithm : HostAlgorithm + { + + /// + /// Gets the key. + /// + public Key Key { get; private set; } + + /// + /// Gets the public key data. + /// + public override byte[] Data + { + get + { + return new SshKeyData(this.Name, this.Key.Public).GetBytes(); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// Host key name. + /// Host key. + public KeyHostAlgorithm(string name, Key key) + : base(name) + { + this.Key = key; + } + + /// + /// Initializes a new instance of the class. + /// + /// Host key name. + /// Host key. + /// Host key encoded data. + public KeyHostAlgorithm(string name, Key key, byte[] data) + : base(name) + { + this.Key = key; + + var sshKey = new SshKeyData(); + sshKey.Load(data); + this.Key.Public = sshKey.Keys; + } + + /// + /// Signs the specified data. + /// + /// The data. + /// + /// Signed data. + /// + public override byte[] Sign(byte[] data) + { + return new SignatureKeyData(this.Name, this.Key.Sign(data)).GetBytes(); + } + + /// + /// Verifies the signature. + /// + /// The data. + /// The signature. + /// + /// True is signature was successfully verifies; otherwise false. + /// + public override bool VerifySignature(byte[] data, byte[] signature) + { + var signatureData = new SignatureKeyData(); + signatureData.Load(signature); + + return this.Key.VerifySignature(data, signatureData.Signature); + } + + private class SshKeyData : SshData + { + public BigInteger[] Keys { get; private set; } + + public string Name { get; private set; } + + public SshKeyData() + { + + } + + public SshKeyData(string name, params BigInteger[] keys) + { + this.Name = name; + this.Keys = keys; + } + + protected override void LoadData() + { + this.Name = this.ReadString(); + var keys = new List(); + while (!this.IsEndOfData) + { + keys.Add(this.ReadBigInt()); + } + this.Keys = keys.ToArray(); + } + + protected override void SaveData() + { + this.Write(this.Name); + foreach (var key in this.Keys) + { + this.Write(key); + } + } + } + + private class SignatureKeyData : SshData + { + /// + /// Gets or sets the name of the algorithm. + /// + /// + /// The name of the algorithm. + /// + public string AlgorithmName { get; private set; } + + /// + /// Gets or sets the signature. + /// + /// + /// The signature. + /// + public byte[] Signature { get; private set; } + + public SignatureKeyData() + { + + } + + public SignatureKeyData(string name, byte[] signature) + { + this.AlgorithmName = name; + this.Signature = signature; + } + + /// + /// Called when type specific data need to be loaded. + /// + protected override void LoadData() + { + this.AlgorithmName = this.ReadString(); + this.Signature = this.ReadBinaryString(); + } + + /// + /// Called when type specific data need to be saved. + /// + protected override void SaveData() + { + this.Write(this.AlgorithmName); + this.WriteBinaryString(this.Signature); + } + } + } +} diff --git a/Renci.SshNet/Session.NET.cs b/Renci.SshNet/Session.NET.cs index 5493332..6432a5d 100644 --- a/Renci.SshNet/Session.NET.cs +++ b/Renci.SshNet/Session.NET.cs @@ -1,188 +1,196 @@ -using System.Linq; -using System; -using System.Net.Sockets; -using System.Net; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using System.Threading; -using Renci.SshNet.Messages.Transport; -using System.IO; -using System.Diagnostics; -using System.Text; -using System.Collections.Generic; - -namespace Renci.SshNet -{ - public partial class Session - { - private TraceSource _log = -#if DEBUG - new TraceSource("SshNet.Logging", SourceLevels.All); -#else - new TraceSource("SshNet.Logging"); -#endif - - partial void IsSocketConnected(ref bool isConnected) - { - isConnected = (!this._isDisconnecting && this._socket != null && this._socket.Connected && this._isAuthenticated && this._messageListenerCompleted != null) - && this._socket.Poll(-1, SelectMode.SelectWrite); - } - - partial void SocketConnect(string host, int port) - { - IPAddress addr = host.GetIPAddress(); - - var ep = new IPEndPoint(addr, port); - this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - var socketBufferSize = 2 * MAXIMUM_PACKET_SIZE; - - this._socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, socketBufferSize); - this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, socketBufferSize); - - this.Log(string.Format("Initiating connect to '{0}:{1}'.", this.ConnectionInfo.Host, this.ConnectionInfo.Port)); - - // Connect socket with specified timeout - var connectResult = this._socket.BeginConnect(ep, null, null); - - if (!connectResult.AsyncWaitHandle.WaitOne(this.ConnectionInfo.Timeout, false)) - { - throw new SshOperationTimeoutException("Connection Could Not Be Established"); - } - - this._socket.EndConnect(connectResult); - } - - partial void SocketDisconnect() - { - this._socket.Disconnect(true); - } - - partial void SocketReadLine(ref string response) - { - var encoding = new Renci.SshNet.Common.ASCIIEncoding(); - - var line = new StringBuilder(); - // Read data one byte at a time to find end of line and leave any unhandled information in the buffer to be processed later - var buffer = new List(); - - var data = new byte[1]; - do - { - var asyncResult = this._socket.BeginReceive(data, 0, data.Length, SocketFlags.None, null, null); - - if (!asyncResult.AsyncWaitHandle.WaitOne(this.ConnectionInfo.Timeout)) - throw new SshOperationTimeoutException("Socket read operation has timed out"); - - var received = this._socket.EndReceive(asyncResult); - - // If zero bytes received then exit - if (received == 0) - break; - - buffer.Add(data[0]); - } - while (!(buffer.Count > 0 && (buffer[buffer.Count - 1] == 0x0A || buffer[buffer.Count - 1] == 0x00))); - - // Return an empty version string if the buffer consists of a 0x00 character. - if (buffer.Count > 0 && buffer[buffer.Count - 1] == 0x00) - { - response = string.Empty; - } - else if (buffer.Count > 1 && buffer[buffer.Count - 2] == 0x0D) - response = encoding.GetString(buffer.Take(buffer.Count - 2).ToArray()); - else - response = encoding.GetString(buffer.Take(buffer.Count - 1).ToArray()); - } - - /// - /// Function to read amount of data before returning, or throwing an exception. - /// - /// The amount wanted. - /// The buffer to read to. - /// Happens when the socket is closed. - /// Unhandled exception. - partial void SocketRead(int length, ref byte[] buffer) - { - var offset = 0; - int receivedTotal = 0; // how many bytes is already received - - do - { - try - { - var receivedBytes = this._socket.Receive(buffer, offset + receivedTotal, length - receivedTotal, SocketFlags.None); - if (receivedBytes > 0) - { - receivedTotal += receivedBytes; - continue; - } - else - { - // 2012-09-11: Kenneth_aa - // When Disconnect or Dispose is called, this throws SshConnectionException(), which... - // 1 - goes up to ReceiveMessage() - // 2 - up again to MessageListener() - // which is where there is a catch-all exception block so it can notify event listeners. - // 3 - MessageListener then again calls RaiseError(). - // There the exception is checked for the exception thrown here (ConnectionLost), and if it matches it will not call Session.SendDisconnect(). - // - // Adding a check for this._isDisconnecting causes ReceiveMessage() to throw SshConnectionException: "Bad packet length {0}". - // - throw new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); - } - } - catch (SocketException exp) - { - if (exp.SocketErrorCode == SocketError.ConnectionAborted) - { - buffer = new byte[length]; - this.Disconnect(); - return; - } - else if (exp.SocketErrorCode == SocketError.WouldBlock || - exp.SocketErrorCode == SocketError.IOPending || - exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable) - { - // socket buffer is probably empty, wait and try again - Thread.Sleep(30); - } - else - throw; // any serious error occurred - } - } while (receivedTotal < length); - } - - partial void SocketWrite(byte[] data) - { - int sent = 0; // how many bytes is already sent - int length = data.Length; - - do - { - try - { - sent += this._socket.Send(data, sent, length - sent, SocketFlags.None); - } - catch (SocketException ex) - { - if (ex.SocketErrorCode == SocketError.WouldBlock || - ex.SocketErrorCode == SocketError.IOPending || - ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable) - { - // socket buffer is probably full, wait and try again - Thread.Sleep(30); - } - else - throw; // any serious error occurr - } - } while (sent < length); - } - - partial void Log(string text) - { - this._log.TraceEvent(System.Diagnostics.TraceEventType.Verbose, 1, text); - } - } -} +using System.Linq; +using System; +using System.Net.Sockets; +using System.Net; +using Renci.SshNet.Common; +using System.Threading; +using Renci.SshNet.Messages.Transport; +using System.Diagnostics; +using System.Collections.Generic; + +namespace Renci.SshNet +{ + public partial class Session + { + private readonly TraceSource _log = +#if DEBUG + new TraceSource("SshNet.Logging", SourceLevels.All); +#else + new TraceSource("SshNet.Logging"); +#endif + + /// + /// Gets a value indicating whether the socket is connected. + /// + /// + /// true if the socket is connected; otherwise, false. + /// + partial void IsSocketConnected(ref bool isConnected) + { + isConnected = (_socket != null && _socket.Connected); + if (isConnected) + { + var connectionClosedOrDataAvailable = _socket.Poll(1000, SelectMode.SelectRead); + isConnected = !(connectionClosedOrDataAvailable && _socket.Available == 0); + } + } + + partial void SocketConnect(string host, int port) + { + const int socketBufferSize = 2 * MaximumSshPacketSize; + + var addr = host.GetIPAddress(); + + var ep = new IPEndPoint(addr, port); + this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + + this._socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, socketBufferSize); + this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, socketBufferSize); + + this.Log(string.Format("Initiating connect to '{0}:{1}'.", this.ConnectionInfo.Host, this.ConnectionInfo.Port)); + + // Connect socket with specified timeout + var connectResult = this._socket.BeginConnect(ep, null, null); + + if (!connectResult.AsyncWaitHandle.WaitOne(this.ConnectionInfo.Timeout, false)) + { + throw new SshOperationTimeoutException("Connection Could Not Be Established"); + } + + this._socket.EndConnect(connectResult); + } + + partial void SocketDisconnect() + { + _socket.Disconnect(true); + } + + partial void SocketReadLine(ref string response) + { + var encoding = new ASCIIEncoding(); + + // Read data one byte at a time to find end of line and leave any unhandled information in the buffer to be processed later + var buffer = new List(); + + var data = new byte[1]; + do + { + var asyncResult = this._socket.BeginReceive(data, 0, data.Length, SocketFlags.None, null, null); + + if (!asyncResult.AsyncWaitHandle.WaitOne(this.ConnectionInfo.Timeout)) + throw new SshOperationTimeoutException("Socket read operation has timed out"); + + var received = this._socket.EndReceive(asyncResult); + + // If zero bytes received then exit + if (received == 0) + break; + + buffer.Add(data[0]); + } + while (!(buffer.Count > 0 && (buffer[buffer.Count - 1] == 0x0A || buffer[buffer.Count - 1] == 0x00))); + + // Return an empty version string if the buffer consists of a 0x00 character. + if (buffer.Count > 0 && buffer[buffer.Count - 1] == 0x00) + { + response = string.Empty; + } + else if (buffer.Count > 1 && buffer[buffer.Count - 2] == 0x0D) + response = encoding.GetString(buffer.Take(buffer.Count - 2).ToArray()); + else + response = encoding.GetString(buffer.Take(buffer.Count - 1).ToArray()); + } + + /// + /// Function to read amount of data before returning, or throwing an exception. + /// + /// The amount wanted. + /// The buffer to read to. + /// Happens when the socket is closed. + /// Unhandled exception. + partial void SocketRead(int length, ref byte[] buffer) + { + var receivedTotal = 0; // how many bytes is already received + + do + { + try + { + var receivedBytes = this._socket.Receive(buffer, receivedTotal, length - receivedTotal, SocketFlags.None); + if (receivedBytes > 0) + { + receivedTotal += receivedBytes; + continue; + } + + // 2012-09-11: Kenneth_aa + // When Disconnect or Dispose is called, this throws SshConnectionException(), which... + // 1 - goes up to ReceiveMessage() + // 2 - up again to MessageListener() + // which is where there is a catch-all exception block so it can notify event listeners. + // 3 - MessageListener then again calls RaiseError(). + // There the exception is checked for the exception thrown here (ConnectionLost), and if it matches it will not call Session.SendDisconnect(). + // + // Adding a check for this._isDisconnecting causes ReceiveMessage() to throw SshConnectionException: "Bad packet length {0}". + // + + if (_isDisconnecting) + throw new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); + throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); + } + catch (SocketException exp) + { + if (exp.SocketErrorCode == SocketError.ConnectionAborted) + { + buffer = new byte[length]; + this.Disconnect(); + return; + } + + if (exp.SocketErrorCode == SocketError.WouldBlock || + exp.SocketErrorCode == SocketError.IOPending || + exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable) + { + // socket buffer is probably empty, wait and try again + Thread.Sleep(30); + } + else + throw; // any serious error occurred + } + } while (receivedTotal < length); + } + + partial void SocketWrite(byte[] data) + { + var sent = 0; // how many bytes is already sent + var length = data.Length; + + do + { + try + { + sent += this._socket.Send(data, sent, length - sent, SocketFlags.None); + } + catch (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.WouldBlock || + ex.SocketErrorCode == SocketError.IOPending || + ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable) + { + // socket buffer is probably full, wait and try again + Thread.Sleep(30); + } + else + throw; // any serious error occurr + } + } while (sent < length); + } + + [Conditional("DEBUG")] + partial void Log(string text) + { + this._log.TraceEvent(TraceEventType.Verbose, 1, text); + } + } +} diff --git a/Renci.SshNet/Session.NET40.cs b/Renci.SshNet/Session.NET40.cs index cdc721b..7d286a2 100644 --- a/Renci.SshNet/Session.NET40.cs +++ b/Renci.SshNet/Session.NET40.cs @@ -1,48 +1,44 @@ -using System.Threading.Tasks; -using System.Linq; -using System; -using System.Net.Sockets; -using System.Net; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using System.Threading; -using Renci.SshNet.Messages.Transport; - -namespace Renci.SshNet -{ - /// - /// Provides functionality to connect and interact with SSH server. - /// - public partial class Session - { - partial void HandleMessageCore(Message message) - { - this.HandleMessage((dynamic)message); - } - - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem((o) => { action(); }); - } - - partial void InternalRegisterMessage(string messageName) - { - lock (this._messagesMetadata) - { - Parallel.ForEach( - from m in this._messagesMetadata where m.Name == messageName select m, - (item) => { item.Enabled = true; item.Activated = true; }); - } - } - - partial void InternalUnRegisterMessage(string messageName) - { - lock (this._messagesMetadata) - { - Parallel.ForEach( - from m in this._messagesMetadata where m.Name == messageName select m, - (item) => { item.Enabled = false; item.Activated = false; }); - } - } - } -} +using System.Threading.Tasks; +using System.Linq; +using System; +using Renci.SshNet.Messages; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Provides functionality to connect and interact with SSH server. + /// + public partial class Session + { + partial void HandleMessageCore(Message message) + { + this.HandleMessage((dynamic)message); + } + + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem(o => action()); + } + + partial void InternalRegisterMessage(string messageName) + { + lock (this._messagesMetadata) + { + Parallel.ForEach( + from m in this._messagesMetadata where m.Name == messageName select m, + item => { item.Enabled = true; item.Activated = true; }); + } + } + + partial void InternalUnRegisterMessage(string messageName) + { + lock (this._messagesMetadata) + { + Parallel.ForEach( + from m in this._messagesMetadata where m.Name == messageName select m, + item => { item.Enabled = false; item.Activated = false; }); + } + } + } +} diff --git a/Renci.SshNet/Session.cs b/Renci.SshNet/Session.cs index 18229b8..b5dbd7f 100644 --- a/Renci.SshNet/Session.cs +++ b/Renci.SshNet/Session.cs @@ -1,2030 +1,2121 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using Renci.SshNet.Compression; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages.Connection; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Security; -using System.Globalization; -using Renci.SshNet.Security.Cryptography.Ciphers; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet -{ - /// - /// Provides functionality to connect and interact with SSH server. - /// - public partial class Session : IDisposable - { - /// - /// Specifies maximum packet size defined by the protocol. - /// - protected const int MAXIMUM_PACKET_SIZE = 35000; - - /// - /// Specifies maximum payload size defined by the protocol. - /// - protected const int MAXIMUM_PAYLOAD_SIZE = 1024 * 32; - - private static RNGCryptoServiceProvider _randomizer = new System.Security.Cryptography.RNGCryptoServiceProvider(); - -#if SILVERLIGHT - private static Regex _serverVersionRe = new Regex("^SSH-(?[^-]+)-(?.+)( SP.+)?$"); -#else - private static Regex _serverVersionRe = new Regex("^SSH-(?[^-]+)-(?.+)( SP.+)?$", RegexOptions.Compiled); -#endif - - /// - /// Controls how many authentication attempts can take place at the same time. - /// - /// - /// Some server may restrict number to prevent authentication attacks - /// - private static SemaphoreLight _authenticationConnection = new SemaphoreLight(3); - - /// - /// Holds metada about session messages - /// - private IEnumerable _messagesMetadata; - - /// - /// Holds connection socket. - /// - private Socket _socket; - - /// - /// Holds locker object for the socket - /// - private object _socketLock = new object(); - - /// - /// Holds reference to task that listens for incoming messages - /// - private EventWaitHandle _messageListenerCompleted; - - /// - /// Specifies outbound packet number - /// - private volatile UInt32 _outboundPacketSequence = 0; - - /// - /// Specifies incoming packet number - /// - private UInt32 _inboundPacketSequence = 0; - - /// - /// WaitHandle to signal that last service request was accepted - /// - private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); - - /// - /// WaitHandle to signal that exception was thrown by another thread. - /// - private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(false); - - /// - /// WaitHandle to signal that key exchange was completed. - /// - private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent(false); - - /// - /// WaitHandle to signal that key exchange is in progress. - /// - private bool _keyExchangeInProgress = false; - - /// - /// Exception that need to be thrown by waiting thread - /// - private Exception _exception; - - /// - /// Specifies whether connection is authenticated - /// - private bool _isAuthenticated; - - /// - /// Specifies whether user issued Disconnect command or not - /// - private bool _isDisconnecting; - - private KeyExchange _keyExchange; - - private HashAlgorithm _serverMac; - - private HashAlgorithm _clientMac; - - private Cipher _clientCipher; - - private Cipher _serverCipher; - - private Compressor _serverDecompression; - - private Compressor _clientCompression; - - private SemaphoreLight _sessionSemaphore; - /// - /// Gets the session semaphore that controls session channels. - /// - /// The session semaphore. - public SemaphoreLight SessionSemaphore - { - get - { - if (this._sessionSemaphore == null) - { - lock (this) - { - if (this._sessionSemaphore == null) - { - this._sessionSemaphore = new SemaphoreLight(this.ConnectionInfo.MaxSessions); - } - } - } - - return this._sessionSemaphore; - } - } - - private bool _isDisconnectMessageSent; - - private uint _nextChannelNumber; - /// - /// Gets the next channel number. - /// - /// The next channel number. - internal uint NextChannelNumber - { - get - { - uint result; - - lock (this) - { - result = this._nextChannelNumber++; - } - - return result; - } - } - - /// - /// Gets a value indicating whether socket connected. - /// - /// - /// true if socket connected; otherwise, false. - /// - public bool IsConnected - { - get - { - var isSocketConnected = false; - IsSocketConnected(ref isSocketConnected); - return isSocketConnected; - } - } - - /// - /// Gets or sets the session id. - /// - /// The session id. - public byte[] SessionId { get; private set; } - - private Message _clientInitMessage; - /// - /// Gets the client init message. - /// - /// The client init message. - public Message ClientInitMessage - { - get - { - if (this._clientInitMessage == null) - { - this._clientInitMessage = new KeyExchangeInitMessage() - { - KeyExchangeAlgorithms = this.ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), - ServerHostKeyAlgorithms = this.ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), - EncryptionAlgorithmsClientToServer = this.ConnectionInfo.Encryptions.Keys.ToArray(), - EncryptionAlgorithmsServerToClient = this.ConnectionInfo.Encryptions.Keys.ToArray(), - MacAlgorithmsClientToServer = this.ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - MacAlgorithmsServerToClient = this.ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - CompressionAlgorithmsClientToServer = this.ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - CompressionAlgorithmsServerToClient = this.ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - LanguagesClientToServer = new string[] { string.Empty }, - LanguagesServerToClient = new string[] { string.Empty }, - FirstKexPacketFollows = false, - Reserved = 0, - }; - } - return this._clientInitMessage; - } - } - - /// - /// Gets or sets the server version string. - /// - /// The server version. - public string ServerVersion { get; private set; } - - /// - /// Gets or sets the client version string. - /// - /// The client version. - public string ClientVersion { get; private set; } - - /// - /// Gets or sets the connection info. - /// - /// The connection info. - public ConnectionInfo ConnectionInfo { get; private set; } - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccured; - - /// - /// Occurs when session has been disconnected form the server. - /// - public event EventHandler Disconnected; - - /// - /// Occurs when host key received. - /// - public event EventHandler HostKeyReceived; - - #region Message events - - /// - /// Occurs when message received - /// - internal event EventHandler> DisconnectReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> IgnoreReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UnimplementedReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> DebugReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ServiceRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ServiceAcceptReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> KeyExchangeInitReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> NewKeysReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationFailureReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationSuccessReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationBannerReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> GlobalRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> RequestSuccessReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> RequestFailureReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelOpenReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelOpenConfirmationReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelOpenFailureReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelWindowAdjustReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelDataReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelExtendedDataReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelEofReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelCloseReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelSuccessReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelFailureReceived; - - /// - /// Occurs when message received and is not handled by any of the event handlers - /// - internal event EventHandler> MessageReceived; - - #endregion - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - internal Session(ConnectionInfo connectionInfo) - { - this.ConnectionInfo = connectionInfo; - //this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.{0}", this.GetType().Assembly.GetName().Version); - this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.0.0.1"); - } - - /// - /// Connects to the server. - /// - public void Connect() - { - // TODO: Add exception documentation for Proxy. - if (this.ConnectionInfo == null) - { - throw new ArgumentNullException("connectionInfo"); - } - - if (this.IsConnected) - return; - - try - { - _authenticationConnection.Wait(); - - if (this.IsConnected) - return; - - lock (this) - { - // If connected don't connect again - if (this.IsConnected) - return; - - // Build list of available messages while connecting - this._messagesMetadata = GetMessagesMetadata(); - - switch (this.ConnectionInfo.ProxyType) - { - case ProxyTypes.None: - this.SocketConnect(this.ConnectionInfo.Host, this.ConnectionInfo.Port); - break; - case ProxyTypes.Socks4: - this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); - this.ConnectSocks4(); - break; - case ProxyTypes.Socks5: - this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); - this.ConnectSocks5(); - break; - case ProxyTypes.Http: - this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); - this.ConnectHttp(); - break; - default: - break; - } - - - Match versionMatch = null; - - // Get server version from the server, - // ignore text lines which are sent before if any - while (true) - { - string serverVersion = string.Empty; - this.SocketReadLine(ref serverVersion); - - this.ServerVersion = serverVersion; - if (string.IsNullOrEmpty(this.ServerVersion)) - { - throw new InvalidOperationException("Server string is null or empty."); - } - - versionMatch = _serverVersionRe.Match(this.ServerVersion); - - if (versionMatch.Success) - { - break; - } - } - - // Set connection versions - this.ConnectionInfo.ServerVersion = this.ServerVersion; - this.ConnectionInfo.ClientVersion = this.ClientVersion; - - // Get server SSH version - var version = versionMatch.Result("${protoversion}"); - - var softwareName = versionMatch.Result("${softwareversion}"); - - this.Log(string.Format("Server version '{0}' on '{1}'.", version, softwareName)); - - if (!(version.Equals("2.0") || version.Equals("1.99"))) - { - throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", version), DisconnectReason.ProtocolVersionNotSupported); - } - - this.SocketWrite(Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\x0D\x0A", this.ClientVersion))); - - // Register Transport response messages - this.RegisterMessage("SSH_MSG_DISCONNECT"); - this.RegisterMessage("SSH_MSG_IGNORE"); - this.RegisterMessage("SSH_MSG_UNIMPLEMENTED"); - this.RegisterMessage("SSH_MSG_DEBUG"); - this.RegisterMessage("SSH_MSG_SERVICE_ACCEPT"); - this.RegisterMessage("SSH_MSG_KEXINIT"); - this.RegisterMessage("SSH_MSG_NEWKEYS"); - - // Some server implementations might sent this message first, prior establishing encryption algorithm - this.RegisterMessage("SSH_MSG_USERAUTH_BANNER"); - - // Start incoming request listener - this._messageListenerCompleted = new ManualResetEvent(false); - - this.ExecuteThread(() => - { - try - { - this.MessageListener(); - } - finally - { - this._messageListenerCompleted.Set(); - } - }); - - // Wait for key exchange to be completed - this.WaitHandle(this._keyExchangeCompletedWaitHandle); - - // If sessionId is not set then its not connected - if (this.SessionId == null) - { - this.Disconnect(); - return; - } - - // Request user authorization service - this.SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication)); - - // Wait for service to be accepted - this.WaitHandle(this._serviceAccepted); - - if (string.IsNullOrEmpty(this.ConnectionInfo.Username)) - { - throw new SshException("Username is not specified."); - } - - this._isAuthenticated = this.ConnectionInfo.Authenticate(this); - - if (!this._isAuthenticated) - { - throw new SshAuthenticationException("User cannot be authenticated."); - } - - // Register Connection messages - this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST"); - this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); - this.RegisterMessage("SSH_MSG_REQUEST_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); - this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); - this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); - this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST"); - this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); - this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_DATA"); - this.RegisterMessage("SSH_MSG_CHANNEL_EOF"); - this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE"); - - Monitor.Pulse(this); - } - } - finally - { - _authenticationConnection.Release(); - } - } - - /// - /// Disconnects from the server - /// - public void Disconnect() - { - this._isDisconnecting = true; - - // If socket still open try to send disconnect message to the server - this.SendDisconnect(DisconnectReason.ByApplication, "Connection terminated by the client."); - - //this.Dispose(); - } - - internal T CreateChannel() where T : Channel, new() - { - return CreateChannel(0, 0x100000, 0x8000); - } - - internal T CreateChannel(uint serverChannelNumber, uint windowSize, uint packetSize) where T : Channel, new() - { - T channel = new T(); - lock (this) - { - channel.Initialize(this, serverChannelNumber, windowSize, packetSize); - } - return channel; - } - - /// - /// Sends "keep alive" message to keep connection alive. - /// - internal void SendKeepAlive() - { - this.SendMessage(new IgnoreMessage()); - } - - /// - /// Waits for handle to signal while checking other handles as well including timeout check to prevent waiting for ever - /// - /// The wait handle. - internal void WaitHandle(WaitHandle waitHandle) - { - var waitHandles = new WaitHandle[] - { - this._exceptionWaitHandle, - waitHandle, - }; - - switch (EventWaitHandle.WaitAny(waitHandles, this.ConnectionInfo.Timeout)) - { - case 0: - throw this._exception; - case System.Threading.WaitHandle.WaitTimeout: - this.SendDisconnect(DisconnectReason.ByApplication, "Operation timeout"); - throw new SshOperationTimeoutException("Session operation has timed out"); - default: - break; - } - } - - /// - /// Sends packet message to the server. - /// - /// The message. - internal void SendMessage(Message message) - { - if (this._socket == null || !this._socket.CanWrite()) - throw new SshConnectionException("Client not connected."); - - if (this._keyExchangeInProgress && !(message is IKeyExchangedAllowed)) - { - // Wait for key exchange to be completed - this.WaitHandle(this._keyExchangeCompletedWaitHandle); - } - - this.Log(string.Format("SendMessage to server '{0}': '{1}'.", message.GetType().Name, message.ToString())); - - // Messages can be sent by different thread so we need to synchronize it - var paddingMultiplier = this._clientCipher == null ? (byte)8 : Math.Max((byte)8, this._serverCipher.MinimumSize); // Should be recalculate base on cipher min length if cipher specified - - var messageData = message.GetBytes(); - - if (messageData.Length > Session.MAXIMUM_PAYLOAD_SIZE) - { - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Payload cannot be more then {0} bytes.", Session.MAXIMUM_PAYLOAD_SIZE)); - } - - if (this._clientCompression != null) - { - messageData = this._clientCompression.Compress(messageData); - } - - var packetLength = messageData.Length + 4 + 1; // add length bytes and padding byte - byte paddingLength = (byte)((-packetLength) & (paddingMultiplier - 1)); - if (paddingLength < paddingMultiplier) - { - paddingLength += paddingMultiplier; - } - - // Build Packet data - var packetData = new byte[4 + 1 + messageData.Length + paddingLength]; - - // Add packet length - ((uint)packetData.Length - 4).GetBytes().CopyTo(packetData, 0); - - // Add packet padding length - packetData[4] = paddingLength; - - // Add packet payload - messageData.CopyTo(packetData, 4 + 1); - - // Add random padding - var paddingBytes = new byte[paddingLength]; - _randomizer.GetBytes(paddingBytes); - paddingBytes.CopyTo(packetData, 4 + 1 + messageData.Length); - - // Lock handling of _outboundPacketSequence since it must be sent sequently to server - lock (this._socketLock) - { - if (this._socket == null || !this._socket.Connected) - throw new SshConnectionException("Client not connected."); - - // Calculate packet hash - var hashData = new byte[4 + packetData.Length]; - this._outboundPacketSequence.GetBytes().CopyTo(hashData, 0); - packetData.CopyTo(hashData, 4); - - // Encrypt packet data - if (this._clientCipher != null) - { - packetData = this._clientCipher.Encrypt(packetData); - } - - if (packetData.Length > Session.MAXIMUM_PACKET_SIZE) - { - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Packet is too big. Maximum packet size is {0} bytes.", Session.MAXIMUM_PACKET_SIZE)); - } - - if (this._clientMac == null) - { - this.SocketWrite(packetData); - } - else - { - var hash = this._clientMac.ComputeHash(hashData.ToArray()); - - var data = new byte[packetData.Length + this._clientMac.HashSize / 8]; - packetData.CopyTo(data, 0); - hash.CopyTo(data, packetData.Length); - - this.SocketWrite(data); - } - - this._outboundPacketSequence++; - - Monitor.Pulse(this._socketLock); - } - } - - private static IEnumerable GetMessagesMetadata() - { - return new MessageMetadata[] - { - new MessageMetadata { Name = "SSH_MSG_NEWKEYS", Number = 21, Enabled = false, Activated = false, Type = typeof(NewKeysMessage), }, - new MessageMetadata { Name = "SSH_MSG_REQUEST_FAILURE", Number = 82, Enabled = false, Activated = false, Type = typeof(RequestFailureMessage), }, - new MessageMetadata { Name = "SSH_MSG_KEXINIT", Number = 20, Enabled = false, Activated = false, Type = typeof(KeyExchangeInitMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN_FAILURE", Number = 92, Enabled = false, Activated = false, Type = typeof(ChannelOpenFailureMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_FAILURE", Number = 100, Enabled = false, Activated = false, Type = typeof(ChannelFailureMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_EXTENDED_DATA", Number = 95, Enabled = false, Activated = false, Type = typeof(ChannelExtendedDataMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_DATA", Number = 94, Enabled = false, Activated = false, Type = typeof(ChannelDataMessage), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_REQUEST", Number = 50, Enabled = false, Activated = false, Type = typeof(RequestMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_REQUEST", Number = 98, Enabled = false, Activated = false, Type = typeof(ChannelRequestMessage), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_BANNER", Number = 53, Enabled = false, Activated = false, Type = typeof(BannerMessage), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_INFO_RESPONSE", Number = 61, Enabled = false, Activated = false, Type = typeof(InformationResponseMessage), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_FAILURE", Number = 51, Enabled = false, Activated = false, Type = typeof(FailureMessage), }, - new MessageMetadata { Name = "SSH_MSG_DEBUG", Number = 4, Enabled = false, Activated = false, Type = typeof(DebugMessage), }, - new MessageMetadata { Name = "SSH_MSG_KEXDH_INIT", Number = 30, Enabled = false, Activated = false, Type = typeof(KeyExchangeDhInitMessage), }, - new MessageMetadata { Name = "SSH_MSG_GLOBAL_REQUEST", Number = 80, Enabled = false, Activated = false, Type = typeof(GlobalRequestMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN", Number = 90, Enabled = false, Activated = false, Type = typeof(ChannelOpenMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", Number = 91, Enabled = false, Activated = false, Type = typeof(ChannelOpenConfirmationMessage), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_INFO_REQUEST", Number = 60, Enabled = false, Activated = false, Type = typeof(InformationRequestMessage), }, - new MessageMetadata { Name = "SSH_MSG_UNIMPLEMENTED", Number = 3, Enabled = false, Activated = false, Type = typeof(UnimplementedMessage), }, - new MessageMetadata { Name = "SSH_MSG_REQUEST_SUCCESS", Number = 81, Enabled = false, Activated = false, Type = typeof(RequestSuccessMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_SUCCESS", Number = 99, Enabled = false, Activated = false, Type = typeof(ChannelSuccessMessage), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", Number = 60, Enabled = false, Activated = false, Type = typeof(PasswordChangeRequiredMessage), }, - new MessageMetadata { Name = "SSH_MSG_DISCONNECT", Number = 1, Enabled = false, Activated = false, Type = typeof(DisconnectMessage), }, - new MessageMetadata { Name = "SSH_MSG_SERVICE_REQUEST", Number = 5, Enabled = false, Activated = false, Type = typeof(ServiceRequestMessage), }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_REQUEST", Number = 34, Enabled = false, Activated = false, Type = typeof(KeyExchangeDhGroupExchangeRequest), }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_GROUP", Number = 31, Enabled = false, Activated = false, Type = typeof(KeyExchangeDhGroupExchangeGroup), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_SUCCESS", Number = 52, Enabled = false, Activated = false, Type = typeof(SuccessMessage), }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_PK_OK", Number = 60, Enabled = false, Activated = false, Type = typeof(PublicKeyMessage), }, - new MessageMetadata { Name = "SSH_MSG_IGNORE", Number = 2, Enabled = false, Activated = false, Type = typeof(IgnoreMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_WINDOW_ADJUST", Number = 93, Enabled = false, Activated = false, Type = typeof(ChannelWindowAdjustMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_EOF", Number = 96, Enabled = false, Activated = false, Type = typeof(ChannelEofMessage), }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_CLOSE", Number = 97, Enabled = false, Activated = false, Type = typeof(ChannelCloseMessage), }, - new MessageMetadata { Name = "SSH_MSG_SERVICE_ACCEPT", Number = 6, Enabled = false, Activated = false, Type = typeof(ServiceAcceptMessage), }, - new MessageMetadata { Name = "SSH_MSG_KEXDH_REPLY", Number = 31, Enabled = false, Activated = false, Type = typeof(KeyExchangeDhReplyMessage), }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_INIT", Number = 32, Enabled = false, Activated = false, Type = typeof(KeyExchangeDhGroupExchangeInit), }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_REPLY", Number = 33, Enabled = false, Activated = false, Type = typeof(KeyExchangeDhGroupExchangeReply), }, - }; - } - - /// - /// Receives the message from the server. - /// - /// Incoming SSH message. - /// - private Message ReceiveMessage() - { - if (!this._socket.Connected) - return null; - - // No lock needed since all messages read by only one thread - var blockSize = this._serverCipher == null ? (byte)8 : Math.Max((byte)8, this._serverCipher.MinimumSize); - - // Read packet length first - var firstBlock = this.Read(blockSize); - - if (this._serverCipher != null) - { - firstBlock = this._serverCipher.Decrypt(firstBlock); - } - - var packetLength = (uint)(firstBlock[0] << 24 | firstBlock[1] << 16 | firstBlock[2] << 8 | firstBlock[3]); - - // Test packet minimum and maximum boundaries - if (packetLength < Math.Max((byte)16, blockSize) - 4 || packetLength > Session.MAXIMUM_PACKET_SIZE - 4) - throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length {0}", packetLength), DisconnectReason.ProtocolError); - - // Read rest of the packet data - int bytesToRead = (int)(packetLength - (blockSize - 4)); - - var data = new byte[bytesToRead + blockSize]; - - firstBlock.CopyTo(data, 0); - - byte[] serverHash = null; - if (this._serverMac != null) - { - serverHash = new byte[this._serverMac.HashSize / 8]; - bytesToRead += serverHash.Length; - } - - if (bytesToRead > 0) - { - var nextBlocks = this.Read(bytesToRead); - - if (serverHash != null) - { - Buffer.BlockCopy(nextBlocks, nextBlocks.Length - serverHash.Length, serverHash, 0, serverHash.Length); - nextBlocks = nextBlocks.Take(nextBlocks.Length - serverHash.Length).ToArray(); - } - - if (nextBlocks.Length > 0) - { - if (this._serverCipher != null) - { - nextBlocks = this._serverCipher.Decrypt(nextBlocks); - } - nextBlocks.CopyTo(data, blockSize); - } - } - - var paddingLength = data[4]; - - var messagePayload = new byte[packetLength - paddingLength - 1]; - Buffer.BlockCopy(data, 5, messagePayload, 0, messagePayload.Length); - - if (this._serverDecompression != null) - { - messagePayload = this._serverDecompression.Decompress(messagePayload); - } - - // Validate message against MAC - if (this._serverMac != null) - { - var clientHashData = new byte[4 + data.Length]; - var lengthBytes = this._inboundPacketSequence.GetBytes(); - - lengthBytes.CopyTo(clientHashData, 0); - data.CopyTo(clientHashData, 4); - - // Calculate packet hash - var clientHash = this._serverMac.ComputeHash(clientHashData); - - if (!serverHash.SequenceEqual(clientHash)) - { - throw new SshConnectionException("MAC error", DisconnectReason.MacError); - } - } - - this._inboundPacketSequence++; - - return this.LoadMessage(messagePayload); - } - - private void SendDisconnect(DisconnectReason reasonCode, string message) - { - // If disconnect message was sent already dont send it again - if (this._isDisconnectMessageSent) - return; - - var disconnectMessage = new DisconnectMessage(reasonCode, message); - - this.SendMessage(disconnectMessage); - - this._isDisconnectMessageSent = true; - } - - partial void HandleMessageCore(Message message); - - /// - /// Handles the message. - /// - /// - /// The message. - private void HandleMessage(T message) where T : Message - { - this.OnMessageReceived(message); - } - - #region Handle transport messages - - private void HandleMessage(DisconnectMessage message) - { - this.OnDisconnectReceived(message); - - // Shutdown and disconnect from the socket - if (this._socket != null) - { - lock (this._socketLock) - { - if (this._socket != null) - { - this.SocketDisconnect(); - this._socket.Dispose(); - this._socket = null; - } - } - } - } - - private void HandleMessage(IgnoreMessage message) - { - this.OnIgnoreReceived(message); - } - - private void HandleMessage(UnimplementedMessage message) - { - this.OnUnimplementedReceived(message); - } - - private void HandleMessage(DebugMessage message) - { - this.OnDebugReceived(message); - } - - private void HandleMessage(ServiceRequestMessage message) - { - this.OnServiceRequestReceived(message); - } - - private void HandleMessage(ServiceAcceptMessage message) - { - // TODO: Refactor to avoid this method here - this.OnServiceAcceptReceived(message); - - this._serviceAccepted.Set(); - } - - private void HandleMessage(KeyExchangeInitMessage message) - { - this.OnKeyExchangeInitReceived(message); - } - - private void HandleMessage(NewKeysMessage message) - { - this.OnNewKeysReceived(message); - } - - #endregion - - #region Handle User Authentication messages - - private void HandleMessage(RequestMessage message) - { - this.OnUserAuthenticationRequestReceived(message); - } - - private void HandleMessage(FailureMessage message) - { - this.OnUserAuthenticationFailureReceived(message); - } - - private void HandleMessage(SuccessMessage message) - { - this.OnUserAuthenticationSuccessReceived(message); - } - - private void HandleMessage(BannerMessage message) - { - this.OnUserAuthenticationBannerReceived(message); - } - - #endregion - - #region Handle connection messages - - private void HandleMessage(GlobalRequestMessage message) - { - this.OnGlobalRequestReceived(message); - } - - private void HandleMessage(RequestSuccessMessage message) - { - this.OnRequestSuccessReceived(message); - } - - private void HandleMessage(RequestFailureMessage message) - { - this.OnRequestFailureReceived(message); - } - - private void HandleMessage(ChannelOpenMessage message) - { - this.OnChannelOpenReceived(message); - } - - private void HandleMessage(ChannelOpenConfirmationMessage message) - { - this.OnChannelOpenConfirmationReceived(message); - } - - private void HandleMessage(ChannelOpenFailureMessage message) - { - this.OnChannelOpenFailureReceived(message); - } - - private void HandleMessage(ChannelWindowAdjustMessage message) - { - this.OnChannelWindowAdjustReceived(message); - } - - private void HandleMessage(ChannelDataMessage message) - { - this.OnChannelDataReceived(message); - } - - private void HandleMessage(ChannelExtendedDataMessage message) - { - this.OnChannelExtendedDataReceived(message); - } - - private void HandleMessage(ChannelEofMessage message) - { - this.OnChannelEofReceived(message); - } - - private void HandleMessage(ChannelCloseMessage message) - { - this.OnChannelCloseReceived(message); - } - - private void HandleMessage(ChannelRequestMessage message) - { - this.OnChannelRequestReceived(message); - } - - private void HandleMessage(ChannelSuccessMessage message) - { - this.OnChannelSuccessReceived(message); - } - - private void HandleMessage(ChannelFailureMessage message) - { - this.OnChannelFailureReceived(message); - } - - #endregion - - #region Handle received message events - - /// - /// Called when received. - /// - /// message. - protected virtual void OnDisconnectReceived(DisconnectMessage message) - { - this.Log(string.Format("Disconnect received: {0} {1}", message.ReasonCode, message.Description)); - - if (this.DisconnectReceived != null) - { - this.DisconnectReceived(this, new MessageEventArgs(message)); - } - - if (this.Disconnected != null) - { - this.Disconnected(this, new EventArgs()); - } - } - - /// - /// Called when received. - /// - /// message. - protected virtual void OnIgnoreReceived(IgnoreMessage message) - { - if (this.IgnoreReceived != null) - { - this.IgnoreReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUnimplementedReceived(UnimplementedMessage message) - { - if (this.UnimplementedReceived != null) - { - this.UnimplementedReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnDebugReceived(DebugMessage message) - { - if (this.DebugReceived != null) - { - this.DebugReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnServiceRequestReceived(ServiceRequestMessage message) - { - if (this.ServiceRequestReceived != null) - { - this.ServiceRequestReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnServiceAcceptReceived(ServiceAcceptMessage message) - { - if (this.ServiceAcceptReceived != null) - { - this.ServiceAcceptReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnKeyExchangeInitReceived(KeyExchangeInitMessage message) - { - this._keyExchangeInProgress = true; - - this._keyExchangeCompletedWaitHandle.Reset(); - - // Disable all registered messages except key exchange related - foreach (var messageMetadata in this._messagesMetadata) - { - if (messageMetadata.Activated == true && messageMetadata.Number > 2 && (messageMetadata.Number < 20 || messageMetadata.Number > 30)) - messageMetadata.Enabled = false; - } - - var keyExchangeAlgorithmName = (from c in this.ConnectionInfo.KeyExchangeAlgorithms.Keys - from s in message.KeyExchangeAlgorithms - where s == c - select c).FirstOrDefault(); - - if (keyExchangeAlgorithmName == null) - { - throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed); - } - - // Create instance of key exchange algorithm that will be used - this._keyExchange = this.ConnectionInfo.KeyExchangeAlgorithms[keyExchangeAlgorithmName].CreateInstance(); - - this.ConnectionInfo.CurrentKeyExchangeAlgorithm = keyExchangeAlgorithmName; - - this._keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived; - - // Start the algorithm implementation - this._keyExchange.Start(this, message); - - if (this.KeyExchangeInitReceived != null) - { - this.KeyExchangeInitReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnNewKeysReceived(NewKeysMessage message) - { - // Update sessionId - if (this.SessionId == null) - { - this.SessionId = this._keyExchange.ExchangeHash; - } - - // Dispose of old ciphers and hash algorithms - if (this._serverMac != null) - { - this._serverMac.Clear(); - this._serverMac = null; - } - - if (this._clientMac != null) - { - this._clientMac.Clear(); - this._clientMac = null; - } - - // Update negotiated algorithms - this._serverCipher = this._keyExchange.CreateServerCipher(); - this._clientCipher = this._keyExchange.CreateClientCipher(); - this._serverMac = this._keyExchange.CreateServerHash(); - this._clientMac = this._keyExchange.CreateClientHash(); - this._clientCompression = this._keyExchange.CreateCompressor(); - this._serverDecompression = this._keyExchange.CreateDecompressor(); - - // Dispose of old KeyExchange object as it is no longer needed. - if (this._keyExchange != null) - { - this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; - this._keyExchange.Dispose(); - this._keyExchange = null; - } - - // Enable all active registered messages - foreach (var messageMetadata in this._messagesMetadata) - { - if (messageMetadata.Activated == true) - messageMetadata.Enabled = true; - } - - if (this.NewKeysReceived != null) - { - this.NewKeysReceived(this, new MessageEventArgs(message)); - } - - // Signal that key exchange completed - this._keyExchangeCompletedWaitHandle.Set(); - - this._keyExchangeInProgress = false; - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationRequestReceived(RequestMessage message) - { - if (this.UserAuthenticationRequestReceived != null) - { - this.UserAuthenticationRequestReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationFailureReceived(FailureMessage message) - { - if (this.UserAuthenticationFailureReceived != null) - { - this.UserAuthenticationFailureReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationSuccessReceived(SuccessMessage message) - { - if (this.UserAuthenticationSuccessReceived != null) - { - this.UserAuthenticationSuccessReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationBannerReceived(BannerMessage message) - { - if (this.UserAuthenticationBannerReceived != null) - { - this.UserAuthenticationBannerReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnGlobalRequestReceived(GlobalRequestMessage message) - { - if (this.GlobalRequestReceived != null) - { - this.GlobalRequestReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnRequestSuccessReceived(RequestSuccessMessage message) - { - if (this.RequestSuccessReceived != null) - { - this.RequestSuccessReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnRequestFailureReceived(RequestFailureMessage message) - { - if (this.RequestFailureReceived != null) - { - this.RequestFailureReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelOpenReceived(ChannelOpenMessage message) - { - if (this.ChannelOpenReceived != null) - { - this.ChannelOpenReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message) - { - if (this.ChannelOpenConfirmationReceived != null) - { - this.ChannelOpenConfirmationReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message) - { - if (this.ChannelOpenFailureReceived != null) - { - this.ChannelOpenFailureReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message) - { - if (this.ChannelWindowAdjustReceived != null) - { - this.ChannelWindowAdjustReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelDataReceived(ChannelDataMessage message) - { - if (this.ChannelDataReceived != null) - { - this.ChannelDataReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message) - { - if (this.ChannelExtendedDataReceived != null) - { - this.ChannelExtendedDataReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelEofReceived(ChannelEofMessage message) - { - if (this.ChannelEofReceived != null) - { - this.ChannelEofReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelCloseReceived(ChannelCloseMessage message) - { - if (this.ChannelCloseReceived != null) - { - this.ChannelCloseReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelRequestReceived(ChannelRequestMessage message) - { - if (this.ChannelRequestReceived != null) - { - this.ChannelRequestReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelSuccessReceived(ChannelSuccessMessage message) - { - if (this.ChannelSuccessReceived != null) - { - this.ChannelSuccessReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelFailureReceived(ChannelFailureMessage message) - { - if (this.ChannelFailureReceived != null) - { - this.ChannelFailureReceived(this, new MessageEventArgs(message)); - } - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnMessageReceived(Message message) - { - if (this.MessageReceived != null) - { - this.MessageReceived(this, new MessageEventArgs(message)); - } - } - - #endregion - - private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e) - { - var handler = this.HostKeyReceived; - if (handler != null) - { - handler(this, e); - } - } - - /// - /// Reads the specified length of bytes from the server - /// - /// The length. - /// - private byte[] Read(int length) - { - byte[] buffer = new byte[length]; - - this.SocketRead(length, ref buffer); - - return buffer; - } - - #region Message loading functions - - /// - /// Registers SSH Message with the session. - /// - /// Name of the message. - public void RegisterMessage(string messageName) - { - this.InternalRegisterMessage(messageName); - } - - /// - /// Removes SSH message from the session - /// - /// Name of the message. - public void UnRegisterMessage(string messageName) - { - this.InternalUnRegisterMessage(messageName); - } - - /// - /// Loads the message. - /// - /// Message data. - /// New message - private Message LoadMessage(byte[] data) - { - var messageType = data[0]; - var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled && m.Activated select m).SingleOrDefault(); - - if (messageMetadata == null) - throw new SshException(string.Format(CultureInfo.CurrentCulture, "Message type {0} is not valid.", messageType)); - - var message = messageMetadata.Type.CreateInstance(); - - message.Load(data); - - this.Log(string.Format("ReceiveMessage from server: '{0}': '{1}'.", message.GetType().Name, message.ToString())); - - return message; - } - - partial void InternalRegisterMessage(string messageName); - - partial void InternalUnRegisterMessage(string messageName); - - #endregion - - partial void ExecuteThread(Action action); - - partial void IsSocketConnected(ref bool isConnected); - - partial void SocketConnect(string host, int port); - - partial void SocketDisconnect(); - - partial void SocketRead(int length, ref byte[] buffer); - - partial void SocketReadLine(ref string response); - - partial void Log(string text); - - /// - /// Writes the specified data to the server. - /// - /// The data. - partial void SocketWrite(byte[] data); - - /// - /// Listens for incoming message from the server and handles them. This method run as a task on separate thread. - /// - private void MessageListener() - { - try - { - while (this._socket != null && this._socket.Connected) - { - var message = this.ReceiveMessage(); - - if (message == null) - { - throw new NullReferenceException("The 'message' variable cannot be null"); - } - - this.HandleMessageCore(message); - } - } - catch (Exception exp) - { - this.RaiseError(exp); - } - } - - private byte SocketReadByte() - { - byte[] buffer = new byte[1]; - - this.SocketRead(1, ref buffer); - - return buffer[0]; - } - - private void SocketWriteByte(byte data) - { - this.SocketWrite(new byte[] { data }); - } - - private void ConnectSocks4() - { - // Send socks version number - this.SocketWriteByte(0x04); - - // Send command code - this.SocketWriteByte(0x01); - - // Send port - this.SocketWriteByte((byte)(this.ConnectionInfo.Port / 0xFF)); - this.SocketWriteByte((byte)(this.ConnectionInfo.Port % 0xFF)); - - // Send IP - IPAddress ipAddress = this.ConnectionInfo.Host.GetIPAddress(); - this.SocketWrite(ipAddress.GetAddressBytes()); - - // Send username - var username = new Renci.SshNet.Common.ASCIIEncoding().GetBytes(this.ConnectionInfo.ProxyUsername); - this.SocketWrite(username); - this.SocketWriteByte(0x00); - - // Read 0 - if (this.SocketReadByte() != 0) - { - throw new ProxyException("SOCKS4: Null is expected."); - } - - // Read response code - var code = this.SocketReadByte(); - - switch (code) - { - case 0x5a: - break; - case 0x5b: - throw new ProxyException("SOCKS4: Connection rejected."); - case 0x5c: - throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server."); - case 0x5d: - throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request."); - default: - throw new ProxyException("SOCKS4: Not valid response."); - } - - byte[] dummyBuffer = new byte[4]; - - // Read 2 bytes to be ignored - this.SocketRead(2, ref dummyBuffer); - - // Read 4 bytes to be ignored - this.SocketRead(4, ref dummyBuffer); - } - - private void ConnectSocks5() - { - // Send socks version number - this.SocketWriteByte(0x05); - - // Send number of supported authentication methods - this.SocketWriteByte(0x02); - - // Send supported authentication methods - this.SocketWriteByte(0x00); // No authentication - this.SocketWriteByte(0x02); // Username/Password - - var socksVersion = this.SocketReadByte(); - if (socksVersion != 0x05) - throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion)); - - var authenticationMethod = this.SocketReadByte(); - switch (authenticationMethod) - { - case 0x00: - break; - case 0x02: - - // Send version - this.SocketWriteByte(0x01); - - var encoding = new Renci.SshNet.Common.ASCIIEncoding(); - - var username = encoding.GetBytes(this.ConnectionInfo.ProxyUsername); - - if (username.Length > byte.MaxValue) - throw new ProxyException("Proxy username is too long."); - - // Send username length - this.SocketWriteByte((byte)username.Length); - - // Send username - this.SocketWrite(username); - - var password = encoding.GetBytes(this.ConnectionInfo.ProxyPassword); - - if (password.Length > byte.MaxValue) - throw new ProxyException("Proxy password is too long."); - - // Send username length - this.SocketWriteByte((byte)password.Length); - - // Send username - this.SocketWrite(password); - - var serverVersion = this.SocketReadByte(); - - if (serverVersion != 1) - throw new ProxyException("SOCKS5: Server authentication version is not valid."); - - var statusCode = this.SocketReadByte(); - if (statusCode != 0) - throw new ProxyException("SOCKS5: Username/Password authentication failed."); - - break; - case 0xFF: - throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); - default: - break; - } - - // Send socks version number - this.SocketWriteByte(0x05); - - // Send command code - this.SocketWriteByte(0x01); // establish a TCP/IP stream connection - - // Send reserved, must be 0x00 - this.SocketWriteByte(0x00); - - IPAddress ip = this.ConnectionInfo.Host.GetIPAddress(); - - // Send address type and address - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - this.SocketWriteByte(0x01); - var address = ip.GetAddressBytes(); - this.SocketWrite(address); - } - else if (ip.AddressFamily == AddressFamily.InterNetworkV6) - { - this.SocketWriteByte(0x04); - var address = ip.GetAddressBytes(); - this.SocketWrite(address); - } - else - { - throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", ip)); - } - - // Send port - this.SocketWriteByte((byte)(this.ConnectionInfo.Port / 0xFF)); - this.SocketWriteByte((byte)(this.ConnectionInfo.Port % 0xFF)); - - // Read Server SOCKS5 version - if (this.SocketReadByte() != 5) - { - throw new ProxyException("SOCKS5: Version 5 is expected."); - } - - // Read response code - var status = this.SocketReadByte(); - - switch (status) - { - case 0x00: - break; - case 0x01: - throw new ProxyException("SOCKS5: General failure."); - case 0x02: - throw new ProxyException("SOCKS5: Connection not allowed by ruleset."); - case 0x03: - throw new ProxyException("SOCKS5: Network unreachable."); - case 0x04: - throw new ProxyException("SOCKS5: Host unreachable."); - case 0x05: - throw new ProxyException("SOCKS5: Connection refused by destination host."); - case 0x06: - throw new ProxyException("SOCKS5: TTL expired."); - case 0x07: - throw new ProxyException("SOCKS5: Command not supported or protocol error."); - case 0x08: - throw new ProxyException("SOCKS5: Address type not supported."); - default: - throw new ProxyException("SOCKS4: Not valid response."); - } - - // Read 0 - if (this.SocketReadByte() != 0) - { - throw new ProxyException("SOCKS5: 0 byte is expected."); - } - - var addressType = this.SocketReadByte(); - byte[] responseIp = new byte[16]; - - switch (addressType) - { - case 0x01: - this.SocketRead(4, ref responseIp); - break; - case 0x04: - this.SocketRead(16, ref responseIp); - break; - default: - throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType)); - } - - byte[] port = new byte[2]; - - // Read 2 bytes to be ignored - this.SocketRead(2, ref port); - - } - - private void ConnectHttp() - { - var httpResponseRe = new Regex(@"HTTP/(?\d[.]\d) (?\d{3}) (?.+)$"); - var httpHeaderRe = new Regex(@"(?[^\[\]()<>@,;:\""/?={} \t]+):(?.+)?"); - - var encoding = new Renci.SshNet.Common.ASCIIEncoding(); - - this.SocketWrite(encoding.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.0\r\n", this.ConnectionInfo.Host, this.ConnectionInfo.Port))); - - // Sent proxy authorization is specified - if (!string.IsNullOrEmpty(this.ConnectionInfo.ProxyUsername)) - { - var authorization = string.Format("Proxy-Authorization: Basic {0}\r\n", - Convert.ToBase64String(encoding.GetBytes(string.Format("{0}:{1}", this.ConnectionInfo.ProxyUsername, this.ConnectionInfo.ProxyPassword))) - ); - this.SocketWrite(encoding.GetBytes(authorization)); - } - - this.SocketWrite(encoding.GetBytes("\r\n")); - - HttpStatusCode statusCode = HttpStatusCode.OK; - var response = string.Empty; - var contentLength = 0; - - while (true) - { - this.SocketReadLine(ref response); - - var match = httpResponseRe.Match(response); - - if (match.Success) - { - statusCode = (HttpStatusCode)int.Parse(match.Result("${statusCode}")); - continue; - } - - // continue on parsing message headers coming from the server - match = httpHeaderRe.Match(response); - if (match.Success) - { - var fieldName = match.Result("${fieldName}"); - if (fieldName.Equals("Content-Length", StringComparison.InvariantCultureIgnoreCase)) - { - contentLength = int.Parse(match.Result("${fieldValue}")); - } - continue; - } - - // Read response body if specified - if (string.IsNullOrEmpty(response) && contentLength > 0) - { - var contentBody = new byte[contentLength]; - this.SocketRead(contentLength, ref contentBody); - } - - switch (statusCode) - { - case HttpStatusCode.OK: - break; - default: - throw new ProxyException(string.Format("HTTP: Status code {0}, \"{1}\"", statusCode, statusCode)); - } - } - } - - /// - /// Raises the event. - /// - /// The exp. - private void RaiseError(Exception exp) - { - var connectionException = exp as SshConnectionException; - - // If connection exception was raised while isDisconnecting is true then this is expected - // case and should be ignore - if (connectionException != null && this._isDisconnecting) - return; - - this._exception = exp; - - this._exceptionWaitHandle.Set(); - - if (this.ErrorOccured != null) - { - this.ErrorOccured(this, new ExceptionEventArgs(exp)); - } - - if (connectionException != null && connectionException.DisconnectReason != DisconnectReason.ConnectionLost) - { - this.SendDisconnect(connectionException.DisconnectReason, exp.ToString()); - } - } - - #region IDisposable Members - - private bool _disposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._disposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - - if (this._socket != null) - { - this._socket.Dispose(); - this._socket = null; - } - - if (this._messageListenerCompleted != null) - { - // Wait for socket to be closed and for task to complete before disposing a task - this._messageListenerCompleted.WaitOne(); - this._messageListenerCompleted.Dispose(); - this._messageListenerCompleted = null; - } - - if (this._serviceAccepted != null) - { - this._serviceAccepted.Dispose(); - this._serviceAccepted = null; - } - - if (this._exceptionWaitHandle != null) - { - this._exceptionWaitHandle.Dispose(); - this._exceptionWaitHandle = null; - } - - if (this._keyExchangeCompletedWaitHandle != null) - { - this._keyExchangeCompletedWaitHandle.Dispose(); - this._keyExchangeCompletedWaitHandle = null; - } - - if (this._serverMac != null) - { - this._serverMac.Clear(); - this._serverMac = null; - } - - if (this._clientMac != null) - { - this._clientMac.Clear(); - this._clientMac = null; - } - - if (this._keyExchange != null) - { - this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; - this._keyExchange.Dispose(); - this._keyExchange = null; - } - } - - // Note disposing has been done. - this._disposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~Session() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - private class MessageMetadata - { - public string Name { get; set; } - - public byte Number { get; set; } - - public bool Enabled { get; set; } - - public bool Activated { get; set; } - - public Type Type { get; set; } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using Renci.SshNet.Compression; +using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Messages.Connection; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Security; +using System.Globalization; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet +{ + /// + /// Provides functionality to connect and interact with SSH server. + /// + public partial class Session : IDisposable + { + /// + /// Specifies maximum packet size defined by the protocol. + /// + private const int MaximumSshPacketSize = LocalChannelDataPacketSize + 3000; + + /// + /// Holds the initial local window size for the channels. + /// + /// + /// 2 MB. + /// + private const int InitialLocalWindowSize = LocalChannelDataPacketSize * 32; + + /// + /// Holds the maximum size of channel data packets that we receive. + /// + /// + /// 64 KB. + /// + private const int LocalChannelDataPacketSize = 1024*64; + + private static readonly RNGCryptoServiceProvider Randomizer = new RNGCryptoServiceProvider(); + +#if SILVERLIGHT + private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+)( SP.+)?$"); +#else + private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+)( SP.+)?$", RegexOptions.Compiled); +#endif + + /// + /// Controls how many authentication attempts can take place at the same time. + /// + /// + /// Some server may restrict number to prevent authentication attacks + /// + private static readonly SemaphoreLight AuthenticationConnection = new SemaphoreLight(3); + + /// + /// Holds metada about session messages + /// + private IEnumerable _messagesMetadata; + + /// + /// Holds connection socket. + /// + private Socket _socket; + + /// + /// Holds locker object for the socket + /// + private readonly object _socketLock = new object(); + + /// + /// Holds reference to task that listens for incoming messages + /// + private EventWaitHandle _messageListenerCompleted; + + /// + /// Specifies outbound packet number + /// + private volatile UInt32 _outboundPacketSequence; + + /// + /// Specifies incoming packet number + /// + private UInt32 _inboundPacketSequence; + + /// + /// WaitHandle to signal that last service request was accepted + /// + private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); + + /// + /// WaitHandle to signal that exception was thrown by another thread. + /// + private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(false); + + /// + /// WaitHandle to signal that key exchange was completed. + /// + private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent(false); + + /// + /// WaitHandle to signal that key exchange is in progress. + /// + private bool _keyExchangeInProgress; + + /// + /// Exception that need to be thrown by waiting thread + /// + private Exception _exception; + + /// + /// Specifies whether connection is authenticated + /// + private bool _isAuthenticated; + + /// + /// Specifies whether user issued Disconnect command or not + /// + private bool _isDisconnecting; + + private KeyExchange _keyExchange; + + private HashAlgorithm _serverMac; + + private HashAlgorithm _clientMac; + + private Cipher _clientCipher; + + private Cipher _serverCipher; + + private Compressor _serverDecompression; + + private Compressor _clientCompression; + + private SemaphoreLight _sessionSemaphore; + + /// + /// Gets the session semaphore that controls session channels. + /// + /// The session semaphore. + public SemaphoreLight SessionSemaphore + { + get + { + if (this._sessionSemaphore == null) + { + lock (this) + { + if (this._sessionSemaphore == null) + { + this._sessionSemaphore = new SemaphoreLight(this.ConnectionInfo.MaxSessions); + } + } + } + + return this._sessionSemaphore; + } + } + + private bool _isDisconnectMessageSent; + + private uint _nextChannelNumber; + /// + /// Gets the next channel number. + /// + /// The next channel number. + internal uint NextChannelNumber + { + get + { + uint result; + + lock (this) + { + result = this._nextChannelNumber++; + } + + return result; + } + } + + /// + /// Gets a value indicating whether the session is connected. + /// + /// + /// true if the session is connected; otherwise, false. + /// + /// + /// This methods returns true in all but the following cases: + /// + /// + /// The SSH_MSG_DISCONNECT message - which is used to disconnect from the server - has been sent. + /// + /// + /// The client has not been authenticated successfully. + /// + /// + /// The listener thread - which is used to receive messages from the server - has stopped. + /// + /// + /// The socket used to communicate with the server is no longer connected. + /// + /// + /// + public bool IsConnected + { + get + { + if (_isDisconnectMessageSent || !this._isAuthenticated) + return false; + if (_messageListenerCompleted == null || _messageListenerCompleted.WaitOne(0)) + return false; + + var isSocketConnected = false; + IsSocketConnected(ref isSocketConnected); + return isSocketConnected; + } + } + + /// + /// Gets the session id. + /// + /// + /// The session id, or null if the client has not been authenticated. + /// + public byte[] SessionId { get; private set; } + + private Message _clientInitMessage; + /// + /// Gets the client init message. + /// + /// The client init message. + public Message ClientInitMessage + { + get + { + if (this._clientInitMessage == null) + { + this._clientInitMessage = new KeyExchangeInitMessage + { + KeyExchangeAlgorithms = this.ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), + ServerHostKeyAlgorithms = this.ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), + EncryptionAlgorithmsClientToServer = this.ConnectionInfo.Encryptions.Keys.ToArray(), + EncryptionAlgorithmsServerToClient = this.ConnectionInfo.Encryptions.Keys.ToArray(), + MacAlgorithmsClientToServer = this.ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + MacAlgorithmsServerToClient = this.ConnectionInfo.HmacAlgorithms.Keys.ToArray(), + CompressionAlgorithmsClientToServer = this.ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + CompressionAlgorithmsServerToClient = this.ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), + LanguagesClientToServer = new[] {string.Empty}, + LanguagesServerToClient = new[] {string.Empty}, + FirstKexPacketFollows = false, + Reserved = 0 + }; + } + return this._clientInitMessage; + } + } + + /// + /// Gets or sets the server version string. + /// + /// The server version. + public string ServerVersion { get; private set; } + + /// + /// Gets or sets the client version string. + /// + /// The client version. + public string ClientVersion { get; private set; } + + /// + /// Gets or sets the connection info. + /// + /// The connection info. + public ConnectionInfo ConnectionInfo { get; private set; } + + /// + /// Occurs when an error occurred. + /// + public event EventHandler ErrorOccured; + + /// + /// Occurs when session has been disconnected form the server. + /// + public event EventHandler Disconnected; + + /// + /// Occurs when host key received. + /// + public event EventHandler HostKeyReceived; + + #region Message events + + /// + /// Occurs when message received + /// + internal event EventHandler> DisconnectReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> IgnoreReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> UnimplementedReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> DebugReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ServiceRequestReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ServiceAcceptReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> KeyExchangeInitReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> NewKeysReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> UserAuthenticationRequestReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> UserAuthenticationFailureReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> UserAuthenticationSuccessReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> UserAuthenticationBannerReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> GlobalRequestReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> RequestSuccessReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> RequestFailureReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelOpenReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelOpenConfirmationReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelOpenFailureReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelWindowAdjustReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelDataReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelExtendedDataReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelEofReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelCloseReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelRequestReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelSuccessReceived; + + /// + /// Occurs when message received + /// + internal event EventHandler> ChannelFailureReceived; + + /// + /// Occurs when message received and is not handled by any of the event handlers + /// + internal event EventHandler> MessageReceived; + + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// is null. + internal Session(ConnectionInfo connectionInfo) + { + if (connectionInfo == null) + throw new ArgumentNullException("connectionInfo"); + + this.ConnectionInfo = connectionInfo; + //this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.{0}", this.GetType().Assembly.GetName().Version); + this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.0.0.1"); + } + + /// + /// Connects to the server. + /// + public void Connect() + { + if (this.IsConnected) + return; + + try + { + AuthenticationConnection.Wait(); + + if (this.IsConnected) + return; + + lock (this) + { + // If connected don't connect again + if (this.IsConnected) + return; + + // reset connection specific information + Reset(); + + // Build list of available messages while connecting + this._messagesMetadata = GetMessagesMetadata(); + + switch (this.ConnectionInfo.ProxyType) + { + case ProxyTypes.None: + this.SocketConnect(this.ConnectionInfo.Host, this.ConnectionInfo.Port); + break; + case ProxyTypes.Socks4: + this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); + this.ConnectSocks4(); + break; + case ProxyTypes.Socks5: + this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); + this.ConnectSocks5(); + break; + case ProxyTypes.Http: + this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); + this.ConnectHttp(); + break; + } + + Match versionMatch; + + // Get server version from the server, + // ignore text lines which are sent before if any + while (true) + { + string serverVersion = string.Empty; + this.SocketReadLine(ref serverVersion); + + this.ServerVersion = serverVersion; + if (string.IsNullOrEmpty(this.ServerVersion)) + { + throw new InvalidOperationException("Server string is null or empty."); + } + + versionMatch = ServerVersionRe.Match(this.ServerVersion); + + if (versionMatch.Success) + { + break; + } + } + + // Set connection versions + this.ConnectionInfo.ServerVersion = this.ServerVersion; + this.ConnectionInfo.ClientVersion = this.ClientVersion; + + // Get server SSH version + var version = versionMatch.Result("${protoversion}"); + + var softwareName = versionMatch.Result("${softwareversion}"); + + this.Log(string.Format("Server version '{0}' on '{1}'.", version, softwareName)); + + if (!(version.Equals("2.0") || version.Equals("1.99"))) + { + throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", version), DisconnectReason.ProtocolVersionNotSupported); + } + + this.SocketWrite(Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\x0D\x0A", this.ClientVersion))); + + // Register Transport response messages + this.RegisterMessage("SSH_MSG_DISCONNECT"); + this.RegisterMessage("SSH_MSG_IGNORE"); + this.RegisterMessage("SSH_MSG_UNIMPLEMENTED"); + this.RegisterMessage("SSH_MSG_DEBUG"); + this.RegisterMessage("SSH_MSG_SERVICE_ACCEPT"); + this.RegisterMessage("SSH_MSG_KEXINIT"); + this.RegisterMessage("SSH_MSG_NEWKEYS"); + + // Some server implementations might sent this message first, prior establishing encryption algorithm + this.RegisterMessage("SSH_MSG_USERAUTH_BANNER"); + + // Start incoming request listener + this._messageListenerCompleted = new ManualResetEvent(false); + + this.ExecuteThread(() => + { + try + { + this.MessageListener(); + } + finally + { + this._messageListenerCompleted.Set(); + } + }); + + // Wait for key exchange to be completed + this.WaitOnHandle(this._keyExchangeCompletedWaitHandle); + + // If sessionId is not set then its not connected + if (this.SessionId == null) + { + this.Disconnect(); + return; + } + + // Request user authorization service + this.SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication)); + + // Wait for service to be accepted + this.WaitOnHandle(this._serviceAccepted); + + if (string.IsNullOrEmpty(this.ConnectionInfo.Username)) + { + throw new SshException("Username is not specified."); + } + + this.ConnectionInfo.Authenticate(this); + this._isAuthenticated = true; + + // Register Connection messages + this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST"); + this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); + this.RegisterMessage("SSH_MSG_REQUEST_FAILURE"); + this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); + this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); + this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); + this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); + this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST"); + this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); + this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE"); + this.RegisterMessage("SSH_MSG_CHANNEL_DATA"); + this.RegisterMessage("SSH_MSG_CHANNEL_EOF"); + this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE"); + + Monitor.Pulse(this); + } + } + finally + { + AuthenticationConnection.Release(); + } + } + + /// + /// Disconnects from the server. + /// + /// + /// This sends a SSH_MSG_DISCONNECT message to the server, waits for the + /// server to close the socket on its end and subsequently closes the client socket. + /// + public void Disconnect() + { + Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client."); + } + + private void Disconnect(DisconnectReason reason, string message) + { + this._isDisconnecting = true; + + // send disconnect message to the server if the connection is still open + // and the disconnect message has not yet been sent + // + // note that this should also cause the listener thread to be stopped as + // the server should respond by closing the socket + SendDisconnect(reason, message); + + // disconnect socket, and dispose it + SocketDisconnectAndDispose(); + + if (_messageListenerCompleted != null) + { + // at this point, we are sure that the listener thread will stop + // as we've disconnected the socket + _messageListenerCompleted.WaitOne(); + _messageListenerCompleted.Dispose(); + _messageListenerCompleted = null; + } + } + + internal T CreateClientChannel() where T : ClientChannel, new() + { + var channel = new T(); + lock (this) + { + channel.Initialize(this, InitialLocalWindowSize, LocalChannelDataPacketSize); + } + return channel; + } + + internal T CreateServerChannel(uint remoteChannelNumber, uint remoteWindowSize, uint remoteChannelDataPacketSize) where T : ServerChannel, new() + { + var channel = new T(); + lock (this) + { + channel.Initialize(this, InitialLocalWindowSize, LocalChannelDataPacketSize, remoteChannelNumber, remoteWindowSize, + remoteChannelDataPacketSize); + } + return channel; + } + + /// + /// Sends "keep alive" message to keep connection alive. + /// + internal void SendKeepAlive() + { + this.SendMessage(new IgnoreMessage()); + } + + /// + /// Waits for the specified handle or the exception handle for the receive thread + /// to signal within the connection timeout. + /// + /// The wait handle. + /// A received package was invalid or failed the message integrity check. + /// None of the handles are signaled in time and the session is not disconnecting. + /// A socket error was signaled while receiving messages from the server. + /// + /// When neither handles are signaled in time and the session is not closing, then the + /// session is disconnected. + /// + /// + internal void WaitOnHandle(WaitHandle waitHandle) + { + var waitHandles = new[] + { + this._exceptionWaitHandle, + this._messageListenerCompleted, + waitHandle + }; + + switch (WaitHandle.WaitAny(waitHandles, ConnectionInfo.Timeout)) + { + case 0: + throw this._exception; + case 1: + // when the session is NOT disconnecting, the listener should actually + // never complete without setting the exception wait handle and should + // end up in case 0... + // + // when the session is disconnecting, the completion of the listener + // should not be considered an error (quite the oppposite actually) + if (!_isDisconnecting) + { + throw new SshConnectionException("Client not connected."); + } + break; + case WaitHandle.WaitTimeout: + // when the session is NOT disconnecting, then we want to disconnect + // the session altogether in case of a timeout, and throw a + // SshOperationTimeoutException + // + // when the session is disconnecting, a timeout is likely when no + // network connectivity is available; depending on the configured + // timeout either the WaitAny times out first or a SocketException + // detailing a timeout thrown hereby completing the listener thread + // (which makes us end up in case 1). Either way, we do not want to + // disconnect while we're already disconnecting and we do not want + // to report an exception to the client when we're disconnecting + // anyway + if (!_isDisconnecting) + { + this.Disconnect(DisconnectReason.ByApplication, "Operation timeout"); + throw new SshOperationTimeoutException("Session operation has timed out"); + } + break; + } + } + + /// + /// Sends packet message to the server. + /// + /// The message. + internal void SendMessage(Message message) + { + if (this._socket == null || !this._socket.CanWrite()) + throw new SshConnectionException("Client not connected."); + + if (this._keyExchangeInProgress && !(message is IKeyExchangedAllowed)) + { + // Wait for key exchange to be completed + this.WaitOnHandle(this._keyExchangeCompletedWaitHandle); + } + + this.Log(string.Format("SendMessage to server '{0}': '{1}'.", message.GetType().Name, message)); + + // Messages can be sent by different thread so we need to synchronize it + var paddingMultiplier = this._clientCipher == null ? (byte)8 : Math.Max((byte)8, this._serverCipher.MinimumSize); // Should be recalculate base on cipher min length if cipher specified + + var messageData = message.GetBytes(); + + if (this._clientCompression != null) + { + messageData = this._clientCompression.Compress(messageData); + } + + var packetLength = messageData.Length + 4 + 1; // add length bytes and padding byte + var paddingLength = (byte)((-packetLength) & (paddingMultiplier - 1)); + if (paddingLength < paddingMultiplier) + { + paddingLength += paddingMultiplier; + } + + // Build Packet data + var packetData = new byte[4 + 1 + messageData.Length + paddingLength]; + + // Add packet length + ((uint)packetData.Length - 4).GetBytes().CopyTo(packetData, 0); + + // Add packet padding length + packetData[4] = paddingLength; + + // Add packet payload + messageData.CopyTo(packetData, 4 + 1); + + // Add random padding + var paddingBytes = new byte[paddingLength]; + Randomizer.GetBytes(paddingBytes); + paddingBytes.CopyTo(packetData, 4 + 1 + messageData.Length); + + // Lock handling of _outboundPacketSequence since it must be sent sequently to server + lock (this._socketLock) + { + if (this._socket == null || !this._socket.Connected) + throw new SshConnectionException("Client not connected."); + + // Calculate packet hash + var hashData = new byte[4 + packetData.Length]; + this._outboundPacketSequence.GetBytes().CopyTo(hashData, 0); + packetData.CopyTo(hashData, 4); + + // Encrypt packet data + if (this._clientCipher != null) + { + packetData = this._clientCipher.Encrypt(packetData); + } + + if (packetData.Length > MaximumSshPacketSize) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Packet is too big. Maximum packet size is {0} bytes.", MaximumSshPacketSize)); + } + + if (this._clientMac == null) + { + this.SocketWrite(packetData); + } + else + { + var hash = this._clientMac.ComputeHash(hashData.ToArray()); + + var data = new byte[packetData.Length + this._clientMac.HashSize / 8]; + packetData.CopyTo(data, 0); + hash.CopyTo(data, packetData.Length); + + this.SocketWrite(data); + } + + this._outboundPacketSequence++; + + Monitor.Pulse(this._socketLock); + } + } + + private static IEnumerable GetMessagesMetadata() + { + return new [] + { + new MessageMetadata { Name = "SSH_MSG_NEWKEYS", Number = 21, Type = typeof(NewKeysMessage) }, + new MessageMetadata { Name = "SSH_MSG_REQUEST_FAILURE", Number = 82, Type = typeof(RequestFailureMessage) }, + new MessageMetadata { Name = "SSH_MSG_KEXINIT", Number = 20, Type = typeof(KeyExchangeInitMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN_FAILURE", Number = 92, Type = typeof(ChannelOpenFailureMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_FAILURE", Number = 100, Type = typeof(ChannelFailureMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_EXTENDED_DATA", Number = 95, Type = typeof(ChannelExtendedDataMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_DATA", Number = 94, Type = typeof(ChannelDataMessage) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_REQUEST", Number = 50, Type = typeof(RequestMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_REQUEST", Number = 98, Type = typeof(ChannelRequestMessage) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_BANNER", Number = 53, Type = typeof(BannerMessage) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_INFO_RESPONSE", Number = 61, Type = typeof(InformationResponseMessage) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_FAILURE", Number = 51, Type = typeof(FailureMessage) }, + new MessageMetadata { Name = "SSH_MSG_DEBUG", Number = 4, Type = typeof(DebugMessage), }, + new MessageMetadata { Name = "SSH_MSG_KEXDH_INIT", Number = 30, Type = typeof(KeyExchangeDhInitMessage) }, + new MessageMetadata { Name = "SSH_MSG_GLOBAL_REQUEST", Number = 80, Type = typeof(GlobalRequestMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN", Number = 90, Type = typeof(ChannelOpenMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", Number = 91, Type = typeof(ChannelOpenConfirmationMessage) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_INFO_REQUEST", Number = 60, Type = typeof(InformationRequestMessage) }, + new MessageMetadata { Name = "SSH_MSG_UNIMPLEMENTED", Number = 3, Type = typeof(UnimplementedMessage) }, + new MessageMetadata { Name = "SSH_MSG_REQUEST_SUCCESS", Number = 81, Type = typeof(RequestSuccessMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_SUCCESS", Number = 99, Type = typeof(ChannelSuccessMessage) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", Number = 60, Type = typeof(PasswordChangeRequiredMessage) }, + new MessageMetadata { Name = "SSH_MSG_DISCONNECT", Number = 1, Type = typeof(DisconnectMessage) }, + new MessageMetadata { Name = "SSH_MSG_SERVICE_REQUEST", Number = 5, Type = typeof(ServiceRequestMessage) }, + new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_REQUEST", Number = 34, Type = typeof(KeyExchangeDhGroupExchangeRequest) }, + new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_GROUP", Number = 31, Type = typeof(KeyExchangeDhGroupExchangeGroup) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_SUCCESS", Number = 52, Type = typeof(SuccessMessage) }, + new MessageMetadata { Name = "SSH_MSG_USERAUTH_PK_OK", Number = 60, Type = typeof(PublicKeyMessage) }, + new MessageMetadata { Name = "SSH_MSG_IGNORE", Number = 2, Type = typeof(IgnoreMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_WINDOW_ADJUST", Number = 93, Type = typeof(ChannelWindowAdjustMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_EOF", Number = 96, Type = typeof(ChannelEofMessage) }, + new MessageMetadata { Name = "SSH_MSG_CHANNEL_CLOSE", Number = 97, Type = typeof(ChannelCloseMessage) }, + new MessageMetadata { Name = "SSH_MSG_SERVICE_ACCEPT", Number = 6, Type = typeof(ServiceAcceptMessage) }, + new MessageMetadata { Name = "SSH_MSG_KEXDH_REPLY", Number = 31, Type = typeof(KeyExchangeDhReplyMessage) }, + new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_INIT", Number = 32, Type = typeof(KeyExchangeDhGroupExchangeInit) }, + new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_REPLY", Number = 33, Type = typeof(KeyExchangeDhGroupExchangeReply) } + }; + } + + /// + /// Receives the message from the server. + /// + /// Incoming SSH message. + /// + private Message ReceiveMessage() + { + // No lock needed since all messages read by only one thread + var blockSize = this._serverCipher == null ? (byte)8 : Math.Max((byte)8, this._serverCipher.MinimumSize); + + // Read packet length first + var firstBlock = this.Read(blockSize); + + if (this._serverCipher != null) + { + firstBlock = this._serverCipher.Decrypt(firstBlock); + } + + var packetLength = (uint)(firstBlock[0] << 24 | firstBlock[1] << 16 | firstBlock[2] << 8 | firstBlock[3]); + + // Test packet minimum and maximum boundaries + if (packetLength < Math.Max((byte)16, blockSize) - 4 || packetLength > MaximumSshPacketSize - 4) + throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length {0}", packetLength), DisconnectReason.ProtocolError); + + // Read rest of the packet data + var bytesToRead = (int)(packetLength - (blockSize - 4)); + + var data = new byte[bytesToRead + blockSize]; + + firstBlock.CopyTo(data, 0); + + byte[] serverHash = null; + if (this._serverMac != null) + { + serverHash = new byte[this._serverMac.HashSize / 8]; + bytesToRead += serverHash.Length; + } + + if (bytesToRead > 0) + { + var nextBlocks = this.Read(bytesToRead); + + if (serverHash != null) + { + Buffer.BlockCopy(nextBlocks, nextBlocks.Length - serverHash.Length, serverHash, 0, serverHash.Length); + nextBlocks = nextBlocks.Take(nextBlocks.Length - serverHash.Length).ToArray(); + } + + if (nextBlocks.Length > 0) + { + if (this._serverCipher != null) + { + nextBlocks = this._serverCipher.Decrypt(nextBlocks); + } + nextBlocks.CopyTo(data, blockSize); + } + } + + var paddingLength = data[4]; + + var messagePayload = new byte[packetLength - paddingLength - 1]; + Buffer.BlockCopy(data, 5, messagePayload, 0, messagePayload.Length); + + if (this._serverDecompression != null) + { + messagePayload = this._serverDecompression.Decompress(messagePayload); + } + + // Validate message against MAC + if (this._serverMac != null) + { + var clientHashData = new byte[4 + data.Length]; + var lengthBytes = this._inboundPacketSequence.GetBytes(); + + lengthBytes.CopyTo(clientHashData, 0); + data.CopyTo(clientHashData, 4); + + // Calculate packet hash + var clientHash = this._serverMac.ComputeHash(clientHashData); + + if (!serverHash.SequenceEqual(clientHash)) + { + throw new SshConnectionException("MAC error", DisconnectReason.MacError); + } + } + + this._inboundPacketSequence++; + + return this.LoadMessage(messagePayload); + } + + private void SendDisconnect(DisconnectReason reasonCode, string message) + { + // only send a disconnect message if it wasn't already sent, and we're + // still connected + if (this._isDisconnectMessageSent || !IsConnected) + return; + + var disconnectMessage = new DisconnectMessage(reasonCode, message); + + this.SendMessage(disconnectMessage); + + this._isDisconnectMessageSent = true; + } + + partial void HandleMessageCore(Message message); + + /// + /// Handles the message. + /// + /// + /// The message. + private void HandleMessage(T message) where T : Message + { + this.OnMessageReceived(message); + } + + #region Handle transport messages + + private void HandleMessage(DisconnectMessage message) + { + this.OnDisconnectReceived(message); + + // disconnect from the socket, and dispose it + SocketDisconnectAndDispose(); + } + + private void HandleMessage(IgnoreMessage message) + { + this.OnIgnoreReceived(message); + } + + private void HandleMessage(UnimplementedMessage message) + { + this.OnUnimplementedReceived(message); + } + + private void HandleMessage(DebugMessage message) + { + this.OnDebugReceived(message); + } + + private void HandleMessage(ServiceRequestMessage message) + { + this.OnServiceRequestReceived(message); + } + + private void HandleMessage(ServiceAcceptMessage message) + { + // TODO: Refactor to avoid this method here + this.OnServiceAcceptReceived(message); + + this._serviceAccepted.Set(); + } + + private void HandleMessage(KeyExchangeInitMessage message) + { + this.OnKeyExchangeInitReceived(message); + } + + private void HandleMessage(NewKeysMessage message) + { + this.OnNewKeysReceived(message); + } + + #endregion + + #region Handle User Authentication messages + + private void HandleMessage(RequestMessage message) + { + this.OnUserAuthenticationRequestReceived(message); + } + + private void HandleMessage(FailureMessage message) + { + this.OnUserAuthenticationFailureReceived(message); + } + + private void HandleMessage(SuccessMessage message) + { + this.OnUserAuthenticationSuccessReceived(message); + } + + private void HandleMessage(BannerMessage message) + { + this.OnUserAuthenticationBannerReceived(message); + } + + #endregion + + #region Handle connection messages + + private void HandleMessage(GlobalRequestMessage message) + { + this.OnGlobalRequestReceived(message); + } + + private void HandleMessage(RequestSuccessMessage message) + { + this.OnRequestSuccessReceived(message); + } + + private void HandleMessage(RequestFailureMessage message) + { + this.OnRequestFailureReceived(message); + } + + private void HandleMessage(ChannelOpenMessage message) + { + this.OnChannelOpenReceived(message); + } + + private void HandleMessage(ChannelOpenConfirmationMessage message) + { + this.OnChannelOpenConfirmationReceived(message); + } + + private void HandleMessage(ChannelOpenFailureMessage message) + { + this.OnChannelOpenFailureReceived(message); + } + + private void HandleMessage(ChannelWindowAdjustMessage message) + { + this.OnChannelWindowAdjustReceived(message); + } + + private void HandleMessage(ChannelDataMessage message) + { + this.OnChannelDataReceived(message); + } + + private void HandleMessage(ChannelExtendedDataMessage message) + { + this.OnChannelExtendedDataReceived(message); + } + + private void HandleMessage(ChannelEofMessage message) + { + this.OnChannelEofReceived(message); + } + + private void HandleMessage(ChannelCloseMessage message) + { + this.OnChannelCloseReceived(message); + } + + private void HandleMessage(ChannelRequestMessage message) + { + this.OnChannelRequestReceived(message); + } + + private void HandleMessage(ChannelSuccessMessage message) + { + this.OnChannelSuccessReceived(message); + } + + private void HandleMessage(ChannelFailureMessage message) + { + this.OnChannelFailureReceived(message); + } + + #endregion + + #region Handle received message events + + /// + /// Called when received. + /// + /// message. + protected virtual void OnDisconnectReceived(DisconnectMessage message) + { + this.Log(string.Format("Disconnect received: {0} {1}", message.ReasonCode, message.Description)); + + var disconnectReceived = DisconnectReceived; + if (disconnectReceived != null) + disconnectReceived(this, new MessageEventArgs(message)); + + var disconnected = Disconnected; + if (disconnected != null) + disconnected(this, new EventArgs()); + } + + /// + /// Called when received. + /// + /// message. + protected virtual void OnIgnoreReceived(IgnoreMessage message) + { + var handlers = IgnoreReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnUnimplementedReceived(UnimplementedMessage message) + { + var handlers = UnimplementedReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnDebugReceived(DebugMessage message) + { + var handlers = DebugReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnServiceRequestReceived(ServiceRequestMessage message) + { + var handlers = ServiceRequestReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnServiceAcceptReceived(ServiceAcceptMessage message) + { + var handlers = ServiceAcceptReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnKeyExchangeInitReceived(KeyExchangeInitMessage message) + { + this._keyExchangeInProgress = true; + + this._keyExchangeCompletedWaitHandle.Reset(); + + // Disable all registered messages except key exchange related + foreach (var messageMetadata in this._messagesMetadata) + { + if (messageMetadata.Activated && messageMetadata.Number > 2 && (messageMetadata.Number < 20 || messageMetadata.Number > 30)) + messageMetadata.Enabled = false; + } + + var keyExchangeAlgorithmName = (from c in this.ConnectionInfo.KeyExchangeAlgorithms.Keys + from s in message.KeyExchangeAlgorithms + where s == c + select c).FirstOrDefault(); + + if (keyExchangeAlgorithmName == null) + { + throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed); + } + + // Create instance of key exchange algorithm that will be used + this._keyExchange = this.ConnectionInfo.KeyExchangeAlgorithms[keyExchangeAlgorithmName].CreateInstance(); + + this.ConnectionInfo.CurrentKeyExchangeAlgorithm = keyExchangeAlgorithmName; + + this._keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived; + + // Start the algorithm implementation + this._keyExchange.Start(this, message); + + var keyExchangeInitReceived = KeyExchangeInitReceived; + if (keyExchangeInitReceived != null) + keyExchangeInitReceived(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnNewKeysReceived(NewKeysMessage message) + { + // Update sessionId + if (this.SessionId == null) + { + this.SessionId = this._keyExchange.ExchangeHash; + } + + // Dispose of old ciphers and hash algorithms + if (this._serverMac != null) + { + this._serverMac.Clear(); + this._serverMac = null; + } + + if (this._clientMac != null) + { + this._clientMac.Clear(); + this._clientMac = null; + } + + // Update negotiated algorithms + this._serverCipher = this._keyExchange.CreateServerCipher(); + this._clientCipher = this._keyExchange.CreateClientCipher(); + this._serverMac = this._keyExchange.CreateServerHash(); + this._clientMac = this._keyExchange.CreateClientHash(); + this._clientCompression = this._keyExchange.CreateCompressor(); + this._serverDecompression = this._keyExchange.CreateDecompressor(); + + // Dispose of old KeyExchange object as it is no longer needed. + if (this._keyExchange != null) + { + this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; + this._keyExchange.Dispose(); + this._keyExchange = null; + } + + // Enable all active registered messages + foreach (var messageMetadata in this._messagesMetadata) + { + if (messageMetadata.Activated) + messageMetadata.Enabled = true; + } + + var newKeysReceived = NewKeysReceived; + if (newKeysReceived != null) + newKeysReceived(this, new MessageEventArgs(message)); + + // Signal that key exchange completed + this._keyExchangeCompletedWaitHandle.Set(); + + this._keyExchangeInProgress = false; + } + + /// + /// Called when client is disconnecting from the server. + /// + internal void OnDisconnecting() + { + _isDisconnecting = true; + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnUserAuthenticationRequestReceived(RequestMessage message) + { + var handlers = UserAuthenticationRequestReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnUserAuthenticationFailureReceived(FailureMessage message) + { + var handlers = UserAuthenticationFailureReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnUserAuthenticationSuccessReceived(SuccessMessage message) + { + var handlers = UserAuthenticationSuccessReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnUserAuthenticationBannerReceived(BannerMessage message) + { + var handlers = UserAuthenticationBannerReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnGlobalRequestReceived(GlobalRequestMessage message) + { + var handlers = GlobalRequestReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnRequestSuccessReceived(RequestSuccessMessage message) + { + var handlers = RequestSuccessReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnRequestFailureReceived(RequestFailureMessage message) + { + var handlers = RequestFailureReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelOpenReceived(ChannelOpenMessage message) + { + var handlers = ChannelOpenReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message) + { + var handlers = ChannelOpenConfirmationReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message) + { + var handlers = ChannelOpenFailureReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message) + { + var handlers = ChannelWindowAdjustReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelDataReceived(ChannelDataMessage message) + { + var handlers = ChannelDataReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message) + { + var handlers = ChannelExtendedDataReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelEofReceived(ChannelEofMessage message) + { + var handlers = ChannelEofReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelCloseReceived(ChannelCloseMessage message) + { + var handlers = ChannelCloseReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelRequestReceived(ChannelRequestMessage message) + { + var handlers = ChannelRequestReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelSuccessReceived(ChannelSuccessMessage message) + { + var handlers = ChannelSuccessReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnChannelFailureReceived(ChannelFailureMessage message) + { + var handlers = ChannelFailureReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + /// + /// Called when message received. + /// + /// message. + protected virtual void OnMessageReceived(Message message) + { + var handlers = MessageReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + + #endregion + + private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e) + { + var handlers = HostKeyReceived; + if (handlers != null) + handlers(this, e); + } + + /// + /// Reads the specified length of bytes from the server. + /// + /// The length. + /// + /// The bytes read from the server. + /// + private byte[] Read(int length) + { + var buffer = new byte[length]; + + this.SocketRead(length, ref buffer); + + return buffer; + } + + #region Message loading functions + + /// + /// Registers SSH Message with the session. + /// + /// Name of the message. + public void RegisterMessage(string messageName) + { + this.InternalRegisterMessage(messageName); + } + + /// + /// Removes SSH message from the session + /// + /// Name of the message. + public void UnRegisterMessage(string messageName) + { + this.InternalUnRegisterMessage(messageName); + } + + /// + /// Loads the message. + /// + /// Message data. + /// New message + private Message LoadMessage(byte[] data) + { + var messageType = data[0]; + var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled && m.Activated select m).SingleOrDefault(); + if (messageMetadata == null) + throw new SshException(string.Format(CultureInfo.CurrentCulture, "Message type {0} is not valid.", messageType)); + + var message = messageMetadata.Type.CreateInstance(); + + message.Load(data); + + this.Log(string.Format("ReceiveMessage from server: '{0}': '{1}'.", message.GetType().Name, message)); + + return message; + } + + partial void InternalRegisterMessage(string messageName); + + partial void InternalUnRegisterMessage(string messageName); + + #endregion + + partial void ExecuteThread(Action action); + + /// + /// Gets a value indicating whether the socket is connected. + /// + /// + /// true if the socket is connected; otherwise, false. + /// + partial void IsSocketConnected(ref bool isConnected); + + partial void SocketConnect(string host, int port); + + partial void SocketDisconnect(); + + partial void SocketRead(int length, ref byte[] buffer); + + partial void SocketReadLine(ref string response); + + partial void Log(string text); + + /// + /// Writes the specified data to the server. + /// + /// The data. + partial void SocketWrite(byte[] data); + + /// + /// Disconnects and disposes the socket. + /// + private void SocketDisconnectAndDispose() + { + if (_socket != null) + { + lock (_socketLock) + { + if (_socket != null) + { + SocketDisconnect(); + _socket.Dispose(); + _socket = null; + } + } + } + } + + /// + /// Listens for incoming message from the server and handles them. This method run as a task on separate thread. + /// + private void MessageListener() + { + try + { + while (this._socket != null && this._socket.Connected) + { + var message = this.ReceiveMessage(); + this.HandleMessageCore(message); + } + } + catch (Exception exp) + { + this.RaiseError(exp); + } + } + + private byte SocketReadByte() + { + var buffer = new byte[1]; + + this.SocketRead(1, ref buffer); + + return buffer[0]; + } + + private void SocketWriteByte(byte data) + { + this.SocketWrite(new[] {data}); + } + + private void ConnectSocks4() + { + // Send socks version number + this.SocketWriteByte(0x04); + + // Send command code + this.SocketWriteByte(0x01); + + // Send port + this.SocketWriteByte((byte)(this.ConnectionInfo.Port / 0xFF)); + this.SocketWriteByte((byte)(this.ConnectionInfo.Port % 0xFF)); + + // Send IP + IPAddress ipAddress = this.ConnectionInfo.Host.GetIPAddress(); + this.SocketWrite(ipAddress.GetAddressBytes()); + + // Send username + var username = new Common.ASCIIEncoding().GetBytes(this.ConnectionInfo.ProxyUsername); + this.SocketWrite(username); + this.SocketWriteByte(0x00); + + // Read 0 + if (this.SocketReadByte() != 0) + { + throw new ProxyException("SOCKS4: Null is expected."); + } + + // Read response code + var code = this.SocketReadByte(); + + switch (code) + { + case 0x5a: + break; + case 0x5b: + throw new ProxyException("SOCKS4: Connection rejected."); + case 0x5c: + throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server."); + case 0x5d: + throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request."); + default: + throw new ProxyException("SOCKS4: Not valid response."); + } + + var dummyBuffer = new byte[4]; + + // Read 2 bytes to be ignored + this.SocketRead(2, ref dummyBuffer); + + // Read 4 bytes to be ignored + this.SocketRead(4, ref dummyBuffer); + } + + private void ConnectSocks5() + { + // Send socks version number + this.SocketWriteByte(0x05); + + // Send number of supported authentication methods + this.SocketWriteByte(0x02); + + // Send supported authentication methods + this.SocketWriteByte(0x00); // No authentication + this.SocketWriteByte(0x02); // Username/Password + + var socksVersion = this.SocketReadByte(); + if (socksVersion != 0x05) + throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion)); + + var authenticationMethod = this.SocketReadByte(); + switch (authenticationMethod) + { + case 0x00: + break; + case 0x02: + + // Send version + this.SocketWriteByte(0x01); + + var encoding = new Common.ASCIIEncoding(); + + var username = encoding.GetBytes(this.ConnectionInfo.ProxyUsername); + + if (username.Length > byte.MaxValue) + throw new ProxyException("Proxy username is too long."); + + // Send username length + this.SocketWriteByte((byte)username.Length); + + // Send username + this.SocketWrite(username); + + var password = encoding.GetBytes(this.ConnectionInfo.ProxyPassword); + + if (password.Length > byte.MaxValue) + throw new ProxyException("Proxy password is too long."); + + // Send username length + this.SocketWriteByte((byte)password.Length); + + // Send username + this.SocketWrite(password); + + var serverVersion = this.SocketReadByte(); + + if (serverVersion != 1) + throw new ProxyException("SOCKS5: Server authentication version is not valid."); + + var statusCode = this.SocketReadByte(); + if (statusCode != 0) + throw new ProxyException("SOCKS5: Username/Password authentication failed."); + + break; + case 0xFF: + throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); + } + + // Send socks version number + this.SocketWriteByte(0x05); + + // Send command code + this.SocketWriteByte(0x01); // establish a TCP/IP stream connection + + // Send reserved, must be 0x00 + this.SocketWriteByte(0x00); + + IPAddress ip = this.ConnectionInfo.Host.GetIPAddress(); + + // Send address type and address + if (ip.AddressFamily == AddressFamily.InterNetwork) + { + this.SocketWriteByte(0x01); + var address = ip.GetAddressBytes(); + this.SocketWrite(address); + } + else if (ip.AddressFamily == AddressFamily.InterNetworkV6) + { + this.SocketWriteByte(0x04); + var address = ip.GetAddressBytes(); + this.SocketWrite(address); + } + else + { + throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", ip)); + } + + // Send port + this.SocketWriteByte((byte)(this.ConnectionInfo.Port / 0xFF)); + this.SocketWriteByte((byte)(this.ConnectionInfo.Port % 0xFF)); + + // Read Server SOCKS5 version + if (this.SocketReadByte() != 5) + { + throw new ProxyException("SOCKS5: Version 5 is expected."); + } + + // Read response code + var status = this.SocketReadByte(); + + switch (status) + { + case 0x00: + break; + case 0x01: + throw new ProxyException("SOCKS5: General failure."); + case 0x02: + throw new ProxyException("SOCKS5: Connection not allowed by ruleset."); + case 0x03: + throw new ProxyException("SOCKS5: Network unreachable."); + case 0x04: + throw new ProxyException("SOCKS5: Host unreachable."); + case 0x05: + throw new ProxyException("SOCKS5: Connection refused by destination host."); + case 0x06: + throw new ProxyException("SOCKS5: TTL expired."); + case 0x07: + throw new ProxyException("SOCKS5: Command not supported or protocol error."); + case 0x08: + throw new ProxyException("SOCKS5: Address type not supported."); + default: + throw new ProxyException("SOCKS4: Not valid response."); + } + + // Read 0 + if (this.SocketReadByte() != 0) + { + throw new ProxyException("SOCKS5: 0 byte is expected."); + } + + var addressType = this.SocketReadByte(); + var responseIp = new byte[16]; + + switch (addressType) + { + case 0x01: + this.SocketRead(4, ref responseIp); + break; + case 0x04: + this.SocketRead(16, ref responseIp); + break; + default: + throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType)); + } + + var port = new byte[2]; + + // Read 2 bytes to be ignored + this.SocketRead(2, ref port); + } + + private void ConnectHttp() + { + var httpResponseRe = new Regex(@"HTTP/(?\d[.]\d) (?\d{3}) (?.+)$"); + var httpHeaderRe = new Regex(@"(?[^\[\]()<>@,;:\""/?={} \t]+):(?.+)?"); + + var encoding = new Common.ASCIIEncoding(); + + this.SocketWrite(encoding.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.0\r\n", this.ConnectionInfo.Host, this.ConnectionInfo.Port))); + + // Sent proxy authorization is specified + if (!string.IsNullOrEmpty(this.ConnectionInfo.ProxyUsername)) + { + var authorization = string.Format("Proxy-Authorization: Basic {0}\r\n", + Convert.ToBase64String(encoding.GetBytes(string.Format("{0}:{1}", this.ConnectionInfo.ProxyUsername, this.ConnectionInfo.ProxyPassword))) + ); + this.SocketWrite(encoding.GetBytes(authorization)); + } + + this.SocketWrite(encoding.GetBytes("\r\n")); + + HttpStatusCode? statusCode = null; + var response = string.Empty; + var contentLength = 0; + + while (true) + { + this.SocketReadLine(ref response); + + if (statusCode == null) + { + var statusMatch = httpResponseRe.Match(response); + if (statusMatch.Success) + { + var httpStatusCode = statusMatch.Result("${statusCode}"); + statusCode = (HttpStatusCode) int.Parse(httpStatusCode); + if (statusCode != HttpStatusCode.OK) + { + var reasonPhrase = statusMatch.Result("${reasonPhrase}"); + throw new ProxyException(string.Format("HTTP: Status code {0}, \"{1}\"", httpStatusCode, + reasonPhrase)); + } + continue; + } + } + + // continue on parsing message headers coming from the server + var headerMatch = httpHeaderRe.Match(response); + if (headerMatch.Success) + { + var fieldName = headerMatch.Result("${fieldName}"); + if (fieldName.Equals("Content-Length", StringComparison.InvariantCultureIgnoreCase)) + { + contentLength = int.Parse(headerMatch.Result("${fieldValue}")); + } + continue; + } + + // check if we've reached the CRLF which separates request line and headers from the message body + if (response.Length == 0) + { + // read response body if specified + if (contentLength > 0) + { + var contentBody = new byte[contentLength]; + SocketRead(contentLength, ref contentBody); + } + break; + } + } + } + + /// + /// Raises the event. + /// + /// The exp. + private void RaiseError(Exception exp) + { + var connectionException = exp as SshConnectionException; + + if (_isDisconnecting) + { + // a connection exception which is raised while isDisconnecting is normal and + // should be ignored + if (connectionException != null) + return; + + // any timeout while disconnecting can be caused by loss of connectivity + // altogether and should be ignored + var socketException = exp as SocketException; + if (socketException != null && socketException.SocketErrorCode == SocketError.TimedOut) + return; + } + + this._exception = exp; + + this._exceptionWaitHandle.Set(); + + var errorOccured = ErrorOccured; + if (errorOccured != null) + errorOccured(this, new ExceptionEventArgs(exp)); + + if (connectionException != null && connectionException.DisconnectReason != DisconnectReason.ConnectionLost) + { + this.Disconnect(connectionException.DisconnectReason, exp.ToString()); + } + } + + /// + /// Resets connection-specific information to ensure state of a previous connection + /// does not affect new connections. + /// + private void Reset() + { + if (_exceptionWaitHandle != null) + _exceptionWaitHandle.Reset(); + if (_keyExchangeCompletedWaitHandle != null) + _keyExchangeCompletedWaitHandle.Reset(); + if (_messageListenerCompleted != null) + _messageListenerCompleted.Reset(); + + SessionId = null; + _isDisconnectMessageSent = false; + _isDisconnecting = false; + _isAuthenticated = false; + _exception = null; + _keyExchangeInProgress = false; + } + + #region IDisposable Members + + private bool _disposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._disposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + Disconnect(); + + if (this._serviceAccepted != null) + { + this._serviceAccepted.Dispose(); + this._serviceAccepted = null; + } + + if (this._exceptionWaitHandle != null) + { + this._exceptionWaitHandle.Dispose(); + this._exceptionWaitHandle = null; + } + + if (this._keyExchangeCompletedWaitHandle != null) + { + this._keyExchangeCompletedWaitHandle.Dispose(); + this._keyExchangeCompletedWaitHandle = null; + } + + if (this._serverMac != null) + { + this._serverMac.Clear(); + this._serverMac = null; + } + + if (this._clientMac != null) + { + this._clientMac.Clear(); + this._clientMac = null; + } + + if (this._keyExchange != null) + { + this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; + this._keyExchange.Dispose(); + this._keyExchange = null; + } + } + + // Note disposing has been done. + this._disposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Session() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + private class MessageMetadata + { + public string Name { get; set; } + + public byte Number { get; set; } + + public bool Enabled { get; set; } + + public bool Activated { get; set; } + + public Type Type { get; set; } + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Flags.cs b/Renci.SshNet/Sftp/Flags.cs index 837d824..d8cbe17 100644 --- a/Renci.SshNet/Sftp/Flags.cs +++ b/Renci.SshNet/Sftp/Flags.cs @@ -1,31 +1,31 @@ -namespace Renci.SshNet.Sftp -{ - public enum Flags - { - None = 0x00000000, - /// - /// SSH_FXF_READ - /// - Read = 0x00000001, - /// - /// SSH_FXF_WRITE - /// - Write = 0x00000002, - /// - /// SSH_FXF_APPEND - /// - Append = 0x00000004, - /// - /// SSH_FXF_CREAT - /// - CreateNewOrOpen = 0x00000008, - /// - /// SSH_FXF_TRUNC - /// - Truncate = 0x00000010, - /// - /// SSH_FXF_EXCL - /// - CreateNew = 0x00000028 - } -} +namespace Renci.SshNet.Sftp +{ + internal enum Flags + { + None = 0x00000000, + /// + /// SSH_FXF_READ + /// + Read = 0x00000001, + /// + /// SSH_FXF_WRITE + /// + Write = 0x00000002, + /// + /// SSH_FXF_APPEND + /// + Append = 0x00000004, + /// + /// SSH_FXF_CREAT + /// + CreateNewOrOpen = 0x00000008, + /// + /// SSH_FXF_TRUNC + /// + Truncate = 0x00000010, + /// + /// SSH_FXF_EXCL + /// + CreateNew = 0x00000028 + } +} diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs index 068b26e..c9ebd0e 100644 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs +++ b/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs @@ -1,33 +1,33 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class FStatVfsRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "fstatvfs@openssh.com"; } - } - - public byte[] Handle { get; private set; } - - public FStatVfsRequest(uint protocolVersion, uint requestId, byte[] handle, Action extendedAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.SetAction(extendedAction); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class FStatVfsRequest : SftpExtendedRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Extended; } + } + + public override string Name + { + get { return "fstatvfs@openssh.com"; } + } + + public byte[] Handle { get; private set; } + + public FStatVfsRequest(uint protocolVersion, uint requestId, byte[] handle, Action extendedAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.SetAction(extendedAction); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs index 422826a..188bc1b 100644 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs +++ b/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs @@ -1,36 +1,36 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class HardLinkRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "hardlink@openssh.com"; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public HardLinkRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.OldPath); - this.Write(this.NewPath); - } - } +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class HardLinkRequest : SftpExtendedRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Extended; } + } + + public override string Name + { + get { return "hardlink@openssh.com"; } + } + + public string OldPath { get; private set; } + + public string NewPath { get; private set; } + + public HardLinkRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.OldPath = oldPath; + this.NewPath = newPath; + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.OldPath); + this.Write(this.NewPath); + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs index 11e4dda..3a3681f 100644 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs +++ b/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs @@ -1,40 +1,40 @@ -using System; -using Renci.SshNet.Sftp.Responses; -using System.Text; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class PosixRenameRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "posix-rename@openssh.com"; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public Encoding Encoding { get; private set; } - - public PosixRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - this.Encoding = encoding; - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.OldPath, this.Encoding); - this.Write(this.NewPath, this.Encoding); - } - } +using System; +using Renci.SshNet.Sftp.Responses; +using System.Text; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class PosixRenameRequest : SftpExtendedRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Extended; } + } + + public override string Name + { + get { return "posix-rename@openssh.com"; } + } + + public string OldPath { get; private set; } + + public string NewPath { get; private set; } + + public Encoding Encoding { get; private set; } + + public PosixRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Encoding encoding, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.OldPath = oldPath; + this.NewPath = newPath; + this.Encoding = encoding; + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.OldPath, this.Encoding); + this.Write(this.NewPath, this.Encoding); + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs index b21e7aa..cdeadb2 100644 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs +++ b/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs @@ -1,37 +1,37 @@ -using System; -using Renci.SshNet.Sftp.Responses; -using System.Text; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class StatVfsRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "statvfs@openssh.com"; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public StatVfsRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action extendedAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(extendedAction); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; +using System.Text; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class StatVfsRequest : SftpExtendedRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Extended; } + } + + public override string Name + { + get { return "statvfs@openssh.com"; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public StatVfsRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action extendedAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + this.SetAction(extendedAction); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs b/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs index a593e44..b272aa1 100644 --- a/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs +++ b/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs @@ -1,34 +1,34 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class PosixRenameRequest : SftpRequest - { - public const string NAME = "posix-rename@openssh.com"; - - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public PosixRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(PosixRenameRequest.NAME); - this.Write(this.OldPath); - this.Write(this.NewPath); - } - } +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class PosixRenameRequest : SftpRequest + { + public const string NAME = "posix-rename@openssh.com"; + + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Extended; } + } + + public string OldPath { get; private set; } + + public string NewPath { get; private set; } + + public PosixRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.OldPath = oldPath; + this.NewPath = newPath; + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(PosixRenameRequest.NAME); + this.Write(this.OldPath); + this.Write(this.NewPath); + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs b/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs index 45424db..d17e6f1 100644 --- a/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs @@ -1,51 +1,48 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpBlockRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Block; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public UInt64 Length { get; private set; } - - public UInt32 LockMask { get; private set; } - - public SftpBlockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, UInt32 lockMask, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Length = length; - this.LockMask = lockMask; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Length = this.ReadUInt64(); - this.LockMask = this.ReadUInt32(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.Write(this.Length); - this.Write(this.LockMask); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpBlockRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Block; } + } + + public byte[] Handle { get; private set; } + + public UInt64 Offset { get; private set; } + + public UInt64 Length { get; private set; } + + public UInt32 LockMask { get; private set; } + + public SftpBlockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, UInt32 lockMask, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.Offset = offset; + this.Length = length; + this.LockMask = lockMask; + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + this.Offset = this.ReadUInt64(); + this.Length = this.ReadUInt64(); + this.LockMask = this.ReadUInt32(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + this.Write(this.Offset); + this.Write(this.Length); + this.Write(this.LockMask); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs b/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs index f034607..3ceb1fd 100644 --- a/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs @@ -1,36 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpCloseRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Close; } - } - - public byte[] Handle { get; private set; } - - public SftpCloseRequest(uint protocolVersion, uint requestId, byte[] handle, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpCloseRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Close; } + } + + public byte[] Handle { get; private set; } + + public SftpCloseRequest(uint protocolVersion, uint requestId, byte[] handle, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs b/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs index f716691..e500e51 100644 --- a/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs @@ -1,28 +1,28 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal abstract class SftpExtendedRequest : SftpRequest - { - public const string NAME = "posix-rename@openssh.com"; - - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public abstract string Name { get; } - - public SftpExtendedRequest(uint protocolVersion, uint requestId, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Name); - } - } +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal abstract class SftpExtendedRequest : SftpRequest + { + public const string NAME = "posix-rename@openssh.com"; + + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Extended; } + } + + public abstract string Name { get; } + + public SftpExtendedRequest(uint protocolVersion, uint requestId, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Name); + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs index cf1b501..64795d0 100644 --- a/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs @@ -1,41 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpFSetStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.FSetStat; } - } - - public byte[] Handle { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpFSetStatRequest(uint protocolVersion, uint requestId, byte[] handle, SftpFileAttributes attributes, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Attributes = attributes; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Attributes = this.ReadAttributes(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Attributes); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpFSetStatRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.FSetStat; } + } + + public byte[] Handle { get; private set; } + + public SftpFileAttributes Attributes { get; private set; } + + public SftpFSetStatRequest(uint protocolVersion, uint requestId, byte[] handle, SftpFileAttributes attributes, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.Attributes = attributes; + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + this.Attributes = this.ReadAttributes(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + this.Write(this.Attributes); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs index 075c819..9265891 100644 --- a/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs @@ -1,37 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpFStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.FStat; } - } - - public byte[] Handle { get; private set; } - - public SftpFStatRequest(uint protocolVersion, uint requestId, byte[] handle, Action attrsAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.SetAction(attrsAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpFStatRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.FStat; } + } + + public byte[] Handle { get; private set; } + + public SftpFStatRequest(uint protocolVersion, uint requestId, byte[] handle, Action attrsAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.SetAction(attrsAction); + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs b/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs index f329597..7d05694 100644 --- a/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs @@ -1,34 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpInitRequest : SftpMessage - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Init; } - } - - public uint Version { get; private set; } - - public SftpInitRequest(uint version) - { - this.Version = version; - } - - protected override void LoadData() - { - base.LoadData(); - this.Version = this.ReadUInt32(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Version); - } - } -} +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpInitRequest : SftpMessage + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Init; } + } + + public uint Version { get; private set; } + + public SftpInitRequest(uint version) + { + this.Version = version; + } + + protected override void LoadData() + { + base.LoadData(); + this.Version = this.ReadUInt32(); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Version); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs index a57d3d2..b03545c 100644 --- a/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs @@ -1,40 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpLStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.LStat; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpLStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action attrsAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(attrsAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpLStatRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.LStat; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpLStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action attrsAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + this.SetAction(attrsAction); + } + + protected override void LoadData() + { + base.LoadData(); + this.Path = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs b/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs index b95cd7e..d62d0be 100644 --- a/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs @@ -1,55 +1,52 @@ -using Renci.SshNet.Sftp.Responses; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpLinkRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Link; } - } - - public string NewLinkPath { get; private set; } - - public string ExistingPath { get; private set; } - - public bool IsSymLink { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The protocol version. - /// The request id. - /// Specifies the path name of the new link to create. - /// Specifies the path of a target object to which the newly created link will refer. In the case of a symbolic link, this path may not exist. - /// if set to false the link should be a hard link, or a second directory entry referring to the same file or directory object. - /// The status action. - public SftpLinkRequest(uint protocolVersion, uint requestId, string newLinkPath, string existingPath, bool isSymLink, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.NewLinkPath = newLinkPath; - this.ExistingPath = existingPath; - this.IsSymLink = isSymLink; - } - - protected override void LoadData() - { - base.LoadData(); - this.NewLinkPath = this.ReadString(); - this.ExistingPath = this.ReadString(); - this.IsSymLink = this.ReadBoolean(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.NewLinkPath); - this.Write(this.ExistingPath); - this.Write(this.IsSymLink); - } - } -} +using Renci.SshNet.Sftp.Responses; +using System; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpLinkRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Link; } + } + + public string NewLinkPath { get; private set; } + + public string ExistingPath { get; private set; } + + public bool IsSymLink { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The protocol version. + /// The request id. + /// Specifies the path name of the new link to create. + /// Specifies the path of a target object to which the newly created link will refer. In the case of a symbolic link, this path may not exist. + /// if set to false the link should be a hard link, or a second directory entry referring to the same file or directory object. + /// The status action. + public SftpLinkRequest(uint protocolVersion, uint requestId, string newLinkPath, string existingPath, bool isSymLink, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.NewLinkPath = newLinkPath; + this.ExistingPath = existingPath; + this.IsSymLink = isSymLink; + } + + protected override void LoadData() + { + base.LoadData(); + this.NewLinkPath = this.ReadString(); + this.ExistingPath = this.ReadString(); + this.IsSymLink = this.ReadBoolean(); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.NewLinkPath); + this.Write(this.ExistingPath); + this.Write(this.IsSymLink); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs index cef26f6..ef7a690 100644 --- a/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs @@ -1,49 +1,47 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpMkDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.MkDir; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpMkDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action statusAction) - : this(protocolVersion, requestId, path, encoding, null, statusAction) - { - } - - public SftpMkDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, SftpFileAttributes attributes, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.Attributes = attributes; - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - this.Attributes = this.ReadAttributes(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - this.Write(this.Attributes); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpMkDirRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.MkDir; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpFileAttributes Attributes { get; private set; } + + public SftpMkDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action statusAction) + : this(protocolVersion, requestId, path, encoding, null, statusAction) + { + } + + public SftpMkDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, SftpFileAttributes attributes, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + this.Attributes = attributes; + } + + protected override void LoadData() + { + base.LoadData(); + this.Path = this.ReadString(this.Encoding); + this.Attributes = this.ReadAttributes(); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + this.Write(this.Attributes); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs index 89b0a24..6792031 100644 --- a/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs @@ -1,40 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpOpenDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.OpenDir; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpOpenDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action handleAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(handleAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpOpenDirRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.OpenDir; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpOpenDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action handleAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + this.SetAction(handleAction); + } + + protected override void LoadData() + { + base.LoadData(); + this.Path = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs b/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs index 853cc76..9fa3333 100644 --- a/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs @@ -1,55 +1,53 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpOpenRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Open; } - } - - public string Filename { get; private set; } - - public Flags Flags { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpOpenRequest(uint protocolVersion, uint requestId, string fileName, Encoding encoding, Flags flags, Action handleAction, Action statusAction) - : this(protocolVersion, requestId, fileName, encoding, flags, new SftpFileAttributes(), handleAction, statusAction) - { - } - - public SftpOpenRequest(uint protocolVersion, uint requestId, string fileName, Encoding encoding, Flags flags, SftpFileAttributes attributes, Action handleAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Filename = fileName; - this.Flags = flags; - this.Attributes = attributes; - this.Encoding = encoding; - - this.SetAction(handleAction); - } - - protected override void LoadData() - { - base.LoadData(); - throw new NotSupportedException(); - } - - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.Filename, this.Encoding); - this.Write((uint)this.Flags); - this.Write(this.Attributes); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpOpenRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Open; } + } + + public string Filename { get; private set; } + + public Flags Flags { get; private set; } + + public SftpFileAttributes Attributes { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpOpenRequest(uint protocolVersion, uint requestId, string fileName, Encoding encoding, Flags flags, Action handleAction, Action statusAction) + : this(protocolVersion, requestId, fileName, encoding, flags, new SftpFileAttributes(), handleAction, statusAction) + { + } + + public SftpOpenRequest(uint protocolVersion, uint requestId, string fileName, Encoding encoding, Flags flags, SftpFileAttributes attributes, Action handleAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Filename = fileName; + this.Flags = flags; + this.Attributes = attributes; + this.Encoding = encoding; + + this.SetAction(handleAction); + } + + protected override void LoadData() + { + base.LoadData(); + throw new NotSupportedException(); + } + + protected override void SaveData() + { + base.SaveData(); + + this.Write(this.Filename, this.Encoding); + this.Write((uint)this.Flags); + this.Write(this.Attributes); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs index c666d71..90806b8 100644 --- a/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs @@ -1,37 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpReadDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.ReadDir; } - } - - public byte[] Handle { get; private set; } - - public SftpReadDirRequest(uint protocolVersion, uint requestId, byte[] handle, Action nameAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.SetAction(nameAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpReadDirRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.ReadDir; } + } + + public byte[] Handle { get; private set; } + + public SftpReadDirRequest(uint protocolVersion, uint requestId, byte[] handle, Action nameAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.SetAction(nameAction); + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs b/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs index e3f279e..e567a11 100644 --- a/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs @@ -1,40 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpReadLinkRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.ReadLink; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpReadLinkRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action nameAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(nameAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpReadLinkRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.ReadLink; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpReadLinkRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action nameAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + this.SetAction(nameAction); + } + + protected override void LoadData() + { + base.LoadData(); + this.Path = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs b/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs index 2db39b4..745035d 100644 --- a/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs @@ -1,47 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpReadRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Read; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public UInt32 Length { get; private set; } - - public SftpReadRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt32 length, Action dataAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Length = length; - this.SetAction(dataAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Length = this.ReadUInt32(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.Write(this.Length); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpReadRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Read; } + } + + public byte[] Handle { get; private set; } + + public UInt64 Offset { get; private set; } + + public UInt32 Length { get; private set; } + + public SftpReadRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt32 length, Action dataAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.Offset = offset; + this.Length = length; + this.SetAction(dataAction); + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + this.Offset = this.ReadUInt64(); + this.Length = this.ReadUInt32(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + this.Write(this.Offset); + this.Write(this.Length); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs index d23e3e3..e0e193c 100644 --- a/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs @@ -1,41 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRealPathRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.RealPath; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRealPathRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action nameAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - if (nameAction == null) - throw new ArgumentNullException("name"); - - if (statusAction == null) - throw new ArgumentNullException("status"); - - this.Path = path; - this.Encoding = encoding; - this.SetAction(nameAction); - - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpRealPathRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.RealPath; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpRealPathRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action nameAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + if (nameAction == null) + throw new ArgumentNullException("nameAction"); + if (statusAction == null) + throw new ArgumentNullException("statusAction"); + + this.Path = path; + this.Encoding = encoding; + this.SetAction(nameAction); + + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs index c71b481..8eefa27 100644 --- a/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs @@ -1,39 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRemoveRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Remove; } - } - - public string Filename { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRemoveRequest(uint protocolVersion, uint requestId, string filename, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Filename = filename; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.Filename = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Filename, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpRemoveRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Remove; } + } + + public string Filename { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpRemoveRequest(uint protocolVersion, uint requestId, string filename, Encoding encoding, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Filename = filename; + this.Encoding = encoding; + } + + protected override void LoadData() + { + base.LoadData(); + this.Filename = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Filename, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs index 3267fb7..07aa990 100644 --- a/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs @@ -1,44 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRenameRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Rename; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.OldPath = this.ReadString(this.Encoding); - this.NewPath = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.OldPath, this.Encoding); - this.Write(this.NewPath, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpRenameRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Rename; } + } + + public string OldPath { get; private set; } + + public string NewPath { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Encoding encoding, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.OldPath = oldPath; + this.NewPath = newPath; + this.Encoding = encoding; + } + + protected override void LoadData() + { + base.LoadData(); + this.OldPath = this.ReadString(this.Encoding); + this.NewPath = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.OldPath, this.Encoding); + this.Write(this.NewPath, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRequest.cs index c864034..f1964b5 100644 --- a/Renci.SshNet/Sftp/Requests/SftpRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpRequest.cs @@ -1,98 +1,94 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; -using System.Threading; - -namespace Renci.SshNet.Sftp.Requests -{ - internal abstract class SftpRequest : SftpMessage - { - private Action _statusAction; - private Action _attrsAction; - private Action _dataAction; - private Action _extendedReplyAction; - private Action _handleAction; - private Action _nameAction; - - public uint RequestId { get; private set; } - - public uint ProtocolVersion { get; private set; } - - public SftpRequest(uint protocolVersion, uint requestId, Action statusAction) - { - this.RequestId = requestId; - this.ProtocolVersion = protocolVersion; - this._statusAction = statusAction; - } - - public void Complete(SftpResponse response) - { - if (response is SftpStatusResponse) - { - this._statusAction(response as SftpStatusResponse); - } - else if (response is SftpAttrsResponse) - { - this._attrsAction(response as SftpAttrsResponse); - } - else if (response is SftpDataResponse) - { - this._dataAction(response as SftpDataResponse); - } - else if (response is SftpExtendedReplyResponse) - { - this._extendedReplyAction(response as SftpExtendedReplyResponse); - } - else if (response is SftpHandleResponse) - { - this._handleAction(response as SftpHandleResponse); - } - else if (response is SftpNameResponse) - { - this._nameAction(response as SftpNameResponse); - } - else - { - throw new InvalidOperationException(string.Format("Response of type '{0}' is not expected.", response.GetType().Name)); - } - } - - protected void SetAction(Action action) - { - this._attrsAction = action; - } - - protected void SetAction(Action action) - { - this._dataAction = action; - } - - protected void SetAction(Action action) - { - this._extendedReplyAction = action; - } - - protected void SetAction(Action action) - { - this._handleAction = action; - } - - protected void SetAction(Action action) - { - this._nameAction = action; - } - - protected override void LoadData() - { - throw new InvalidOperationException("Request cannot be saved."); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.RequestId); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal abstract class SftpRequest : SftpMessage + { + private readonly Action _statusAction; + private Action _attrsAction; + private Action _dataAction; + private Action _extendedReplyAction; + private Action _handleAction; + private Action _nameAction; + + public uint RequestId { get; private set; } + + public uint ProtocolVersion { get; private set; } + + public SftpRequest(uint protocolVersion, uint requestId, Action statusAction) + { + this.RequestId = requestId; + this.ProtocolVersion = protocolVersion; + this._statusAction = statusAction; + } + + public void Complete(SftpResponse response) + { + if (response is SftpStatusResponse) + { + this._statusAction(response as SftpStatusResponse); + } + else if (response is SftpAttrsResponse) + { + this._attrsAction(response as SftpAttrsResponse); + } + else if (response is SftpDataResponse) + { + this._dataAction(response as SftpDataResponse); + } + else if (response is SftpExtendedReplyResponse) + { + this._extendedReplyAction(response as SftpExtendedReplyResponse); + } + else if (response is SftpHandleResponse) + { + this._handleAction(response as SftpHandleResponse); + } + else if (response is SftpNameResponse) + { + this._nameAction(response as SftpNameResponse); + } + else + { + throw new InvalidOperationException(string.Format("Response of type '{0}' is not expected.", response.GetType().Name)); + } + } + + protected void SetAction(Action action) + { + this._attrsAction = action; + } + + protected void SetAction(Action action) + { + this._dataAction = action; + } + + protected void SetAction(Action action) + { + this._extendedReplyAction = action; + } + + protected void SetAction(Action action) + { + this._handleAction = action; + } + + protected void SetAction(Action action) + { + this._nameAction = action; + } + + protected override void LoadData() + { + throw new InvalidOperationException("Request cannot be saved."); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.RequestId); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs index 86fa680..f8e577c 100644 --- a/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs @@ -1,39 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRmDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.RmDir; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRmDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpRmDirRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.RmDir; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpRmDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + } + + protected override void LoadData() + { + base.LoadData(); + this.Path = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs index d9c081e..cca618d 100644 --- a/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs @@ -1,44 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpSetStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.SetStat; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpSetStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, SftpFileAttributes attributes, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.Attributes = attributes; - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - this.Attributes = this.ReadAttributes(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - this.Write(this.Attributes); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpSetStatRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.SetStat; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpFileAttributes Attributes { get; private set; } + + public SftpSetStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, SftpFileAttributes attributes, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + this.Attributes = attributes; + } + + protected override void LoadData() + { + base.LoadData(); + this.Path = this.ReadString(this.Encoding); + this.Attributes = this.ReadAttributes(); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + this.Write(this.Attributes); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs index 2fb1507..562b80d 100644 --- a/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs @@ -1,40 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Stat; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action attrsAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(attrsAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpStatRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Stat; } + } + + public string Path { get; private set; } + + public Encoding Encoding { get; private set; } + + public SftpStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action attrsAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.Encoding = encoding; + this.SetAction(attrsAction); + } + + protected override void LoadData() + { + base.LoadData(); + this.Path = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.Path, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs b/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs index 7ab8ae5..5111135 100644 --- a/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs @@ -1,44 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpSymLinkRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.SymLink; } - } - - public string NewLinkPath { get; set; } - - public string ExistingPath { get; set; } - - public Encoding Encoding { get; set; } - - public SftpSymLinkRequest(uint protocolVersion, uint requestId, string newLinkPath, string existingPath, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.NewLinkPath = newLinkPath; - this.ExistingPath = existingPath; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.NewLinkPath = this.ReadString(this.Encoding); - this.ExistingPath = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.NewLinkPath, this.Encoding); - this.Write(this.ExistingPath, this.Encoding); - } - } -} +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpSymLinkRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.SymLink; } + } + + public string NewLinkPath { get; set; } + + public string ExistingPath { get; set; } + + public Encoding Encoding { get; set; } + + public SftpSymLinkRequest(uint protocolVersion, uint requestId, string newLinkPath, string existingPath, Encoding encoding, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.NewLinkPath = newLinkPath; + this.ExistingPath = existingPath; + this.Encoding = encoding; + } + + protected override void LoadData() + { + base.LoadData(); + this.NewLinkPath = this.ReadString(this.Encoding); + this.ExistingPath = this.ReadString(this.Encoding); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(this.NewLinkPath, this.Encoding); + this.Write(this.ExistingPath, this.Encoding); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs b/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs index 3996f60..e918359 100644 --- a/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs @@ -1,46 +1,43 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpUnblockRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Block; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public UInt64 Length { get; private set; } - - public SftpUnblockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, UInt32 lockMask, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Length = length; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Length = this.ReadUInt64(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.Write(this.Length); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpUnblockRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Block; } + } + + public byte[] Handle { get; private set; } + + public UInt64 Offset { get; private set; } + + public UInt64 Length { get; private set; } + + public SftpUnblockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, UInt32 lockMask, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.Offset = offset; + this.Length = length; + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + this.Offset = this.ReadUInt64(); + this.Length = this.ReadUInt64(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + this.Write(this.Offset); + this.Write(this.Length); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs b/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs index 1756b7f..559b746 100644 --- a/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs +++ b/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs @@ -1,46 +1,43 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpWriteRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Write; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public byte[] Data { get; private set; } - - public SftpWriteRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, byte[] data, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Data = data; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Data = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.WriteBinaryString(this.Data); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class SftpWriteRequest : SftpRequest + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Write; } + } + + public byte[] Handle { get; private set; } + + public UInt64 Offset { get; private set; } + + public byte[] Data { get; private set; } + + public SftpWriteRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, byte[] data, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Handle = handle; + this.Offset = offset; + this.Data = data; + } + + protected override void LoadData() + { + base.LoadData(); + this.Handle = this.ReadBinaryString(); + this.Offset = this.ReadUInt64(); + this.Data = this.ReadBinaryString(); + } + + protected override void SaveData() + { + base.SaveData(); + this.WriteBinaryString(this.Handle); + this.Write(this.Offset); + this.WriteBinaryString(this.Data); + } + } +} diff --git a/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs b/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs index 2d33fac..1e65283 100644 --- a/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs +++ b/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs @@ -1,31 +1,31 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class StatVfsRequest : SftpRequest - { - public const string NAME = "statvfs@openssh.com"; - - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public string Path { get; private set; } - - public StatVfsRequest(uint protocolVersion, uint requestId, string path, Action extendedAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.SetAction(extendedAction); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(StatVfsRequest.NAME); - this.Write(this.Path); - } - } -} +using System; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Sftp.Requests +{ + internal class StatVfsRequest : SftpRequest + { + public const string NAME = "statvfs@openssh.com"; + + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Extended; } + } + + public string Path { get; private set; } + + public StatVfsRequest(uint protocolVersion, uint requestId, string path, Action extendedAction, Action statusAction) + : base(protocolVersion, requestId, statusAction) + { + this.Path = path; + this.SetAction(extendedAction); + } + + protected override void SaveData() + { + base.SaveData(); + this.Write(StatVfsRequest.NAME); + this.Write(this.Path); + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs b/Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs index a4b62e0..4e53692 100644 --- a/Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs +++ b/Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs @@ -1,25 +1,22 @@ -using Renci.SshNet.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal abstract class ExtendedReplyInfo : SshData - { - protected override void LoadData() - { - // Read Message Type - var messageType = this.ReadByte(); - - // Read Response ID - var responseId = this.ReadUInt32(); - } - - protected override void SaveData() - { - throw new NotImplementedException(); - } - } -} +using Renci.SshNet.Common; +using System; + +namespace Renci.SshNet.Sftp.Responses +{ + internal abstract class ExtendedReplyInfo : SshData + { + protected override void LoadData() + { + // Read Message Type + var messageType = this.ReadByte(); + + // Read Response ID + var responseId = this.ReadUInt32(); + } + + protected override void SaveData() + { + throw new NotImplementedException(); + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs b/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs index 54026f3..6ad8188 100644 --- a/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs +++ b/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs @@ -1,26 +1,24 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class StatVfsReplyInfo : ExtendedReplyInfo - { - public SftpFileSytemInformation Information { get; private set; } - - protected override void LoadData() - { - base.LoadData(); - - this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64()); - } - - protected override void SaveData() - { - throw new System.NotImplementedException(); - } - } +namespace Renci.SshNet.Sftp.Responses +{ + internal class StatVfsReplyInfo : ExtendedReplyInfo + { + public SftpFileSytemInformation Information { get; private set; } + + protected override void LoadData() + { + base.LoadData(); + + this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64()); + } + + protected override void SaveData() + { + throw new System.NotImplementedException(); + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs b/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs index cc12827..d1e33e1 100644 --- a/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs @@ -1,28 +1,23 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpAttrsResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Attrs; } - } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpAttrsResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - protected override void LoadData() - { - base.LoadData(); - this.Attributes = this.ReadAttributes(); - } - } -} +namespace Renci.SshNet.Sftp.Responses +{ + internal class SftpAttrsResponse : SftpResponse + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Attrs; } + } + + public SftpFileAttributes Attributes { get; private set; } + + public SftpAttrsResponse(uint protocolVersion) + : base(protocolVersion) + { + } + + protected override void LoadData() + { + base.LoadData(); + this.Attributes = this.ReadAttributes(); + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs b/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs index 8c367f4..57b172d 100644 --- a/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs @@ -1,36 +1,31 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpDataResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Data; } - } - - public byte[] Data { get; set; } - - public bool IsEof { get; set; } - - public SftpDataResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - protected override void LoadData() - { - base.LoadData(); - - this.Data = this.ReadBinaryString(); - - if (!this.IsEndOfData) - { - this.IsEof = this.ReadBoolean(); - } - } - } -} +namespace Renci.SshNet.Sftp.Responses +{ + internal class SftpDataResponse : SftpResponse + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Data; } + } + + public byte[] Data { get; set; } + + public bool IsEof { get; set; } + + public SftpDataResponse(uint protocolVersion) + : base(protocolVersion) + { + } + + protected override void LoadData() + { + base.LoadData(); + + this.Data = this.ReadBinaryString(); + + if (!this.IsEndOfData) + { + this.IsEof = this.ReadBoolean(); + } + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs b/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs index 56d1e6e..3aa8eba 100644 --- a/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs @@ -1,26 +1,22 @@ -using Renci.SshNet.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpExtendedReplyResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.ExtendedReply; } - } - - public SftpExtendedReplyResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - public T GetReply() where T : SshData, new() - { - return this.OfType(); - } - } -} +using Renci.SshNet.Common; + +namespace Renci.SshNet.Sftp.Responses +{ + internal class SftpExtendedReplyResponse : SftpResponse + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.ExtendedReply; } + } + + public SftpExtendedReplyResponse(uint protocolVersion) + : base(protocolVersion) + { + } + + public T GetReply() where T : SshData, new() + { + return this.OfType(); + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs b/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs index 7dc6b95..15a85e8 100644 --- a/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs @@ -1,29 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpHandleResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Handle; } - } - - public byte[] Handle { get; private set; } - - public SftpHandleResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - protected override void LoadData() - { - base.LoadData(); - - this.Handle = this.ReadBinaryString(); - } - } -} +namespace Renci.SshNet.Sftp.Responses +{ + internal class SftpHandleResponse : SftpResponse + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Handle; } + } + + public byte[] Handle { get; private set; } + + public SftpHandleResponse(uint protocolVersion) + : base(protocolVersion) + { + } + + protected override void LoadData() + { + base.LoadData(); + + this.Handle = this.ReadBinaryString(); + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs b/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs index a1b8c50..7034713 100644 --- a/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs @@ -1,44 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpNameResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Name; } - } - - public uint Count { get; private set; } - - public Encoding Encoding { get; private set; } - - public KeyValuePair[] Files { get; private set; } - - public SftpNameResponse(uint protocolVersion, Encoding encoding) - : base(protocolVersion) - { - this.Files = new KeyValuePair[0]; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - - this.Count = this.ReadUInt32(); - this.Files = new KeyValuePair[this.Count]; - - for (int i = 0; i < this.Count; i++) - { - var fileName = this.ReadString(this.Encoding); - this.ReadString(); // This field value has meaningless information - var attributes = this.ReadAttributes(); - this.Files[i] = new KeyValuePair(fileName, attributes); - } - } - } -} +using System.Collections.Generic; +using System.Text; + +namespace Renci.SshNet.Sftp.Responses +{ + internal class SftpNameResponse : SftpResponse + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Name; } + } + + public uint Count { get; private set; } + + public Encoding Encoding { get; private set; } + + public KeyValuePair[] Files { get; private set; } + + public SftpNameResponse(uint protocolVersion, Encoding encoding) + : base(protocolVersion) + { + this.Files = new KeyValuePair[0]; + this.Encoding = encoding; + } + + protected override void LoadData() + { + base.LoadData(); + + this.Count = this.ReadUInt32(); + this.Files = new KeyValuePair[this.Count]; + + for (int i = 0; i < this.Count; i++) + { + var fileName = this.ReadString(this.Encoding); + this.ReadString(); // This field value has meaningless information + var attributes = this.ReadAttributes(); + this.Files[i] = new KeyValuePair(fileName, attributes); + } + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/SftpResponse.cs b/Renci.SshNet/Sftp/Responses/SftpResponse.cs index b195147..988dfcc 100644 --- a/Renci.SshNet/Sftp/Responses/SftpResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpResponse.cs @@ -1,31 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - public abstract class SftpResponse : SftpMessage - { - public uint ResponseId { get; private set; } - - public uint ProtocolVersion { get; private set; } - - public SftpResponse(uint protocolVersion) - { - this.ProtocolVersion = protocolVersion; - } - - protected override void LoadData() - { - base.LoadData(); - - this.ResponseId = this.ReadUInt32(); - } - - protected override void SaveData() - { - throw new InvalidOperationException("Response cannot be saved."); - } - } -} +using System; + +namespace Renci.SshNet.Sftp.Responses +{ + internal abstract class SftpResponse : SftpMessage + { + public uint ResponseId { get; private set; } + + public uint ProtocolVersion { get; private set; } + + public SftpResponse(uint protocolVersion) + { + this.ProtocolVersion = protocolVersion; + } + + protected override void LoadData() + { + base.LoadData(); + + this.ResponseId = this.ReadUInt32(); + } + + protected override void SaveData() + { + throw new InvalidOperationException("Response cannot be saved."); + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs b/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs index fef01ba..d4a8593 100644 --- a/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs @@ -1,44 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - public class SftpStatusResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Status; } - } - - public SftpStatusResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - public StatusCodes StatusCode { get; private set; } - - public string ErrorMessage { get; private set; } - - public string Language { get; private set; } - - protected override void LoadData() - { - base.LoadData(); - - this.StatusCode = (StatusCodes)this.ReadUInt32(); - - if (this.ProtocolVersion < 3) - { - return; - } - - if (!this.IsEndOfData) - { - this.ErrorMessage = this.ReadString(); - this.Language = this.ReadString(); - } - } - } -} +namespace Renci.SshNet.Sftp.Responses +{ + internal class SftpStatusResponse : SftpResponse + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Status; } + } + + public SftpStatusResponse(uint protocolVersion) + : base(protocolVersion) + { + } + + public StatusCodes StatusCode { get; private set; } + + public string ErrorMessage { get; private set; } + + public string Language { get; private set; } + + protected override void LoadData() + { + base.LoadData(); + + this.StatusCode = (StatusCodes)this.ReadUInt32(); + + if (this.ProtocolVersion < 3) + { + return; + } + + if (!this.IsEndOfData) + { + this.ErrorMessage = this.ReadString(); + this.Language = this.ReadString(); + } + } + } +} diff --git a/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs b/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs index 10f8b6f..e39b312 100644 --- a/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs +++ b/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs @@ -1,32 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpVersionResponse : SftpMessage - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Version; } - } - - public uint Version { get; private set; } - - public IDictionary Extentions { get; private set; } - - protected override void LoadData() - { - base.LoadData(); - this.Version = this.ReadUInt32(); - this.Extentions = this.ReadExtensionPair(); - } - - protected override void SaveData() - { - throw new InvalidOperationException(); - } - - } -} +using System; +using System.Collections.Generic; + +namespace Renci.SshNet.Sftp.Responses +{ + internal class SftpVersionResponse : SftpMessage + { + public override SftpMessageTypes SftpMessageType + { + get { return SftpMessageTypes.Version; } + } + + public uint Version { get; private set; } + + public IDictionary Extentions { get; private set; } + + protected override void LoadData() + { + base.LoadData(); + this.Version = this.ReadUInt32(); + this.Extentions = this.ReadExtensionPair(); + } + + protected override void SaveData() + { + throw new InvalidOperationException(); + } + + } +} diff --git a/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs b/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs index 6a6d36a..8280c87 100644 --- a/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs +++ b/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs @@ -1,24 +1,24 @@ -namespace Renci.SshNet.Sftp.Responses -{ - internal class StatVfsResponse : SftpExtendedReplyResponse - { - public SftpFileSytemInformation Information { get; private set; } - - public StatVfsResponse() - : base(0) - { - } - - protected override void LoadData() - { - base.LoadData(); - - this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64()); - } - } +namespace Renci.SshNet.Sftp.Responses +{ + internal class StatVfsResponse : SftpExtendedReplyResponse + { + public SftpFileSytemInformation Information { get; private set; } + + public StatVfsResponse() + : base(0) + { + } + + protected override void LoadData() + { + base.LoadData(); + + this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64(), this.ReadUInt64(), + this.ReadUInt64()); + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpDataMessage.cs b/Renci.SshNet/Sftp/SftpDataMessage.cs index 31276c6..bd29c79 100644 --- a/Renci.SshNet/Sftp/SftpDataMessage.cs +++ b/Renci.SshNet/Sftp/SftpDataMessage.cs @@ -1,24 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Renci.SshNet.Messages.Connection; - -namespace Renci.SshNet.Sftp -{ - internal class SftpDataMessage : ChannelDataMessage - { - public SftpDataMessage(uint localChannelNumber, SftpMessage sftpMessage) - { - this.LocalChannelNumber = localChannelNumber; - - var messageData = sftpMessage.GetBytes(); - - var data = new byte[4 + messageData.Length]; - - ((uint)messageData.Length).GetBytes().CopyTo(data, 0); - messageData.CopyTo(data, 4); - this.Data = data; - } - - } -} +using Renci.SshNet.Messages.Connection; + +namespace Renci.SshNet.Sftp +{ + internal class SftpDataMessage : ChannelDataMessage + { + public SftpDataMessage(uint localChannelNumber, SftpMessage sftpMessage) + { + this.LocalChannelNumber = localChannelNumber; + + var messageData = sftpMessage.GetBytes(); + + var data = new byte[4 + messageData.Length]; + + ((uint)messageData.Length).GetBytes().CopyTo(data, 0); + messageData.CopyTo(data, 4); + this.Data = data; + } + } +} diff --git a/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs b/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs index d7fc30c..91e98c2 100644 --- a/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs +++ b/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs @@ -1,50 +1,46 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous download operation. - /// - public class SftpDownloadAsyncResult : AsyncResult - { - /// - /// Gets or sets a value indicating whether to cancel asynchronous download operation. - /// - /// - /// true if download operation to be canceled; otherwise, false. - /// - /// - /// Download operation will be canceled after finishing uploading current buffer. - /// - public bool IsDownloadCanceled { get; set; } - - /// - /// Gets the number of downloaded bytes. - /// - public ulong DownloadedBytes { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpDownloadAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - - } - - /// - /// Updates asynchronous operation status information. - /// - /// Number of downloaded bytes. - internal void Update(ulong downloadedBytes) - { - this.DownloadedBytes = downloadedBytes; - } - } -} +using System; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Sftp +{ + /// + /// Encapsulates the results of an asynchronous download operation. + /// + public class SftpDownloadAsyncResult : AsyncResult + { + /// + /// Gets or sets a value indicating whether to cancel asynchronous download operation. + /// + /// + /// true if download operation to be canceled; otherwise, false. + /// + /// + /// Download operation will be canceled after finishing uploading current buffer. + /// + public bool IsDownloadCanceled { get; set; } + + /// + /// Gets the number of downloaded bytes. + /// + public ulong DownloadedBytes { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The async callback. + /// The state. + public SftpDownloadAsyncResult(AsyncCallback asyncCallback, Object state) + : base(asyncCallback, state) + { + } + + /// + /// Updates asynchronous operation status information. + /// + /// Number of downloaded bytes. + internal void Update(ulong downloadedBytes) + { + this.DownloadedBytes = downloadedBytes; + } + } +} diff --git a/Renci.SshNet/Sftp/SftpFile.cs b/Renci.SshNet/Sftp/SftpFile.cs index 094b291..d94c533 100644 --- a/Renci.SshNet/Sftp/SftpFile.cs +++ b/Renci.SshNet/Sftp/SftpFile.cs @@ -1,512 +1,510 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security; -using System.Globalization; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Represents SFTP file information - /// - public class SftpFile - { - private SftpSession _sftpSession; - - /// - /// Gets the file attributes. - /// - public SftpFileAttributes Attributes { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The SFTP session. - /// Full path of the directory or file. - /// Attributes of the directory or file. - /// or is null. - internal SftpFile(SftpSession sftpSession, string fullName, SftpFileAttributes attributes) - { - if (sftpSession == null) - throw new SshConnectionException("Client not connected."); - - if (attributes == null) - throw new ArgumentNullException("attributes"); - - if (fullName == null) - throw new ArgumentNullException("fullName"); - - this._sftpSession = sftpSession; - this.Attributes = attributes; - - this.Name = fullName.Substring(fullName.LastIndexOf('/') + 1); - - this.FullName = fullName; - } - - /// - /// Gets the full path of the directory or file. - /// - public string FullName { get; private set; } - - /// - /// For files, gets the name of the file. For directories, gets the name of the last directory in the hierarchy if a hierarchy exists. - /// Otherwise, the Name property gets the name of the directory. - /// - public string Name { get; private set; } - - /// - /// Gets or sets the time the current file or directory was last accessed. - /// - /// - /// The time that the current file or directory was last accessed. - /// - public DateTime LastAccessTime - { - get - { - return this.Attributes.LastAccessTime; - } - set - { - this.Attributes.LastAccessTime = value; - } - } - - /// - /// Gets or sets the time when the current file or directory was last written to. - /// - /// - /// The time the current file was last written. - /// - public DateTime LastWriteTime - { - get - { - return this.Attributes.LastWriteTime; - } - set - { - this.Attributes.LastWriteTime = value; - } - } - - /// - /// Gets or sets the time, in coordinated universal time (UTC), the current file or directory was last accessed. - /// - /// - /// The time that the current file or directory was last accessed. - /// - public DateTime LastAccessTimeUtc - { - get - { - return this.Attributes.LastAccessTime.ToUniversalTime(); - } - set - { - this.Attributes.LastAccessTime = value.ToLocalTime(); - } - } - - /// - /// Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to. - /// - /// - /// The time the current file was last written. - /// - public DateTime LastWriteTimeUtc - { - get - { - return this.Attributes.LastWriteTime.ToUniversalTime(); - } - set - { - this.Attributes.LastWriteTime = value.ToLocalTime(); - } - } - - /// - /// Gets or sets the size, in bytes, of the current file. - /// - /// - /// The size of the current file in bytes. - /// - public long Length - { - get - { - return this.Attributes.Size; - } - } - - /// - /// Gets or sets file user id. - /// - /// - /// File user id. - /// - public int UserId - { - get - { - return this.Attributes.UserId; - } - set - { - this.Attributes.UserId = value; - } - } - - /// - /// Gets or sets file group id. - /// - /// - /// File group id. - /// - public int GroupId - { - get - { - return this.Attributes.GroupId; - } - set - { - this.Attributes.GroupId = value; - } - } - - /// - /// Gets a value indicating whether file represents a socket. - /// - /// - /// true if file represents a socket; otherwise, false. - /// - public bool IsSocket - { - get - { - return this.Attributes.IsSocket; - } - } - - /// - /// Gets a value indicating whether file represents a symbolic link. - /// - /// - /// true if file represents a symbolic link; otherwise, false. - /// - public bool IsSymbolicLink - { - get - { - return this.Attributes.IsSymbolicLink; - } - } - - /// - /// Gets a value indicating whether file represents a regular file. - /// - /// - /// true if file represents a regular file; otherwise, false. - /// - public bool IsRegularFile - { - get - { - return this.Attributes.IsRegularFile; - } - } - - /// - /// Gets a value indicating whether file represents a block device. - /// - /// - /// true if file represents a block device; otherwise, false. - /// - public bool IsBlockDevice - { - get - { - return this.Attributes.IsBlockDevice; - } - } - - /// - /// Gets a value indicating whether file represents a directory. - /// - /// - /// true if file represents a directory; otherwise, false. - /// - public bool IsDirectory - { - get - { - return this.Attributes.IsDirectory; - } - } - - /// - /// Gets a value indicating whether file represents a character device. - /// - /// - /// true if file represents a character device; otherwise, false. - /// - public bool IsCharacterDevice - { - get - { - return this.Attributes.IsCharacterDevice; - } - } - - /// - /// Gets a value indicating whether file represents a named pipe. - /// - /// - /// true if file represents a named pipe; otherwise, false. - /// - public bool IsNamedPipe - { - get - { - return this.Attributes.IsNamedPipe; - } - } - - /// - /// Gets or sets a value indicating whether the owner can read from this file. - /// - /// - /// true if owner can read from this file; otherwise, false. - /// - public bool OwnerCanRead - { - get - { - return this.Attributes.OwnerCanRead; - } - set - { - this.Attributes.OwnerCanRead = value; - } - } - - /// - /// Gets or sets a value indicating whether the owner can write into this file. - /// - /// - /// true if owner can write into this file; otherwise, false. - /// - public bool OwnerCanWrite - { - get - { - return this.Attributes.OwnerCanWrite; - } - set - { - this.Attributes.OwnerCanWrite = value; - } - } - - /// - /// Gets or sets a value indicating whether the owner can execute this file. - /// - /// - /// true if owner can execute this file; otherwise, false. - /// - public bool OwnerCanExecute - { - get - { - return this.Attributes.OwnerCanExecute; - } - set - { - this.Attributes.OwnerCanExecute = value; - } - } - - /// - /// Gets or sets a value indicating whether the group members can read from this file. - /// - /// - /// true if group members can read from this file; otherwise, false. - /// - public bool GroupCanRead - { - get - { - return this.Attributes.GroupCanRead; - } - set - { - this.Attributes.GroupCanRead = value; - } - } - - /// - /// Gets or sets a value indicating whether the group members can write into this file. - /// - /// - /// true if group members can write into this file; otherwise, false. - /// - public bool GroupCanWrite - { - get - { - return this.Attributes.GroupCanWrite; - } - set - { - this.Attributes.GroupCanWrite = value; - } - } - - /// - /// Gets or sets a value indicating whether the group members can execute this file. - /// - /// - /// true if group members can execute this file; otherwise, false. - /// - public bool GroupCanExecute - { - get - { - return this.Attributes.GroupCanExecute; - } - set - { - this.Attributes.GroupCanExecute = value; - } - } - - /// - /// Gets or sets a value indicating whether the others can read from this file. - /// - /// - /// true if others can read from this file; otherwise, false. - /// - public bool OthersCanRead - { - get - { - return this.Attributes.OthersCanRead; - } - set - { - this.Attributes.OthersCanRead = value; - } - } - - /// - /// Gets or sets a value indicating whether the others can write into this file. - /// - /// - /// true if others can write into this file; otherwise, false. - /// - public bool OthersCanWrite - { - get - { - return this.Attributes.OthersCanWrite; - } - set - { - this.Attributes.OthersCanWrite = value; - } - } - - /// - /// Gets or sets a value indicating whether the others can execute this file. - /// - /// - /// true if others can execute this file; otherwise, false. - /// - public bool OthersCanExecute - { - get - { - return this.Attributes.OthersCanExecute; - } - set - { - this.Attributes.OthersCanExecute = value; - } - } - - /// - /// Gets the extension part of the file. - /// - /// - /// File extensions. - /// - public IDictionary Extensions { get; private set; } - - /// - /// Sets file permissions. - /// - /// The mode. - public void SetPermissions(short mode) - { - this.Attributes.SetPermissions(mode); - - this.UpdateStatus(); - } - - /// - /// Permanently deletes a file on remote machine. - /// - public void Delete() - { - if (this.IsDirectory) - { - this._sftpSession.RequestRmDir(this.FullName); - } - else - { - this._sftpSession.RequestRemove(this.FullName); - } - } - - /// - /// Moves a specified file to a new location on remote machine, providing the option to specify a new file name. - /// - /// The path to move the file to, which can specify a different file name. - /// is null. - public void MoveTo(string destFileName) - { - if (destFileName == null) - throw new ArgumentNullException("destFileName"); - this._sftpSession.RequestRename(this.FullName, destFileName); - - var fullPath = this._sftpSession.GetCanonicalPath(destFileName); - - this.Name = fullPath.Substring(fullPath.LastIndexOf('/') + 1); - - this.FullName = fullPath; - } - - /// - /// Updates file status on the server. - /// - public void UpdateStatus() - { - this._sftpSession.RequestSetStat(this.FullName, this.Attributes); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "Name {0}, Length {1}, User ID {2}, Group ID {3}, Accessed {4}, Modified {5}", this.Name, this.Length, this.UserId, this.GroupId, this.LastAccessTime, this.LastWriteTime); - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Sftp +{ + /// + /// Represents SFTP file information + /// + public class SftpFile + { + private readonly SftpSession _sftpSession; + + /// + /// Gets the file attributes. + /// + public SftpFileAttributes Attributes { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The SFTP session. + /// Full path of the directory or file. + /// Attributes of the directory or file. + /// or is null. + internal SftpFile(SftpSession sftpSession, string fullName, SftpFileAttributes attributes) + { + if (sftpSession == null) + throw new SshConnectionException("Client not connected."); + + if (attributes == null) + throw new ArgumentNullException("attributes"); + + if (fullName == null) + throw new ArgumentNullException("fullName"); + + this._sftpSession = sftpSession; + this.Attributes = attributes; + + this.Name = fullName.Substring(fullName.LastIndexOf('/') + 1); + + this.FullName = fullName; + } + + /// + /// Gets the full path of the directory or file. + /// + public string FullName { get; private set; } + + /// + /// For files, gets the name of the file. For directories, gets the name of the last directory in the hierarchy if a hierarchy exists. + /// Otherwise, the Name property gets the name of the directory. + /// + public string Name { get; private set; } + + /// + /// Gets or sets the time the current file or directory was last accessed. + /// + /// + /// The time that the current file or directory was last accessed. + /// + public DateTime LastAccessTime + { + get + { + return this.Attributes.LastAccessTime; + } + set + { + this.Attributes.LastAccessTime = value; + } + } + + /// + /// Gets or sets the time when the current file or directory was last written to. + /// + /// + /// The time the current file was last written. + /// + public DateTime LastWriteTime + { + get + { + return this.Attributes.LastWriteTime; + } + set + { + this.Attributes.LastWriteTime = value; + } + } + + /// + /// Gets or sets the time, in coordinated universal time (UTC), the current file or directory was last accessed. + /// + /// + /// The time that the current file or directory was last accessed. + /// + public DateTime LastAccessTimeUtc + { + get + { + return this.Attributes.LastAccessTime.ToUniversalTime(); + } + set + { + this.Attributes.LastAccessTime = value.ToLocalTime(); + } + } + + /// + /// Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to. + /// + /// + /// The time the current file was last written. + /// + public DateTime LastWriteTimeUtc + { + get + { + return this.Attributes.LastWriteTime.ToUniversalTime(); + } + set + { + this.Attributes.LastWriteTime = value.ToLocalTime(); + } + } + + /// + /// Gets or sets the size, in bytes, of the current file. + /// + /// + /// The size of the current file in bytes. + /// + public long Length + { + get + { + return this.Attributes.Size; + } + } + + /// + /// Gets or sets file user id. + /// + /// + /// File user id. + /// + public int UserId + { + get + { + return this.Attributes.UserId; + } + set + { + this.Attributes.UserId = value; + } + } + + /// + /// Gets or sets file group id. + /// + /// + /// File group id. + /// + public int GroupId + { + get + { + return this.Attributes.GroupId; + } + set + { + this.Attributes.GroupId = value; + } + } + + /// + /// Gets a value indicating whether file represents a socket. + /// + /// + /// true if file represents a socket; otherwise, false. + /// + public bool IsSocket + { + get + { + return this.Attributes.IsSocket; + } + } + + /// + /// Gets a value indicating whether file represents a symbolic link. + /// + /// + /// true if file represents a symbolic link; otherwise, false. + /// + public bool IsSymbolicLink + { + get + { + return this.Attributes.IsSymbolicLink; + } + } + + /// + /// Gets a value indicating whether file represents a regular file. + /// + /// + /// true if file represents a regular file; otherwise, false. + /// + public bool IsRegularFile + { + get + { + return this.Attributes.IsRegularFile; + } + } + + /// + /// Gets a value indicating whether file represents a block device. + /// + /// + /// true if file represents a block device; otherwise, false. + /// + public bool IsBlockDevice + { + get + { + return this.Attributes.IsBlockDevice; + } + } + + /// + /// Gets a value indicating whether file represents a directory. + /// + /// + /// true if file represents a directory; otherwise, false. + /// + public bool IsDirectory + { + get + { + return this.Attributes.IsDirectory; + } + } + + /// + /// Gets a value indicating whether file represents a character device. + /// + /// + /// true if file represents a character device; otherwise, false. + /// + public bool IsCharacterDevice + { + get + { + return this.Attributes.IsCharacterDevice; + } + } + + /// + /// Gets a value indicating whether file represents a named pipe. + /// + /// + /// true if file represents a named pipe; otherwise, false. + /// + public bool IsNamedPipe + { + get + { + return this.Attributes.IsNamedPipe; + } + } + + /// + /// Gets or sets a value indicating whether the owner can read from this file. + /// + /// + /// true if owner can read from this file; otherwise, false. + /// + public bool OwnerCanRead + { + get + { + return this.Attributes.OwnerCanRead; + } + set + { + this.Attributes.OwnerCanRead = value; + } + } + + /// + /// Gets or sets a value indicating whether the owner can write into this file. + /// + /// + /// true if owner can write into this file; otherwise, false. + /// + public bool OwnerCanWrite + { + get + { + return this.Attributes.OwnerCanWrite; + } + set + { + this.Attributes.OwnerCanWrite = value; + } + } + + /// + /// Gets or sets a value indicating whether the owner can execute this file. + /// + /// + /// true if owner can execute this file; otherwise, false. + /// + public bool OwnerCanExecute + { + get + { + return this.Attributes.OwnerCanExecute; + } + set + { + this.Attributes.OwnerCanExecute = value; + } + } + + /// + /// Gets or sets a value indicating whether the group members can read from this file. + /// + /// + /// true if group members can read from this file; otherwise, false. + /// + public bool GroupCanRead + { + get + { + return this.Attributes.GroupCanRead; + } + set + { + this.Attributes.GroupCanRead = value; + } + } + + /// + /// Gets or sets a value indicating whether the group members can write into this file. + /// + /// + /// true if group members can write into this file; otherwise, false. + /// + public bool GroupCanWrite + { + get + { + return this.Attributes.GroupCanWrite; + } + set + { + this.Attributes.GroupCanWrite = value; + } + } + + /// + /// Gets or sets a value indicating whether the group members can execute this file. + /// + /// + /// true if group members can execute this file; otherwise, false. + /// + public bool GroupCanExecute + { + get + { + return this.Attributes.GroupCanExecute; + } + set + { + this.Attributes.GroupCanExecute = value; + } + } + + /// + /// Gets or sets a value indicating whether the others can read from this file. + /// + /// + /// true if others can read from this file; otherwise, false. + /// + public bool OthersCanRead + { + get + { + return this.Attributes.OthersCanRead; + } + set + { + this.Attributes.OthersCanRead = value; + } + } + + /// + /// Gets or sets a value indicating whether the others can write into this file. + /// + /// + /// true if others can write into this file; otherwise, false. + /// + public bool OthersCanWrite + { + get + { + return this.Attributes.OthersCanWrite; + } + set + { + this.Attributes.OthersCanWrite = value; + } + } + + /// + /// Gets or sets a value indicating whether the others can execute this file. + /// + /// + /// true if others can execute this file; otherwise, false. + /// + public bool OthersCanExecute + { + get + { + return this.Attributes.OthersCanExecute; + } + set + { + this.Attributes.OthersCanExecute = value; + } + } + + /// + /// Gets the extension part of the file. + /// + /// + /// File extensions. + /// + public IDictionary Extensions { get; private set; } + + /// + /// Sets file permissions. + /// + /// The mode. + public void SetPermissions(short mode) + { + this.Attributes.SetPermissions(mode); + + this.UpdateStatus(); + } + + /// + /// Permanently deletes a file on remote machine. + /// + public void Delete() + { + if (this.IsDirectory) + { + this._sftpSession.RequestRmDir(this.FullName); + } + else + { + this._sftpSession.RequestRemove(this.FullName); + } + } + + /// + /// Moves a specified file to a new location on remote machine, providing the option to specify a new file name. + /// + /// The path to move the file to, which can specify a different file name. + /// is null. + public void MoveTo(string destFileName) + { + if (destFileName == null) + throw new ArgumentNullException("destFileName"); + this._sftpSession.RequestRename(this.FullName, destFileName); + + var fullPath = this._sftpSession.GetCanonicalPath(destFileName); + + this.Name = fullPath.Substring(fullPath.LastIndexOf('/') + 1); + + this.FullName = fullPath; + } + + /// + /// Updates file status on the server. + /// + public void UpdateStatus() + { + this._sftpSession.RequestSetStat(this.FullName, this.Attributes); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "Name {0}, Length {1}, User ID {2}, Group ID {3}, Accessed {4}, Modified {5}", this.Name, this.Length, this.UserId, this.GroupId, this.LastAccessTime, this.LastWriteTime); + } + } +} diff --git a/Renci.SshNet/Sftp/SftpFileAttributes.cs b/Renci.SshNet/Sftp/SftpFileAttributes.cs index da9a73d..1d40ab0 100644 --- a/Renci.SshNet/Sftp/SftpFileAttributes.cs +++ b/Renci.SshNet/Sftp/SftpFileAttributes.cs @@ -1,437 +1,436 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; - -namespace Renci.SshNet.Sftp -{ - /// - /// Contains SFTP file attributes. - /// - public class SftpFileAttributes - { - #region Bitmask constats - - private static UInt32 S_IFMT = 0xF000; // bitmask for the file type bitfields - - private static UInt32 S_IFSOCK = 0xC000; // socket - - private static UInt32 S_IFLNK = 0xA000; // symbolic link - - private static UInt32 S_IFREG = 0x8000; // regular file - - private static UInt32 S_IFBLK = 0x6000; // block device - - private static UInt32 S_IFDIR = 0x4000; // directory - - private static UInt32 S_IFCHR = 0x2000; // character device - - private static UInt32 S_IFIFO = 0x1000; // FIFO - - private static UInt32 S_ISUID = 0x0800; // set UID bit - - private static UInt32 S_ISGID = 0x0400; // set-group-ID bit (see below) - - private static UInt32 S_ISVTX = 0x0200; // sticky bit (see below) - - private static UInt32 S_IRUSR = 0x0100; // owner has read permission - - private static UInt32 S_IWUSR = 0x0080; // owner has write permission - - private static UInt32 S_IXUSR = 0x0040; // owner has execute permission - - private static UInt32 S_IRGRP = 0x0020; // group has read permission - - private static UInt32 S_IWGRP = 0x0010; // group has write permission - - private static UInt32 S_IXGRP = 0x0008; // group has execute permission - - private static UInt32 S_IROTH = 0x0004; // others have read permission - - private static UInt32 S_IWOTH = 0x0002; // others have write permission - - private static UInt32 S_IXOTH = 0x0001; // others have execute permission - - #endregion - - private bool _isBitFiledsBitSet; - private bool _isUIDBitSet; - private bool _isGroupIDBitSet; - private bool _isStickyBitSet; - - private DateTime _originalLastAccessTime; - private DateTime _originalLastWriteTime; - private long _originalSize; - private int _originalUserId; - private int _originalGroupId; - private uint _originalPermissions; - private IDictionary _originalExtensions; - - internal bool IsLastAccessTimeChanged - { - get { return this._originalLastAccessTime != this.LastAccessTime; } - } - - internal bool IsLastWriteTimeChanged - { - get { return this._originalLastWriteTime != this.LastWriteTime; } - } - - internal bool IsSizeChanged - { - get { return this._originalSize != this.Size; } - } - - internal bool IsUserIdChanged - { - get { return this._originalUserId != this.UserId; } - } - - internal bool IsGroupIdChanged - { - get { return this._originalGroupId != this.GroupId; } - } - - internal bool IsPermissionsChanged - { - get { return this._originalPermissions != this.Permissions; } - } - - internal bool IsExtensionsChanged - { - get { return this._originalExtensions != null && this.Extensions != null && !this._originalExtensions.SequenceEqual(this.Extensions); } - } - - /// - /// Gets or sets the time the current file or directory was last accessed. - /// - /// - /// The time that the current file or directory was last accessed. - /// - public DateTime LastAccessTime { get; set; } - - /// - /// Gets or sets the time when the current file or directory was last written to. - /// - /// - /// The time the current file was last written. - /// - public DateTime LastWriteTime { get; set; } - - /// - /// Gets or sets the size, in bytes, of the current file. - /// - /// - /// The size of the current file in bytes. - /// - public long Size { get; set; } - - /// - /// Gets or sets file user id. - /// - /// - /// File user id. - /// - public int UserId { get; set; } - - /// - /// Gets or sets file group id. - /// - /// - /// File group id. - /// - public int GroupId { get; set; } - - /// - /// Gets a value indicating whether file represents a socket. - /// - /// - /// true if file represents a socket; otherwise, false. - /// - public bool IsSocket { get; private set; } - - /// - /// Gets a value indicating whether file represents a symbolic link. - /// - /// - /// true if file represents a symbolic link; otherwise, false. - /// - public bool IsSymbolicLink { get; private set; } - - /// - /// Gets a value indicating whether file represents a regular file. - /// - /// - /// true if file represents a regular file; otherwise, false. - /// - public bool IsRegularFile { get; private set; } - - /// - /// Gets a value indicating whether file represents a block device. - /// - /// - /// true if file represents a block device; otherwise, false. - /// - public bool IsBlockDevice { get; private set; } - - /// - /// Gets a value indicating whether file represents a directory. - /// - /// - /// true if file represents a directory; otherwise, false. - /// - public bool IsDirectory { get; private set; } - - /// - /// Gets a value indicating whether file represents a character device. - /// - /// - /// true if file represents a character device; otherwise, false. - /// - public bool IsCharacterDevice { get; private set; } - - /// - /// Gets a value indicating whether file represents a named pipe. - /// - /// - /// true if file represents a named pipe; otherwise, false. - /// - public bool IsNamedPipe { get; private set; } - - /// - /// Gets a value indicating whether the owner can read from this file. - /// - /// - /// true if owner can read from this file; otherwise, false. - /// - public bool OwnerCanRead { get; set; } - - /// - /// Gets a value indicating whether the owner can write into this file. - /// - /// - /// true if owner can write into this file; otherwise, false. - /// - public bool OwnerCanWrite { get; set; } - - /// - /// Gets a value indicating whether the owner can execute this file. - /// - /// - /// true if owner can execute this file; otherwise, false. - /// - public bool OwnerCanExecute { get; set; } - - /// - /// Gets a value indicating whether the group members can read from this file. - /// - /// - /// true if group members can read from this file; otherwise, false. - /// - public bool GroupCanRead { get; set; } - - /// - /// Gets a value indicating whether the group members can write into this file. - /// - /// - /// true if group members can write into this file; otherwise, false. - /// - public bool GroupCanWrite { get; set; } - - /// - /// Gets a value indicating whether the group members can execute this file. - /// - /// - /// true if group members can execute this file; otherwise, false. - /// - public bool GroupCanExecute { get; set; } - - /// - /// Gets a value indicating whether the others can read from this file. - /// - /// - /// true if others can read from this file; otherwise, false. - /// - public bool OthersCanRead { get; set; } - - /// - /// Gets a value indicating whether the others can write into this file. - /// - /// - /// true if others can write into this file; otherwise, false. - /// - public bool OthersCanWrite { get; set; } - - /// - /// Gets a value indicating whether the others can execute this file. - /// - /// - /// true if others can execute this file; otherwise, false. - /// - public bool OthersCanExecute { get; set; } - - /// - /// Gets or sets the extensions. - /// - /// - /// The extensions. - /// - public IDictionary Extensions { get; private set; } - - internal uint Permissions - { - get - { - uint permission = 0; - - if (this._isBitFiledsBitSet) - permission = permission | S_IFMT; - - if (this.IsSocket) - permission = permission | S_IFSOCK; - - if (this.IsSymbolicLink) - permission = permission | S_IFLNK; - - if (this.IsRegularFile) - permission = permission | S_IFREG; - - if (this.IsBlockDevice) - permission = permission | S_IFBLK; - - if (this.IsDirectory) - permission = permission | S_IFDIR; - - if (this.IsCharacterDevice) - permission = permission | S_IFCHR; - - if (this.IsNamedPipe) - permission = permission | S_IFIFO; - - if (this._isUIDBitSet) - permission = permission | S_ISUID; - - if (this._isGroupIDBitSet) - permission = permission | S_ISGID; - - if (this._isStickyBitSet) - permission = permission | S_ISVTX; - - if (this.OwnerCanRead) - permission = permission | S_IRUSR; - - if (this.OwnerCanWrite) - permission = permission | S_IWUSR; - - if (this.OwnerCanExecute) - permission = permission | S_IXUSR; - - if (this.GroupCanRead) - permission = permission | S_IRGRP; - - if (this.GroupCanWrite) - permission = permission | S_IWGRP; - - if (this.GroupCanExecute) - permission = permission | S_IXGRP; - - if (this.OthersCanRead) - permission = permission | S_IROTH; - - if (this.OthersCanWrite) - permission = permission | S_IWOTH; - - if (this.OthersCanExecute) - permission = permission | S_IXOTH; - - return permission; - } - private set - { - this._isBitFiledsBitSet = ((value & S_IFMT) == S_IFMT); - - this.IsSocket = ((value & S_IFSOCK) == S_IFSOCK); - - this.IsSymbolicLink = ((value & S_IFLNK) == S_IFLNK); - - this.IsRegularFile = ((value & S_IFREG) == S_IFREG); - - this.IsBlockDevice = ((value & S_IFBLK) == S_IFBLK); - - this.IsDirectory = ((value & S_IFDIR) == S_IFDIR); - - this.IsCharacterDevice = ((value & S_IFCHR) == S_IFCHR); - - this.IsNamedPipe = ((value & S_IFIFO) == S_IFIFO); - - this._isUIDBitSet = ((value & S_ISUID) == S_ISUID); - - this._isGroupIDBitSet = ((value & S_ISGID) == S_ISGID); - - this._isStickyBitSet = ((value & S_ISVTX) == S_ISVTX); - - this.OwnerCanRead = ((value & S_IRUSR) == S_IRUSR); - - this.OwnerCanWrite = ((value & S_IWUSR) == S_IWUSR); - - this.OwnerCanExecute = ((value & S_IXUSR) == S_IXUSR); - - this.GroupCanRead = ((value & S_IRGRP) == S_IRGRP); - - this.GroupCanWrite = ((value & S_IWGRP) == S_IWGRP); - - this.GroupCanExecute = ((value & S_IXGRP) == S_IXGRP); - - this.OthersCanRead = ((value & S_IROTH) == S_IROTH); - - this.OthersCanWrite = ((value & S_IWOTH) == S_IWOTH); - - this.OthersCanExecute = ((value & S_IXOTH) == S_IXOTH); - } - } - - internal SftpFileAttributes() - { - } - - public SftpFileAttributes(DateTime lastAccessTime, DateTime lastWriteTime, long size, int userId, int groupId, uint permissions, IDictionary extensions) - { - this.LastAccessTime = this._originalLastAccessTime = lastAccessTime; - this.LastWriteTime = this._originalLastWriteTime = lastWriteTime; - this.Size = this._originalSize = size; - this.UserId = this._originalUserId = userId; - this.GroupId = this._originalGroupId = groupId; - this.Permissions = this._originalPermissions = permissions; - this.Extensions = this._originalExtensions = extensions; - } - - /// - /// Sets the permissions. - /// - /// The mode. - public void SetPermissions(short mode) - { - if (mode < 0 || mode > 999) - { - throw new ArgumentOutOfRangeException("mode"); - } - - var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToArray(); - - var permission = (modeBytes[0] & 0x0F) * 8 * 8 + (modeBytes[1] & 0x0F) * 8 + (modeBytes[2] & 0x0F); - - this.OwnerCanRead = (permission & S_IRUSR) == S_IRUSR; - this.OwnerCanWrite = (permission & S_IWUSR) == S_IWUSR; - this.OwnerCanExecute = (permission & S_IXUSR) == S_IXUSR; - - this.GroupCanRead = (permission & S_IRGRP) == S_IRGRP; - this.GroupCanWrite = (permission & S_IWGRP) == S_IWGRP; - this.GroupCanExecute = (permission & S_IXGRP) == S_IXGRP; - - this.OthersCanRead = (permission & S_IROTH) == S_IROTH; - this.OthersCanWrite = (permission & S_IWOTH) == S_IWOTH; - this.OthersCanExecute = (permission & S_IXOTH) == S_IXOTH; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Globalization; + +namespace Renci.SshNet.Sftp +{ + /// + /// Contains SFTP file attributes. + /// + public class SftpFileAttributes + { + #region Bitmask constats + + private const UInt32 S_IFMT = 0xF000; // bitmask for the file type bitfields + + private const UInt32 S_IFSOCK = 0xC000; // socket + + private const UInt32 S_IFLNK = 0xA000; // symbolic link + + private const UInt32 S_IFREG = 0x8000; // regular file + + private const UInt32 S_IFBLK = 0x6000; // block device + + private const UInt32 S_IFDIR = 0x4000; // directory + + private const UInt32 S_IFCHR = 0x2000; // character device + + private const UInt32 S_IFIFO = 0x1000; // FIFO + + private const UInt32 S_ISUID = 0x0800; // set UID bit + + private const UInt32 S_ISGID = 0x0400; // set-group-ID bit (see below) + + private const UInt32 S_ISVTX = 0x0200; // sticky bit (see below) + + private const UInt32 S_IRUSR = 0x0100; // owner has read permission + + private const UInt32 S_IWUSR = 0x0080; // owner has write permission + + private const UInt32 S_IXUSR = 0x0040; // owner has execute permission + + private const UInt32 S_IRGRP = 0x0020; // group has read permission + + private const UInt32 S_IWGRP = 0x0010; // group has write permission + + private const UInt32 S_IXGRP = 0x0008; // group has execute permission + + private const UInt32 S_IROTH = 0x0004; // others have read permission + + private const UInt32 S_IWOTH = 0x0002; // others have write permission + + private const UInt32 S_IXOTH = 0x0001; // others have execute permission + + #endregion + + private bool _isBitFiledsBitSet; + private bool _isUIDBitSet; + private bool _isGroupIDBitSet; + private bool _isStickyBitSet; + + private readonly DateTime _originalLastAccessTime; + private readonly DateTime _originalLastWriteTime; + private readonly long _originalSize; + private readonly int _originalUserId; + private readonly int _originalGroupId; + private readonly uint _originalPermissions; + private readonly IDictionary _originalExtensions; + + internal bool IsLastAccessTimeChanged + { + get { return this._originalLastAccessTime != this.LastAccessTime; } + } + + internal bool IsLastWriteTimeChanged + { + get { return this._originalLastWriteTime != this.LastWriteTime; } + } + + internal bool IsSizeChanged + { + get { return this._originalSize != this.Size; } + } + + internal bool IsUserIdChanged + { + get { return this._originalUserId != this.UserId; } + } + + internal bool IsGroupIdChanged + { + get { return this._originalGroupId != this.GroupId; } + } + + internal bool IsPermissionsChanged + { + get { return this._originalPermissions != this.Permissions; } + } + + internal bool IsExtensionsChanged + { + get { return this._originalExtensions != null && this.Extensions != null && !this._originalExtensions.SequenceEqual(this.Extensions); } + } + + /// + /// Gets or sets the time the current file or directory was last accessed. + /// + /// + /// The time that the current file or directory was last accessed. + /// + public DateTime LastAccessTime { get; set; } + + /// + /// Gets or sets the time when the current file or directory was last written to. + /// + /// + /// The time the current file was last written. + /// + public DateTime LastWriteTime { get; set; } + + /// + /// Gets or sets the size, in bytes, of the current file. + /// + /// + /// The size of the current file in bytes. + /// + public long Size { get; set; } + + /// + /// Gets or sets file user id. + /// + /// + /// File user id. + /// + public int UserId { get; set; } + + /// + /// Gets or sets file group id. + /// + /// + /// File group id. + /// + public int GroupId { get; set; } + + /// + /// Gets a value indicating whether file represents a socket. + /// + /// + /// true if file represents a socket; otherwise, false. + /// + public bool IsSocket { get; private set; } + + /// + /// Gets a value indicating whether file represents a symbolic link. + /// + /// + /// true if file represents a symbolic link; otherwise, false. + /// + public bool IsSymbolicLink { get; private set; } + + /// + /// Gets a value indicating whether file represents a regular file. + /// + /// + /// true if file represents a regular file; otherwise, false. + /// + public bool IsRegularFile { get; private set; } + + /// + /// Gets a value indicating whether file represents a block device. + /// + /// + /// true if file represents a block device; otherwise, false. + /// + public bool IsBlockDevice { get; private set; } + + /// + /// Gets a value indicating whether file represents a directory. + /// + /// + /// true if file represents a directory; otherwise, false. + /// + public bool IsDirectory { get; private set; } + + /// + /// Gets a value indicating whether file represents a character device. + /// + /// + /// true if file represents a character device; otherwise, false. + /// + public bool IsCharacterDevice { get; private set; } + + /// + /// Gets a value indicating whether file represents a named pipe. + /// + /// + /// true if file represents a named pipe; otherwise, false. + /// + public bool IsNamedPipe { get; private set; } + + /// + /// Gets a value indicating whether the owner can read from this file. + /// + /// + /// true if owner can read from this file; otherwise, false. + /// + public bool OwnerCanRead { get; set; } + + /// + /// Gets a value indicating whether the owner can write into this file. + /// + /// + /// true if owner can write into this file; otherwise, false. + /// + public bool OwnerCanWrite { get; set; } + + /// + /// Gets a value indicating whether the owner can execute this file. + /// + /// + /// true if owner can execute this file; otherwise, false. + /// + public bool OwnerCanExecute { get; set; } + + /// + /// Gets a value indicating whether the group members can read from this file. + /// + /// + /// true if group members can read from this file; otherwise, false. + /// + public bool GroupCanRead { get; set; } + + /// + /// Gets a value indicating whether the group members can write into this file. + /// + /// + /// true if group members can write into this file; otherwise, false. + /// + public bool GroupCanWrite { get; set; } + + /// + /// Gets a value indicating whether the group members can execute this file. + /// + /// + /// true if group members can execute this file; otherwise, false. + /// + public bool GroupCanExecute { get; set; } + + /// + /// Gets a value indicating whether the others can read from this file. + /// + /// + /// true if others can read from this file; otherwise, false. + /// + public bool OthersCanRead { get; set; } + + /// + /// Gets a value indicating whether the others can write into this file. + /// + /// + /// true if others can write into this file; otherwise, false. + /// + public bool OthersCanWrite { get; set; } + + /// + /// Gets a value indicating whether the others can execute this file. + /// + /// + /// true if others can execute this file; otherwise, false. + /// + public bool OthersCanExecute { get; set; } + + /// + /// Gets or sets the extensions. + /// + /// + /// The extensions. + /// + public IDictionary Extensions { get; private set; } + + internal uint Permissions + { + get + { + uint permission = 0; + + if (this._isBitFiledsBitSet) + permission = permission | S_IFMT; + + if (this.IsSocket) + permission = permission | S_IFSOCK; + + if (this.IsSymbolicLink) + permission = permission | S_IFLNK; + + if (this.IsRegularFile) + permission = permission | S_IFREG; + + if (this.IsBlockDevice) + permission = permission | S_IFBLK; + + if (this.IsDirectory) + permission = permission | S_IFDIR; + + if (this.IsCharacterDevice) + permission = permission | S_IFCHR; + + if (this.IsNamedPipe) + permission = permission | S_IFIFO; + + if (this._isUIDBitSet) + permission = permission | S_ISUID; + + if (this._isGroupIDBitSet) + permission = permission | S_ISGID; + + if (this._isStickyBitSet) + permission = permission | S_ISVTX; + + if (this.OwnerCanRead) + permission = permission | S_IRUSR; + + if (this.OwnerCanWrite) + permission = permission | S_IWUSR; + + if (this.OwnerCanExecute) + permission = permission | S_IXUSR; + + if (this.GroupCanRead) + permission = permission | S_IRGRP; + + if (this.GroupCanWrite) + permission = permission | S_IWGRP; + + if (this.GroupCanExecute) + permission = permission | S_IXGRP; + + if (this.OthersCanRead) + permission = permission | S_IROTH; + + if (this.OthersCanWrite) + permission = permission | S_IWOTH; + + if (this.OthersCanExecute) + permission = permission | S_IXOTH; + + return permission; + } + private set + { + this._isBitFiledsBitSet = ((value & S_IFMT) == S_IFMT); + + this.IsSocket = ((value & S_IFSOCK) == S_IFSOCK); + + this.IsSymbolicLink = ((value & S_IFLNK) == S_IFLNK); + + this.IsRegularFile = ((value & S_IFREG) == S_IFREG); + + this.IsBlockDevice = ((value & S_IFBLK) == S_IFBLK); + + this.IsDirectory = ((value & S_IFDIR) == S_IFDIR); + + this.IsCharacterDevice = ((value & S_IFCHR) == S_IFCHR); + + this.IsNamedPipe = ((value & S_IFIFO) == S_IFIFO); + + this._isUIDBitSet = ((value & S_ISUID) == S_ISUID); + + this._isGroupIDBitSet = ((value & S_ISGID) == S_ISGID); + + this._isStickyBitSet = ((value & S_ISVTX) == S_ISVTX); + + this.OwnerCanRead = ((value & S_IRUSR) == S_IRUSR); + + this.OwnerCanWrite = ((value & S_IWUSR) == S_IWUSR); + + this.OwnerCanExecute = ((value & S_IXUSR) == S_IXUSR); + + this.GroupCanRead = ((value & S_IRGRP) == S_IRGRP); + + this.GroupCanWrite = ((value & S_IWGRP) == S_IWGRP); + + this.GroupCanExecute = ((value & S_IXGRP) == S_IXGRP); + + this.OthersCanRead = ((value & S_IROTH) == S_IROTH); + + this.OthersCanWrite = ((value & S_IWOTH) == S_IWOTH); + + this.OthersCanExecute = ((value & S_IXOTH) == S_IXOTH); + } + } + + internal SftpFileAttributes() + { + } + + internal SftpFileAttributes(DateTime lastAccessTime, DateTime lastWriteTime, long size, int userId, int groupId, uint permissions, IDictionary extensions) + { + this.LastAccessTime = this._originalLastAccessTime = lastAccessTime; + this.LastWriteTime = this._originalLastWriteTime = lastWriteTime; + this.Size = this._originalSize = size; + this.UserId = this._originalUserId = userId; + this.GroupId = this._originalGroupId = groupId; + this.Permissions = this._originalPermissions = permissions; + this.Extensions = this._originalExtensions = extensions; + } + + /// + /// Sets the permissions. + /// + /// The mode. + public void SetPermissions(short mode) + { + if (mode < 0 || mode > 999) + { + throw new ArgumentOutOfRangeException("mode"); + } + + var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToArray(); + + var permission = (modeBytes[0] & 0x0F) * 8 * 8 + (modeBytes[1] & 0x0F) * 8 + (modeBytes[2] & 0x0F); + + this.OwnerCanRead = (permission & S_IRUSR) == S_IRUSR; + this.OwnerCanWrite = (permission & S_IWUSR) == S_IWUSR; + this.OwnerCanExecute = (permission & S_IXUSR) == S_IXUSR; + + this.GroupCanRead = (permission & S_IRGRP) == S_IRGRP; + this.GroupCanWrite = (permission & S_IWGRP) == S_IWGRP; + this.GroupCanExecute = (permission & S_IXGRP) == S_IXGRP; + + this.OthersCanRead = (permission & S_IROTH) == S_IROTH; + this.OthersCanWrite = (permission & S_IWOTH) == S_IWOTH; + this.OthersCanExecute = (permission & S_IXOTH) == S_IXOTH; + } + } +} diff --git a/Renci.SshNet/Sftp/SftpFileStream.cs b/Renci.SshNet/Sftp/SftpFileStream.cs index c493d7a..c8bd83d 100644 --- a/Renci.SshNet/Sftp/SftpFileStream.cs +++ b/Renci.SshNet/Sftp/SftpFileStream.cs @@ -1,1034 +1,899 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using System.Threading; -using System.Diagnostics.CodeAnalysis; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Exposes a System.IO.Stream around a remote SFTP file, supporting both synchronous and asynchronous read and write operations. - /// - public class SftpFileStream : Stream - { - // TODO: Add security method to set userid, groupid and other permission settings - // Internal state. - private byte[] _handle; - private FileAccess _access; - private bool _ownsHandle; - private bool _isAsync; - private string _path; - private SftpSession _session; - - // Buffer information. - private int _bufferSize; - private byte[] _buffer; - private int _bufferPosn; - private int _bufferLen; - private long _position; - private bool _bufferOwnedByWrite; - private bool _canSeek; - private ulong _serverFilePosition; - - private SftpFileAttributes _attributes; - - private object _lock = new object(); - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get - { - return ((this._access & FileAccess.Read) != 0); - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get - { - return this._canSeek; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get - { - return ((this._access & FileAccess.Write) != 0); - } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// A long value representing the length of the stream in bytes. - /// A class derived from Stream does not support seeking. - /// Methods were called after the stream was closed. - /// IO operation failed. - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Be design this is the exception that stream need to throw.")] - public override long Length - { - get - { - // Validate that the object can actually do this. - if (!this._canSeek) - { - throw new NotSupportedException("Seek operation is not supported."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - if (this._handle == null) - { - // ECMA says this should be IOException even though - // everywhere else uses ObjectDisposedException. - throw new IOException("Stream is closed."); - } - - // Flush the write buffer, because it may - // affect the length of the stream. - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - } - - // Update file attributes - this._attributes = this._session.RequestFStat(this._handle); - - if (this._attributes != null && this._attributes.Size > -1) - { - return this._attributes.Size; - } - else - { - throw new IOException("Seek operation failed."); - } - } - } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// - /// An I/O error occurs. - /// - /// The stream does not support seeking. - /// - /// Methods were called after the stream was closed. - public override long Position - { - get - { - if (!this._canSeek) - { - throw new NotSupportedException("Seek operation not supported."); - } - return this._position; - } - set - { - this.Seek(value, SeekOrigin.Begin); - } - } - - /// - /// Gets a value indicating whether the FileStream was opened asynchronously or synchronously. - /// - /// - /// true if this instance is async; otherwise, false. - /// - public virtual bool IsAsync - { - get - { - return this._isAsync; - } - } - - /// - /// Gets the name of the FileStream that was passed to the constructor. - /// - public string Name { get; private set; } - - /// - /// Gets the operating system file handle for the file that the current SftpFileStream object encapsulates. - /// - public virtual byte[] Handle - { - get - { - this.Flush(); - return this._handle; - } - } - - /// - /// Gets or sets the operation timeout. - /// - /// - /// The timeout. - /// - public TimeSpan Timeout { get; set; } - - internal SftpFileStream(SftpSession session, string path, FileMode mode) - : this(session, path, mode, FileAccess.ReadWrite, 65536, false) - { - // Nothing to do here. - } - - internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access) - : this(session, path, mode, access, 65536, false) - { - // Nothing to do here. - } - - internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access, int bufferSize) - : this(session, path, mode, access, bufferSize, false) - { - // Nothing to do here. - } - - internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, bool useAsync) - { - // Validate the parameters. - if (session == null) - throw new SshConnectionException("Client not connected."); - - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (bufferSize <= 0 || bufferSize > 64 * 1024) - { - throw new ArgumentOutOfRangeException("bufferSize"); - } - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - { - throw new ArgumentOutOfRangeException("access"); - } - if (mode < FileMode.CreateNew || mode > FileMode.Append) - { - throw new ArgumentOutOfRangeException("mode"); - } - - this.Timeout = TimeSpan.FromSeconds(30); - this.Name = path; - - // Initialize the object state. - this._session = session; - this._access = access; - this._ownsHandle = true; - this._isAsync = useAsync; - this._path = path; - this._bufferSize = bufferSize; - this._buffer = new byte[bufferSize]; - this._bufferPosn = 0; - this._bufferLen = 0; - this._bufferOwnedByWrite = false; - this._canSeek = true; - this._position = 0; - this._serverFilePosition = 0; - this._session.Disconnected += Session_Disconnected; - - var flags = Flags.None; - - switch (access) - { - case FileAccess.Read: - flags |= Flags.Read; - break; - case FileAccess.Write: - flags |= Flags.Write; - break; - case FileAccess.ReadWrite: - flags |= Flags.Read; - flags |= Flags.Write; - break; - default: - break; - } - - switch (mode) - { - case FileMode.Append: - flags |= Flags.Append; - break; - case FileMode.Create: - this._handle = this._session.RequestOpen(path, flags | Flags.Truncate, true); - if (this._handle == null) - { - flags |= Flags.CreateNew; - } - else - { - flags |= Flags.Truncate; - } - break; - case FileMode.CreateNew: - flags |= Flags.CreateNew; - break; - case FileMode.Open: - break; - case FileMode.OpenOrCreate: - flags |= Flags.CreateNewOrOpen; - break; - case FileMode.Truncate: - flags |= Flags.Truncate; - break; - default: - break; - } - - if (this._handle == null) - this._handle = this._session.RequestOpen(this._path, flags); - - this._attributes = this._session.RequestFStat(this._handle); - - if (mode == FileMode.Append) - { - this._position = this._attributes.Size; - this._serverFilePosition = (ulong)this._attributes.Size; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~SftpFileStream() - { - this.Dispose(false); - } - - /// - /// Begins an asynchronous read operation. - /// - /// The buffer to read the data into. - /// The byte offset in at which to begin writing data read from the stream. - /// The maximum number of bytes to read. - /// An optional asynchronous callback, to be called when the read is complete. - /// A user-provided object that distinguishes this particular asynchronous read request from other requests. - /// - /// An that represents the asynchronous read, which could still be pending. - /// - /// Attempted an asynchronous read past the end of the stream, or a disk error occurs. - /// - /// One or more of the arguments is invalid. - /// - /// Methods were called after the stream was closed. - /// - /// The current Stream implementation does not support the read operation. - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) - { - return base.BeginRead(buffer, offset, count, callback, state); - } - - /// - /// Waits for the pending asynchronous read to complete. - /// - /// The reference to the pending asynchronous request to finish. - /// - /// The number of bytes read from the stream, between zero (0) and the number of bytes you requested. Streams return zero (0) only at the end of the stream, otherwise, they should block until at least one byte is available. - /// - /// - /// is null. - /// - /// - /// did not originate from a method on the current stream. - /// - /// The stream is closed or an internal error has occurred. - public override int EndRead(IAsyncResult asyncResult) - { - return base.EndRead(asyncResult); - } - - /// - /// Begins an asynchronous write operation. - /// - /// The buffer to write data from. - /// The byte offset in from which to begin writing. - /// The maximum number of bytes to write. - /// An optional asynchronous callback, to be called when the write is complete. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// - /// An IAsyncResult that represents the asynchronous write, which could still be pending. - /// - /// Attempted an asynchronous write past the end of the stream, or a disk error occurs. - /// - /// One or more of the arguments is invalid. - /// - /// Methods were called after the stream was closed. - /// - /// The current Stream implementation does not support the write operation. - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) - { - return base.BeginWrite(buffer, offset, count, callback, state); - } - - /// - /// Ends an asynchronous write operation. - /// - /// A reference to the outstanding asynchronous I/O request. - /// - /// is null. - /// - /// - /// did not originate from a method on the current stream. - /// - /// The stream is closed or an internal error has occurred. - public override void EndWrite(IAsyncResult asyncResult) - { - base.EndWrite(asyncResult); - } - - /// - /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. - /// - public override void Close() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the file. - /// - /// An I/O error occurs. - /// Stream is closed. - public override void Flush() - { - lock (this._lock) - { - if (this._handle != null) - { - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - } - else - { - this.FlushReadBuffer(); - } - } - else - { - throw new ObjectDisposedException("Stream is closed."); - } - } - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// - /// The sum of and is larger than the buffer length. - /// - /// - /// is null. - /// - /// - /// or is negative. - /// - /// An I/O error occurs. - /// - /// The stream does not support reading. - /// - /// Methods were called after the stream was closed. - public override int Read(byte[] buffer, int offset, int count) - { - int readLen = 0; - int tempLen; - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - else if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset"); - } - else if (count < 0) - { - throw new ArgumentOutOfRangeException("count"); - } - else if ((buffer.Length - offset) < count) - { - throw new ArgumentException("Invalid array range."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Set up for the read operation. - this.SetupRead(); - - // Read data into the caller's buffer. - while (count > 0) - { - // How much data do we have available in the buffer? - tempLen = this._bufferLen - this._bufferPosn; - if (tempLen <= 0) - { - this._bufferPosn = 0; - - var data = this._session.RequestRead(this._handle, (ulong)this._position, (uint)this._bufferSize); - - this._bufferLen = data.Length; - - Buffer.BlockCopy(data, 0, this._buffer, 0, this._bufferLen); - this._serverFilePosition = (ulong)this._position; - - if (this._bufferLen < 0) - { - this._bufferLen = 0; - // TODO: Add SFTP error code or message if possible - throw new IOException("Read operation failed."); - } - else if (this._bufferLen == 0) - { - break; - } - else - { - tempLen = this._bufferLen; - } - } - - // Don't read more than the caller wants. - if (tempLen > count) - { - tempLen = count; - } - - // Copy stream data to the caller's buffer. - Buffer.BlockCopy(this._buffer, this._bufferPosn, buffer, offset, tempLen); - - // Advance to the next buffer positions. - readLen += tempLen; - offset += tempLen; - count -= tempLen; - this._bufferPosn += tempLen; - this._position += tempLen; - } - } - - // Return the number of bytes that were read to the caller. - return readLen; - } - - /// - /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. - /// - /// - /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. - /// - /// The stream does not support reading. - /// - /// Methods were called after the stream was closed. - /// Read operation failed. - public override int ReadByte() - { - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup the object for reading. - this.SetupRead(); - - // Read more data into the internal buffer if necessary. - if (this._bufferPosn >= this._bufferLen) - { - this._bufferPosn = 0; - - var data = this._session.RequestRead(this._handle, (ulong)this._position, (uint)this._bufferSize); - - this._bufferLen = data.Length; - Buffer.BlockCopy(data, 0, this._buffer, 0, this._bufferSize); - this._serverFilePosition = (ulong)this._position; - - if (this._bufferLen < 0) - { - this._bufferLen = 0; - // TODO: Add SFTP error code or message if possible - throw new IOException("Read operation failed."); - } - else if (this._bufferLen == 0) - { - // We've reached EOF. - return -1; - } - } - - // Extract the next byte from the buffer. - ++this._position; - return this._buffer[this._bufferPosn++]; - } - } - - /// - /// Sets the position within the current stream. - /// - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// - /// The new position within the current stream. - /// - /// An I/O error occurs. - /// - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// - /// Methods were called after the stream was closed. - public override long Seek(long offset, SeekOrigin origin) - { - long newPosn = -1; - - // Bail out if this stream is not capable of seeking. - if (!this._canSeek) - { - throw new NotSupportedException("Seek is not supported."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Bail out if the handle is invalid. - if (this._handle == null) - { - throw new ObjectDisposedException("Stream is closed."); - } - - // Don't do anything if the position won't be moving. - if (origin == SeekOrigin.Begin && offset == this._position) - { - return offset; - } - else if (origin == SeekOrigin.Current && offset == 0) - { - return this._position; - } - - this._attributes = this._session.RequestFStat(this._handle); - - // The behaviour depends upon the read/write mode. - if (this._bufferOwnedByWrite) - { - // Flush the write buffer and then seek. - this.FlushWriteBuffer(); - - switch (origin) - { - case SeekOrigin.Begin: - newPosn = offset; - break; - case SeekOrigin.Current: - newPosn = this._position + offset; - break; - case SeekOrigin.End: - newPosn = this._attributes.Size - offset; - break; - default: - break; - } - - if (newPosn == -1) - { - throw new EndOfStreamException("End of stream."); - } - this._position = newPosn; - this._serverFilePosition = (ulong)newPosn; - } - else - { - // Determine if the seek is to somewhere inside - // the current read buffer bounds. - if (origin == SeekOrigin.Begin) - { - newPosn = this._position - this._bufferPosn; - if (offset >= newPosn && offset < - (newPosn + this._bufferLen)) - { - this._bufferPosn = (int)(offset - newPosn); - this._position = offset; - return this._position; - } - } - else if (origin == SeekOrigin.Current) - { - newPosn = this._position + offset; - if (newPosn >= (this._position - this._bufferPosn) && - newPosn < (this._position - this._bufferPosn + this._bufferLen)) - { - this._bufferPosn = - (int)(newPosn - (this._position - this._bufferPosn)); - this._position = newPosn; - return this._position; - } - } - - // Abandon the read buffer. - this._bufferPosn = 0; - this._bufferLen = 0; - - // Seek to the new position. - switch (origin) - { - case SeekOrigin.Begin: - newPosn = offset; - break; - case SeekOrigin.Current: - newPosn = this._position + offset; - break; - case SeekOrigin.End: - newPosn = this._attributes.Size - offset; - break; - default: - break; - } - - if (newPosn < 0) - { - throw new EndOfStreamException(); - } - - this._position = newPosn; - } - return this._position; - } - } - - /// - /// When overridden in a derived class, sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - /// An I/O error occurs. - /// - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// - /// Methods were called after the stream was closed. - /// must be greater than zero. - public override void SetLength(long value) - { - // Validate the parameters and setup the object for writing. - if (value < 0) - { - throw new ArgumentOutOfRangeException("value"); - } - if (!this._canSeek) - { - throw new NotSupportedException("Seek is not supported."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup this object for writing. - this.SetupWrite(); - - this._attributes.Size = value; - - this._session.RequestFSetStat(this._handle, this._attributes); - } - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies bytes from to the current stream. - /// The zero-based byte offset in at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// The sum of and is greater than the buffer length. - /// - /// - /// is null. - /// - /// - /// or is negative. - /// - /// An I/O error occurs. - /// - /// The stream does not support writing. - /// - /// Methods were called after the stream was closed. - public override void Write(byte[] buffer, int offset, int count) - { - int tempLen; - - // Validate the parameters - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - else if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset"); - } - else if (count < 0) - { - throw new ArgumentOutOfRangeException("count"); - } - else if ((buffer.Length - offset) < count) - { - throw new ArgumentException("Invalid array range."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup this object for writing. - this.SetupWrite(); - - // Write data to the file stream. - while (count > 0) - { - // Determine how many bytes we can write to the buffer. - tempLen = this._bufferSize - this._bufferPosn; - if (tempLen <= 0) - { - var data = new byte[this._bufferPosn]; - - Buffer.BlockCopy(this._buffer, 0, data, 0, this._bufferPosn); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosn = 0; - tempLen = this._bufferSize; - } - if (tempLen > count) - { - tempLen = count; - } - - // Can we short-cut the internal buffer? - if (this._bufferPosn == 0 && tempLen == this._bufferSize) - { - // Yes: write the data directly to the file. - var data = new byte[tempLen]; - - Buffer.BlockCopy(buffer, offset, data, 0, tempLen); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - } - else - { - // No: copy the data to the write buffer first. - Buffer.BlockCopy(buffer, offset, this._buffer, this._bufferPosn, tempLen); - this._bufferPosn += tempLen; - } - - // Advance the buffer and stream positions. - this._position += tempLen; - offset += tempLen; - count -= tempLen; - } - - // If the buffer is full, then do a speculative flush now, - // rather than waiting for the next call to this method. - if (this._bufferPosn >= this._bufferSize) - { - var data = new byte[this._bufferPosn]; - - Buffer.BlockCopy(this._buffer, 0, data, 0, this._bufferPosn); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosn = 0; - } - } - } - - /// - /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. - /// - /// The byte to write to the stream. - /// An I/O error occurs. - /// - /// The stream does not support writing, or the stream is already closed. - /// - /// Methods were called after the stream was closed. - public override void WriteByte(byte value) - { - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup the object for writing. - this.SetupWrite(); - - // Flush the current buffer if it is full. - if (this._bufferPosn >= this._bufferSize) - { - var data = new byte[this._bufferPosn]; - - Buffer.BlockCopy(this._buffer, 0, data, 0, this._bufferPosn); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosn = 0; - } - - // Write the byte into the buffer and advance the posn. - this._buffer[this._bufferPosn++] = value; - ++this._position; - } - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._session != null) - { - lock (this._lock) - { - if (this._session != null) - { - if (this._handle != null) - { - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - } - - if (this._ownsHandle) - { - this._session.RequestClose(this._handle); - } - - this._handle = null; - } - - this._session.Disconnected -= Session_Disconnected; - this._session = null; - } - } - } - } - - /// - /// Flushes the read data from the buffer. - /// - private void FlushReadBuffer() - { - if (this._canSeek) - { - if (this._bufferPosn < this._bufferLen) - { - this._position -= this._bufferPosn; - } - this._bufferPosn = 0; - this._bufferLen = 0; - } - } - - /// - /// Flush any buffered write data to the file. - /// - private void FlushWriteBuffer() - { - if (this._bufferPosn > 0) - { - var data = new byte[this._bufferPosn]; - - Buffer.BlockCopy(this._buffer, 0, data, 0, this._bufferPosn); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosn = 0; - } - } - - /// - /// Setups the read. - /// - private void SetupRead() - { - if ((this._access & FileAccess.Read) == 0) - { - throw new NotSupportedException("Read not supported."); - } - if (this._handle == null) - { - throw new ObjectDisposedException("Stream is closed."); - } - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - this._bufferOwnedByWrite = false; - } - } - - /// - /// Setups the write. - /// - private void SetupWrite() - { - if ((this._access & FileAccess.Write) == 0) - { - throw new NotSupportedException("Write not supported."); - } - if (this._handle == null) - { - throw new ObjectDisposedException("Stream is closed."); - } - if (!this._bufferOwnedByWrite) - { - this.FlushReadBuffer(); - this._bufferOwnedByWrite = true; - } - } - - private void Session_Disconnected(object sender, EventArgs e) - { - lock (this._lock) - { - this._session.Disconnected -= Session_Disconnected; - this._session = null; - } - } - } +using System; +using System.IO; +using System.Threading; +using System.Diagnostics.CodeAnalysis; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Sftp +{ + /// + /// Exposes a System.IO.Stream around a remote SFTP file, supporting both synchronous and asynchronous read and write operations. + /// + public class SftpFileStream : Stream + { + // TODO: Add security method to set userid, groupid and other permission settings + // Internal state. + private byte[] _handle; + private readonly FileAccess _access; + private readonly bool _ownsHandle; + private readonly bool _isAsync; + private SftpSession _session; + + // Buffer information. + private readonly int _readBufferSize; + private readonly byte[] _readBuffer; + private readonly int _writeBufferSize; + private readonly byte[] _writeBuffer; + private int _bufferPosition; + private int _bufferLen; + private long _position; + private bool _bufferOwnedByWrite; + private readonly bool _canSeek; + private ulong _serverFilePosition; + + private SftpFileAttributes _attributes; + + private readonly object _lock = new object(); + + /// + /// Gets a value indicating whether the current stream supports reading. + /// + /// true if the stream supports reading; otherwise, false. + public override bool CanRead + { + get + { + return ((this._access & FileAccess.Read) != 0); + } + } + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + /// true if the stream supports seeking; otherwise, false. + public override bool CanSeek + { + get + { + return this._canSeek; + } + } + + /// + /// Gets a value indicating whether the current stream supports writing. + /// + /// true if the stream supports writing; otherwise, false. + public override bool CanWrite + { + get + { + return ((this._access & FileAccess.Write) != 0); + } + } + + /// + /// Gets the length in bytes of the stream. + /// + /// A long value representing the length of the stream in bytes. + /// A class derived from Stream does not support seeking. + /// Methods were called after the stream was closed. + /// IO operation failed. + [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Be design this is the exception that stream need to throw.")] + public override long Length + { + get + { + // Validate that the object can actually do this. + if (!this._canSeek) + { + throw new NotSupportedException("Seek operation is not supported."); + } + + // Lock down the file stream while we do this. + lock (this._lock) + { + if (this._handle == null) + { + // ECMA says this should be IOException even though + // everywhere else uses ObjectDisposedException. + throw new IOException("Stream is closed."); + } + + // Flush the write buffer, because it may + // affect the length of the stream. + if (this._bufferOwnedByWrite) + { + this.FlushWriteBuffer(); + } + + // Update file attributes + this._attributes = this._session.RequestFStat(this._handle); + + if (this._attributes != null && this._attributes.Size > -1) + { + return this._attributes.Size; + } + throw new IOException("Seek operation failed."); + } + } + } + + /// + /// Gets or sets the position within the current stream. + /// + /// The current position within the stream. + /// + /// An I/O error occurs. + /// + /// The stream does not support seeking. + /// + /// Methods were called after the stream was closed. + public override long Position + { + get + { + if (!this._canSeek) + { + throw new NotSupportedException("Seek operation not supported."); + } + return this._position; + } + set + { + this.Seek(value, SeekOrigin.Begin); + } + } + + /// + /// Gets a value indicating whether the FileStream was opened asynchronously or synchronously. + /// + /// + /// true if this instance is async; otherwise, false. + /// + public virtual bool IsAsync + { + get + { + return this._isAsync; + } + } + + /// + /// Gets the name of the FileStream that was passed to the constructor. + /// + public string Name { get; private set; } + + /// + /// Gets the operating system file handle for the file that the current SftpFileStream object encapsulates. + /// + public virtual byte[] Handle + { + get + { + this.Flush(); + return this._handle; + } + } + + /// + /// Gets or sets the operation timeout. + /// + /// + /// The timeout. + /// + public TimeSpan Timeout { get; set; } + + /// + /// Initializes a new instance with a read and write buffer + /// of 4 KB. + /// + internal SftpFileStream(SftpSession session, string path, FileMode mode) + : this(session, path, mode, FileAccess.ReadWrite) + { + } + + internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access) + : this(session, path, mode, access, 4096) + { + } + + internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access, int bufferSize) + : this(session, path, mode, access, bufferSize, false) + { + } + + internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, bool useAsync) + { + // Validate the parameters. + if (session == null) + throw new SshConnectionException("Client not connected."); + + if (path == null) + { + throw new ArgumentNullException("path"); + } + if (bufferSize <= 0) + { + throw new ArgumentOutOfRangeException("bufferSize"); + } + if (access < FileAccess.Read || access > FileAccess.ReadWrite) + { + throw new ArgumentOutOfRangeException("access"); + } + if (mode < FileMode.CreateNew || mode > FileMode.Append) + { + throw new ArgumentOutOfRangeException("mode"); + } + + this.Timeout = TimeSpan.FromSeconds(30); + this.Name = path; + + // Initialize the object state. + this._session = session; + this._access = access; + this._ownsHandle = true; + this._isAsync = useAsync; + this._bufferPosition = 0; + this._bufferLen = 0; + this._bufferOwnedByWrite = false; + this._canSeek = true; + this._position = 0; + this._serverFilePosition = 0; + this._session.Disconnected += Session_Disconnected; + + var flags = Flags.None; + + switch (access) + { + case FileAccess.Read: + flags |= Flags.Read; + break; + case FileAccess.Write: + flags |= Flags.Write; + break; + case FileAccess.ReadWrite: + flags |= Flags.Read; + flags |= Flags.Write; + break; + } + + switch (mode) + { + case FileMode.Append: + flags |= Flags.Append; + break; + case FileMode.Create: + this._handle = this._session.RequestOpen(path, flags | Flags.Truncate, true); + if (this._handle == null) + { + flags |= Flags.CreateNew; + } + else + { + flags |= Flags.Truncate; + } + break; + case FileMode.CreateNew: + flags |= Flags.CreateNew; + break; + case FileMode.Open: + break; + case FileMode.OpenOrCreate: + flags |= Flags.CreateNewOrOpen; + break; + case FileMode.Truncate: + flags |= Flags.Truncate; + break; + } + + if (this._handle == null) + this._handle = this._session.RequestOpen(path, flags); + + this._attributes = this._session.RequestFStat(this._handle); + + this._readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize); + this._readBuffer = new byte[_readBufferSize]; + this._writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, _handle); + this._writeBuffer = new byte[_writeBufferSize]; + + if (mode == FileMode.Append) + { + this._position = this._attributes.Size; + this._serverFilePosition = (ulong)this._attributes.Size; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~SftpFileStream() + { + this.Dispose(false); + } + + /// + /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. + /// + public override void Close() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Clears all buffers for this stream and causes any buffered data to be written to the file. + /// + /// An I/O error occurs. + /// Stream is closed. + public override void Flush() + { + lock (this._lock) + { + if (this._handle != null) + { + if (this._bufferOwnedByWrite) + { + this.FlushWriteBuffer(); + } + else + { + this.FlushReadBuffer(); + } + } + else + { + throw new ObjectDisposedException("Stream is closed."); + } + } + } + + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// + /// The sum of and is larger than the buffer length. + /// is null. + /// or is negative. + /// An I/O error occurs. + /// The stream does not support reading. + /// Methods were called after the stream was closed. + public override int Read(byte[] buffer, int offset, int count) + { + int readLen = 0; + + if (buffer == null) + throw new ArgumentNullException("buffer"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + if ((buffer.Length - offset) < count) + throw new ArgumentException("Invalid array range."); + + // Lock down the file stream while we do this. + lock (this._lock) + { + // Set up for the read operation. + this.SetupRead(); + + // Read data into the caller's buffer. + while (count > 0) + { + // How much data do we have available in the buffer? + var tempLen = this._bufferLen - this._bufferPosition; + if (tempLen <= 0) + { + this._bufferPosition = 0; + + var data = this._session.RequestRead(this._handle, (ulong)this._position, (uint)this._readBufferSize); + + this._bufferLen = data.Length; + + Buffer.BlockCopy(data, 0, this._readBuffer, 0, this._bufferLen); + this._serverFilePosition = (ulong)this._position; + + if (this._bufferLen < 0) + { + this._bufferLen = 0; + // TODO: Add SFTP error code or message if possible + throw new IOException("Read operation failed."); + } + if (this._bufferLen == 0) + { + break; + } + tempLen = this._bufferLen; + } + + // Don't read more than the caller wants. + if (tempLen > count) + { + tempLen = count; + } + + // Copy stream data to the caller's buffer. + Buffer.BlockCopy(this._readBuffer, this._bufferPosition, buffer, offset, tempLen); + + // Advance to the next buffer positions. + readLen += tempLen; + offset += tempLen; + count -= tempLen; + this._bufferPosition += tempLen; + this._position += tempLen; + } + } + + // Return the number of bytes that were read to the caller. + return readLen; + } + + /// + /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. + /// + /// + /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. + /// + /// The stream does not support reading. + /// Methods were called after the stream was closed. + /// Read operation failed. + public override int ReadByte() + { + // Lock down the file stream while we do this. + lock (this._lock) + { + // Setup the object for reading. + this.SetupRead(); + + // Read more data into the internal buffer if necessary. + if (this._bufferPosition >= this._bufferLen) + { + this._bufferPosition = 0; + + var data = this._session.RequestRead(this._handle, (ulong)this._position, (uint)this._readBufferSize); + + this._bufferLen = data.Length; + Buffer.BlockCopy(data, 0, this._readBuffer, 0, this._readBufferSize); + this._serverFilePosition = (ulong)this._position; + + if (this._bufferLen < 0) + { + this._bufferLen = 0; + // TODO: Add SFTP error code or message if possible + throw new IOException("Read operation failed."); + } + if (this._bufferLen == 0) + { + // We've reached EOF. + return -1; + } + } + + // Extract the next byte from the buffer. + ++this._position; + return this._readBuffer[this._bufferPosition++]; + } + } + + /// + /// Sets the position within the current stream. + /// + /// A byte offset relative to the parameter. + /// A value of type indicating the reference point used to obtain the new position. + /// + /// The new position within the current stream. + /// + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. + public override long Seek(long offset, SeekOrigin origin) + { + long newPosn = -1; + + // Bail out if this stream is not capable of seeking. + if (!this._canSeek) + { + throw new NotSupportedException("Seek is not supported."); + } + + // Lock down the file stream while we do this. + lock (this._lock) + { + // Bail out if the handle is invalid. + if (this._handle == null) + { + throw new ObjectDisposedException("Stream is closed."); + } + + // Don't do anything if the position won't be moving. + if (origin == SeekOrigin.Begin && offset == this._position) + { + return offset; + } + if (origin == SeekOrigin.Current && offset == 0) + { + return this._position; + } + + this._attributes = this._session.RequestFStat(this._handle); + + // The behaviour depends upon the read/write mode. + if (this._bufferOwnedByWrite) + { + // Flush the write buffer and then seek. + this.FlushWriteBuffer(); + + switch (origin) + { + case SeekOrigin.Begin: + newPosn = offset; + break; + case SeekOrigin.Current: + newPosn = this._position + offset; + break; + case SeekOrigin.End: + newPosn = this._attributes.Size - offset; + break; + } + + if (newPosn == -1) + { + throw new EndOfStreamException("End of stream."); + } + this._position = newPosn; + this._serverFilePosition = (ulong)newPosn; + } + else + { + // Determine if the seek is to somewhere inside + // the current read buffer bounds. + if (origin == SeekOrigin.Begin) + { + newPosn = this._position - this._bufferPosition; + if (offset >= newPosn && offset < + (newPosn + this._bufferLen)) + { + this._bufferPosition = (int)(offset - newPosn); + this._position = offset; + return this._position; + } + } + else if (origin == SeekOrigin.Current) + { + newPosn = this._position + offset; + if (newPosn >= (this._position - this._bufferPosition) && + newPosn < (this._position - this._bufferPosition + this._bufferLen)) + { + this._bufferPosition = + (int)(newPosn - (this._position - this._bufferPosition)); + this._position = newPosn; + return this._position; + } + } + + // Abandon the read buffer. + this._bufferPosition = 0; + this._bufferLen = 0; + + // Seek to the new position. + switch (origin) + { + case SeekOrigin.Begin: + newPosn = offset; + break; + case SeekOrigin.Current: + newPosn = this._position + offset; + break; + case SeekOrigin.End: + newPosn = this._attributes.Size - offset; + break; + } + + if (newPosn < 0) + { + throw new EndOfStreamException(); + } + + this._position = newPosn; + } + return this._position; + } + } + + /// + /// When overridden in a derived class, sets the length of the current stream. + /// + /// The desired length of the current stream in bytes. + /// An I/O error occurs. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. + /// must be greater than zero. + public override void SetLength(long value) + { + // Validate the parameters and setup the object for writing. + if (value < 0) + { + throw new ArgumentOutOfRangeException("value"); + } + if (!this._canSeek) + { + throw new NotSupportedException("Seek is not supported."); + } + + // Lock down the file stream while we do this. + lock (this._lock) + { + // Setup this object for writing. + this.SetupWrite(); + + this._attributes.Size = value; + + this._session.RequestFSetStat(this._handle, this._attributes); + } + } + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies bytes from to the current stream. + /// The zero-based byte offset in at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// The sum of and is greater than the buffer length. + /// is null. + /// or is negative. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + public override void Write(byte[] buffer, int offset, int count) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + if ((buffer.Length - offset) < count) + throw new ArgumentException("Invalid array range."); + + // Lock down the file stream while we do this. + lock (this._lock) + { + // Setup this object for writing. + this.SetupWrite(); + + // Write data to the file stream. + while (count > 0) + { + // Determine how many bytes we can write to the buffer. + var tempLen = this._writeBufferSize - this._bufferPosition; + if (tempLen <= 0) + { + var data = new byte[this._bufferPosition]; + + Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); + + using (var wait = new AutoResetEvent(false)) + { + this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); + this._serverFilePosition += (ulong)data.Length; + } + + this._bufferPosition = 0; + tempLen = this._writeBufferSize; + } + if (tempLen > count) + { + tempLen = count; + } + + // Can we short-cut the internal buffer? + if (this._bufferPosition == 0 && tempLen == this._writeBufferSize) + { + // Yes: write the data directly to the file. + var data = new byte[tempLen]; + + Buffer.BlockCopy(buffer, offset, data, 0, tempLen); + + using (var wait = new AutoResetEvent(false)) + { + this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); + this._serverFilePosition += (ulong)data.Length; + } + } + else + { + // No: copy the data to the write buffer first. + Buffer.BlockCopy(buffer, offset, _writeBuffer, this._bufferPosition, tempLen); + this._bufferPosition += tempLen; + } + + // Advance the buffer and stream positions. + this._position += tempLen; + offset += tempLen; + count -= tempLen; + } + + // If the buffer is full, then do a speculative flush now, + // rather than waiting for the next call to this method. + if (this._bufferPosition >= _writeBufferSize) + { + var data = new byte[this._bufferPosition]; + + Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); + + using (var wait = new AutoResetEvent(false)) + { + this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); + this._serverFilePosition += (ulong)data.Length; + } + + this._bufferPosition = 0; + } + } + } + + /// + /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. + /// + /// The byte to write to the stream. + /// An I/O error occurs. + /// The stream does not support writing, or the stream is already closed. + /// Methods were called after the stream was closed. + public override void WriteByte(byte value) + { + // Lock down the file stream while we do this. + lock (this._lock) + { + // Setup the object for writing. + this.SetupWrite(); + + // Flush the current buffer if it is full. + if (this._bufferPosition >= this._writeBufferSize) + { + var data = new byte[this._bufferPosition]; + + Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); + + using (var wait = new AutoResetEvent(false)) + { + this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); + this._serverFilePosition += (ulong)data.Length; + } + + this._bufferPosition = 0; + } + + // Write the byte into the buffer and advance the posn. + _writeBuffer[this._bufferPosition++] = value; + ++this._position; + } + } + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (this._session != null) + { + lock (this._lock) + { + if (this._session != null) + { + if (this._handle != null) + { + if (this._bufferOwnedByWrite) + { + this.FlushWriteBuffer(); + } + + if (this._ownsHandle) + { + this._session.RequestClose(this._handle); + } + + this._handle = null; + } + + this._session.Disconnected -= Session_Disconnected; + this._session = null; + } + } + } + } + + /// + /// Flushes the read data from the buffer. + /// + private void FlushReadBuffer() + { + if (this._canSeek) + { + if (this._bufferPosition < this._bufferLen) + { + this._position -= this._bufferPosition; + } + this._bufferPosition = 0; + this._bufferLen = 0; + } + } + + /// + /// Flush any buffered write data to the file. + /// + private void FlushWriteBuffer() + { + if (this._bufferPosition > 0) + { + var data = new byte[this._bufferPosition]; + + Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); + + using (var wait = new AutoResetEvent(false)) + { + this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); + this._serverFilePosition += (ulong)data.Length; + } + + this._bufferPosition = 0; + } + } + + /// + /// Setups the read. + /// + private void SetupRead() + { + if ((this._access & FileAccess.Read) == 0) + { + throw new NotSupportedException("Read not supported."); + } + if (this._handle == null) + { + throw new ObjectDisposedException("Stream is closed."); + } + if (this._bufferOwnedByWrite) + { + this.FlushWriteBuffer(); + this._bufferOwnedByWrite = false; + } + } + + /// + /// Setups the write. + /// + private void SetupWrite() + { + if ((this._access & FileAccess.Write) == 0) + { + throw new NotSupportedException("Write not supported."); + } + if (this._handle == null) + { + throw new ObjectDisposedException("Stream is closed."); + } + if (!this._bufferOwnedByWrite) + { + this.FlushReadBuffer(); + this._bufferOwnedByWrite = true; + } + } + + private void Session_Disconnected(object sender, EventArgs e) + { + lock (this._lock) + { + this._session.Disconnected -= Session_Disconnected; + this._session = null; + } + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpFileSystemInformation.cs b/Renci.SshNet/Sftp/SftpFileSystemInformation.cs index abf189d..e5bb503 100644 --- a/Renci.SshNet/Sftp/SftpFileSystemInformation.cs +++ b/Renci.SshNet/Sftp/SftpFileSystemInformation.cs @@ -1,136 +1,136 @@ -namespace Renci.SshNet.Sftp -{ - /// - /// Contains File system information exposed by statvfs@openssh.com request. - /// - public class SftpFileSytemInformation - { - private ulong _flag; - - private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; - - private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - public ulong BlockSize { get; private set; } - - /// - /// Gets the total blocks. - /// - /// - /// The total blocks. - /// - public ulong TotalBlocks { get; private set; } - - /// - /// Gets the free blocks. - /// - /// - /// The free blocks. - /// - public ulong FreeBlocks { get; private set; } - - /// - /// Gets the available blocks. - /// - /// - /// The available blocks. - /// - public ulong AvailableBlocks { get; private set; } - - /// - /// Gets the total nodes. - /// - /// - /// The total nodes. - /// - public ulong TotalNodes { get; private set; } - - /// - /// Gets the free nodes. - /// - /// - /// The free nodes. - /// - public ulong FreeNodes { get; private set; } - - /// - /// Gets the available nodes. - /// - /// - /// The available nodes. - /// - public ulong AvailableNodes { get; private set; } - - /// - /// Gets the sid. - /// - /// - /// The sid. - /// - public ulong Sid { get; private set; } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } - } - - /// - /// Gets a value indicating whether [supports set uid]. - /// - /// - /// true if [supports set uid]; otherwise, false. - /// - public bool SupportsSetUid - { - get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } - } - - /// - /// Gets the max name lenght. - /// - /// - /// The max name lenght. - /// - public ulong MaxNameLenght { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bsize. - /// The frsize. - /// The blocks. - /// The bfree. - /// The bavail. - /// The files. - /// The ffree. - /// The favail. - /// The sid. - /// The flag. - /// The namemax. - internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) - { - this.BlockSize = frsize; - this.TotalBlocks = blocks; - this.FreeBlocks = bfree; - this.AvailableBlocks = bavail; - this.TotalNodes = files; - this.FreeNodes = ffree; - this.AvailableNodes = favail; - this.Sid = sid; - this._flag = flag; - this.MaxNameLenght = namemax; - } - } +namespace Renci.SshNet.Sftp +{ + /// + /// Contains File system information exposed by statvfs@openssh.com request. + /// + public class SftpFileSytemInformation + { + private readonly ulong _flag; + + private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; + + private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; + + /// + /// Gets the size of the block. + /// + /// + /// The size of the block. + /// + public ulong BlockSize { get; private set; } + + /// + /// Gets the total blocks. + /// + /// + /// The total blocks. + /// + public ulong TotalBlocks { get; private set; } + + /// + /// Gets the free blocks. + /// + /// + /// The free blocks. + /// + public ulong FreeBlocks { get; private set; } + + /// + /// Gets the available blocks. + /// + /// + /// The available blocks. + /// + public ulong AvailableBlocks { get; private set; } + + /// + /// Gets the total nodes. + /// + /// + /// The total nodes. + /// + public ulong TotalNodes { get; private set; } + + /// + /// Gets the free nodes. + /// + /// + /// The free nodes. + /// + public ulong FreeNodes { get; private set; } + + /// + /// Gets the available nodes. + /// + /// + /// The available nodes. + /// + public ulong AvailableNodes { get; private set; } + + /// + /// Gets the sid. + /// + /// + /// The sid. + /// + public ulong Sid { get; private set; } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } + } + + /// + /// Gets a value indicating whether [supports set uid]. + /// + /// + /// true if [supports set uid]; otherwise, false. + /// + public bool SupportsSetUid + { + get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } + } + + /// + /// Gets the max name lenght. + /// + /// + /// The max name lenght. + /// + public ulong MaxNameLenght { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The bsize. + /// The frsize. + /// The blocks. + /// The bfree. + /// The bavail. + /// The files. + /// The ffree. + /// The favail. + /// The sid. + /// The flag. + /// The namemax. + internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) + { + this.BlockSize = frsize; + this.TotalBlocks = blocks; + this.FreeBlocks = bfree; + this.AvailableBlocks = bavail; + this.TotalNodes = files; + this.FreeNodes = ffree; + this.AvailableNodes = favail; + this.Sid = sid; + this._flag = flag; + this.MaxNameLenght = namemax; + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpFilesystemInformation.cs b/Renci.SshNet/Sftp/SftpFilesystemInformation.cs index abf189d..e5bb503 100644 --- a/Renci.SshNet/Sftp/SftpFilesystemInformation.cs +++ b/Renci.SshNet/Sftp/SftpFilesystemInformation.cs @@ -1,136 +1,136 @@ -namespace Renci.SshNet.Sftp -{ - /// - /// Contains File system information exposed by statvfs@openssh.com request. - /// - public class SftpFileSytemInformation - { - private ulong _flag; - - private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; - - private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - public ulong BlockSize { get; private set; } - - /// - /// Gets the total blocks. - /// - /// - /// The total blocks. - /// - public ulong TotalBlocks { get; private set; } - - /// - /// Gets the free blocks. - /// - /// - /// The free blocks. - /// - public ulong FreeBlocks { get; private set; } - - /// - /// Gets the available blocks. - /// - /// - /// The available blocks. - /// - public ulong AvailableBlocks { get; private set; } - - /// - /// Gets the total nodes. - /// - /// - /// The total nodes. - /// - public ulong TotalNodes { get; private set; } - - /// - /// Gets the free nodes. - /// - /// - /// The free nodes. - /// - public ulong FreeNodes { get; private set; } - - /// - /// Gets the available nodes. - /// - /// - /// The available nodes. - /// - public ulong AvailableNodes { get; private set; } - - /// - /// Gets the sid. - /// - /// - /// The sid. - /// - public ulong Sid { get; private set; } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } - } - - /// - /// Gets a value indicating whether [supports set uid]. - /// - /// - /// true if [supports set uid]; otherwise, false. - /// - public bool SupportsSetUid - { - get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } - } - - /// - /// Gets the max name lenght. - /// - /// - /// The max name lenght. - /// - public ulong MaxNameLenght { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bsize. - /// The frsize. - /// The blocks. - /// The bfree. - /// The bavail. - /// The files. - /// The ffree. - /// The favail. - /// The sid. - /// The flag. - /// The namemax. - internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) - { - this.BlockSize = frsize; - this.TotalBlocks = blocks; - this.FreeBlocks = bfree; - this.AvailableBlocks = bavail; - this.TotalNodes = files; - this.FreeNodes = ffree; - this.AvailableNodes = favail; - this.Sid = sid; - this._flag = flag; - this.MaxNameLenght = namemax; - } - } +namespace Renci.SshNet.Sftp +{ + /// + /// Contains File system information exposed by statvfs@openssh.com request. + /// + public class SftpFileSytemInformation + { + private readonly ulong _flag; + + private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; + + private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; + + /// + /// Gets the size of the block. + /// + /// + /// The size of the block. + /// + public ulong BlockSize { get; private set; } + + /// + /// Gets the total blocks. + /// + /// + /// The total blocks. + /// + public ulong TotalBlocks { get; private set; } + + /// + /// Gets the free blocks. + /// + /// + /// The free blocks. + /// + public ulong FreeBlocks { get; private set; } + + /// + /// Gets the available blocks. + /// + /// + /// The available blocks. + /// + public ulong AvailableBlocks { get; private set; } + + /// + /// Gets the total nodes. + /// + /// + /// The total nodes. + /// + public ulong TotalNodes { get; private set; } + + /// + /// Gets the free nodes. + /// + /// + /// The free nodes. + /// + public ulong FreeNodes { get; private set; } + + /// + /// Gets the available nodes. + /// + /// + /// The available nodes. + /// + public ulong AvailableNodes { get; private set; } + + /// + /// Gets the sid. + /// + /// + /// The sid. + /// + public ulong Sid { get; private set; } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } + } + + /// + /// Gets a value indicating whether [supports set uid]. + /// + /// + /// true if [supports set uid]; otherwise, false. + /// + public bool SupportsSetUid + { + get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } + } + + /// + /// Gets the max name lenght. + /// + /// + /// The max name lenght. + /// + public ulong MaxNameLenght { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The bsize. + /// The frsize. + /// The blocks. + /// The bfree. + /// The bavail. + /// The files. + /// The ffree. + /// The favail. + /// The sid. + /// The flag. + /// The namemax. + internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) + { + this.BlockSize = frsize; + this.TotalBlocks = blocks; + this.FreeBlocks = bfree; + this.AvailableBlocks = bavail; + this.TotalNodes = files; + this.FreeNodes = ffree; + this.AvailableNodes = favail; + this.Sid = sid; + this._flag = flag; + this.MaxNameLenght = namemax; + } + } } \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs b/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs index f85f0ea..13093d2 100644 --- a/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs +++ b/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs @@ -1,39 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous directory list operation. - /// - public class SftpListDirectoryAsyncResult : AsyncResult> - { - /// - /// Gets the number of files read so far. - /// - public int FilesRead { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpListDirectoryAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - - } - - /// - /// Updates asynchronous operation status information. - /// - /// The files read. - internal void Update(int filesRead) - { - this.FilesRead = filesRead; - } - } -} +using System; +using System.Collections.Generic; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Sftp +{ + /// + /// Encapsulates the results of an asynchronous directory list operation. + /// + public class SftpListDirectoryAsyncResult : AsyncResult> + { + /// + /// Gets the number of files read so far. + /// + public int FilesRead { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The async callback. + /// The state. + public SftpListDirectoryAsyncResult(AsyncCallback asyncCallback, Object state) + : base(asyncCallback, state) + { + + } + + /// + /// Updates asynchronous operation status information. + /// + /// The files read. + internal void Update(int filesRead) + { + this.FilesRead = filesRead; + } + } +} diff --git a/Renci.SshNet/Sftp/SftpMessage.cs b/Renci.SshNet/Sftp/SftpMessage.cs index 5f10d0d..c187a41 100644 --- a/Renci.SshNet/Sftp/SftpMessage.cs +++ b/Renci.SshNet/Sftp/SftpMessage.cs @@ -1,201 +1,199 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Renci.SshNet.Common; -using System.Globalization; -using Renci.SshNet.Sftp.Responses; -using System.Text; - -namespace Renci.SshNet.Sftp -{ - public abstract class SftpMessage : SshData - { - public static SftpMessage Load(uint protocolVersion, byte[] data, Encoding encoding) - { - var messageType = (SftpMessageTypes)data.FirstOrDefault(); - - return Load(protocolVersion, data, messageType, encoding); - } - - protected override int ZeroReaderIndex - { - get - { - return 1; - } - } - - public abstract SftpMessageTypes SftpMessageType { get; } - - protected override void LoadData() - { - } - - protected override void SaveData() - { - this.Write((byte)this.SftpMessageType); - } - - protected SftpFileAttributes ReadAttributes() - { - - var flag = this.ReadUInt32(); - - long size = -1; - int userId = -1; - int groupId = -1; - uint permissions = 0; - var accessTime = DateTime.MinValue; - var modifyTime = DateTime.MinValue; - IDictionary extensions = null; - - if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE - { - size = (long)this.ReadUInt64(); - } - - if ((flag & 0x00000002) == 0x00000002) // SSH_FILEXFER_ATTR_UIDGID - { - userId = (int)this.ReadUInt32(); - - groupId = (int)this.ReadUInt32(); - } - - if ((flag & 0x00000004) == 0x00000004) // SSH_FILEXFER_ATTR_PERMISSIONS - { - permissions = this.ReadUInt32(); - } - - if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME - { - var time = this.ReadUInt32(); - accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000); - time = this.ReadUInt32(); - modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000); - } - - if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_ACMODTIME - { - var extendedCount = this.ReadUInt32(); - extensions = this.ReadExtensionPair(); - } - var attributes = new SftpFileAttributes(accessTime, modifyTime, size, userId, groupId, permissions, extensions); - - return attributes; - } - - protected void Write(SftpFileAttributes attributes) - { - if (attributes == null) - { - this.Write((uint)0); - return; - } - else - { - UInt32 flag = 0; - - if (attributes.IsSizeChanged && attributes.IsRegularFile) - { - flag |= 0x00000001; - } - - if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) - { - flag |= 0x00000002; - } - - if (attributes.IsPermissionsChanged) - { - flag |= 0x00000004; - } - - if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) - { - flag |= 0x00000008; - } - - if (attributes.IsExtensionsChanged) - { - flag |= 0x80000000; - } - - this.Write(flag); - - if (attributes.IsSizeChanged && attributes.IsRegularFile) - { - this.Write((UInt64)attributes.Size); - } - - if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) - { - this.Write((UInt32)attributes.UserId); - this.Write((UInt32)attributes.GroupId); - } - - if (attributes.IsPermissionsChanged) - { - this.Write(attributes.Permissions); - } - - if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) - { - uint time = (uint)(attributes.LastAccessTime.ToFileTime() / 10000000 - 11644473600); - this.Write(time); - time = (uint)(attributes.LastWriteTime.ToFileTime() / 10000000 - 11644473600); - this.Write(time); - } - - if (attributes.IsExtensionsChanged) - { - this.Write(attributes.Extensions); - } - } - } - - private static SftpMessage Load(uint protocolVersion, byte[] data, SftpMessageTypes messageType, Encoding encoding) - { - SftpMessage message = null; - - switch (messageType) - { - case SftpMessageTypes.Version: - message = new SftpVersionResponse(); - break; - case SftpMessageTypes.Status: - message = new SftpStatusResponse(protocolVersion); - break; - case SftpMessageTypes.Data: - message = new SftpDataResponse(protocolVersion); - break; - case SftpMessageTypes.Handle: - message = new SftpHandleResponse(protocolVersion); - break; - case SftpMessageTypes.Name: - message = new SftpNameResponse(protocolVersion, encoding); - break; - case SftpMessageTypes.Attrs: - message = new SftpAttrsResponse(protocolVersion); - break; - case SftpMessageTypes.ExtendedReply: - message = new SftpExtendedReplyResponse(protocolVersion); - break; - default: - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", messageType)); - } - - message.LoadBytes(data); - - message.ResetReader(); - - message.LoadData(); - - return message; - } - - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "SFTP Message : {0}", this.SftpMessageType); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Renci.SshNet.Common; +using System.Globalization; +using Renci.SshNet.Sftp.Responses; +using System.Text; + +namespace Renci.SshNet.Sftp +{ + internal abstract class SftpMessage : SshData + { + public static SftpMessage Load(uint protocolVersion, byte[] data, Encoding encoding) + { + var messageType = (SftpMessageTypes)data.FirstOrDefault(); + + return Load(protocolVersion, data, messageType, encoding); + } + + protected override int ZeroReaderIndex + { + get + { + return 1; + } + } + + public abstract SftpMessageTypes SftpMessageType { get; } + + protected override void LoadData() + { + } + + protected override void SaveData() + { + this.Write((byte)this.SftpMessageType); + } + + protected SftpFileAttributes ReadAttributes() + { + + var flag = this.ReadUInt32(); + + long size = -1; + int userId = -1; + int groupId = -1; + uint permissions = 0; + var accessTime = DateTime.MinValue; + var modifyTime = DateTime.MinValue; + IDictionary extensions = null; + + if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE + { + size = (long)this.ReadUInt64(); + } + + if ((flag & 0x00000002) == 0x00000002) // SSH_FILEXFER_ATTR_UIDGID + { + userId = (int)this.ReadUInt32(); + + groupId = (int)this.ReadUInt32(); + } + + if ((flag & 0x00000004) == 0x00000004) // SSH_FILEXFER_ATTR_PERMISSIONS + { + permissions = this.ReadUInt32(); + } + + if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME + { + var time = this.ReadUInt32(); + accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000); + time = this.ReadUInt32(); + modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000); + } + + if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_ACMODTIME + { + var extendedCount = this.ReadUInt32(); + extensions = this.ReadExtensionPair(); + } + var attributes = new SftpFileAttributes(accessTime, modifyTime, size, userId, groupId, permissions, extensions); + + return attributes; + } + + protected void Write(SftpFileAttributes attributes) + { + if (attributes == null) + { + this.Write((uint)0); + return; + } + + UInt32 flag = 0; + + if (attributes.IsSizeChanged && attributes.IsRegularFile) + { + flag |= 0x00000001; + } + + if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) + { + flag |= 0x00000002; + } + + if (attributes.IsPermissionsChanged) + { + flag |= 0x00000004; + } + + if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) + { + flag |= 0x00000008; + } + + if (attributes.IsExtensionsChanged) + { + flag |= 0x80000000; + } + + this.Write(flag); + + if (attributes.IsSizeChanged && attributes.IsRegularFile) + { + this.Write((UInt64)attributes.Size); + } + + if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) + { + this.Write((UInt32)attributes.UserId); + this.Write((UInt32)attributes.GroupId); + } + + if (attributes.IsPermissionsChanged) + { + this.Write(attributes.Permissions); + } + + if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) + { + var time = (uint)(attributes.LastAccessTime.ToFileTime() / 10000000 - 11644473600); + this.Write(time); + time = (uint)(attributes.LastWriteTime.ToFileTime() / 10000000 - 11644473600); + this.Write(time); + } + + if (attributes.IsExtensionsChanged) + { + this.Write(attributes.Extensions); + } + } + + private static SftpMessage Load(uint protocolVersion, byte[] data, SftpMessageTypes messageType, Encoding encoding) + { + SftpMessage message; + + switch (messageType) + { + case SftpMessageTypes.Version: + message = new SftpVersionResponse(); + break; + case SftpMessageTypes.Status: + message = new SftpStatusResponse(protocolVersion); + break; + case SftpMessageTypes.Data: + message = new SftpDataResponse(protocolVersion); + break; + case SftpMessageTypes.Handle: + message = new SftpHandleResponse(protocolVersion); + break; + case SftpMessageTypes.Name: + message = new SftpNameResponse(protocolVersion, encoding); + break; + case SftpMessageTypes.Attrs: + message = new SftpAttrsResponse(protocolVersion); + break; + case SftpMessageTypes.ExtendedReply: + message = new SftpExtendedReplyResponse(protocolVersion); + break; + default: + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", messageType)); + } + + message.LoadBytes(data); + + message.ResetReader(); + + message.LoadData(); + + return message; + } + + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "SFTP Message : {0}", this.SftpMessageType); + } + } +} diff --git a/Renci.SshNet/Sftp/SftpMessageTypes.cs b/Renci.SshNet/Sftp/SftpMessageTypes.cs index 77ab9bc..b87bb8d 100644 --- a/Renci.SshNet/Sftp/SftpMessageTypes.cs +++ b/Renci.SshNet/Sftp/SftpMessageTypes.cs @@ -1,130 +1,130 @@ - -namespace Renci.SshNet.Sftp -{ - public enum SftpMessageTypes : byte - { - /// - /// SSH_FXP_INIT - /// - Init = 1, - /// - /// SSH_FXP_VERSION - /// - Version = 2, - /// - /// SSH_FXP_OPEN - /// - Open = 3, - /// - /// SSH_FXP_CLOSE - /// - Close = 4, - /// - /// SSH_FXP_READ - /// - Read = 5, - /// - /// SSH_FXP_WRITE - /// - Write = 6, - /// - /// SSH_FXP_LSTAT - /// - LStat = 7, - /// - /// SSH_FXP_FSTAT - /// - FStat = 8, - /// - /// SSH_FXP_SETSTAT - /// - SetStat = 9, - /// - /// SSH_FXP_FSETSTAT - /// - FSetStat = 10, - /// - /// SSH_FXP_OPENDIR - /// - OpenDir = 11, - /// - /// SSH_FXP_READDIR - /// - ReadDir = 12, - /// - /// SSH_FXP_REMOVE - /// - Remove = 13, - /// - /// SSH_FXP_MKDIR - /// - MkDir = 14, - /// - /// SSH_FXP_RMDIR - /// - RmDir = 15, - /// - /// SSH_FXP_REALPATH - /// - RealPath = 16, - /// - /// SSH_FXP_STAT - /// - Stat = 17, - /// - /// SSH_FXP_RENAME - /// - Rename = 18, - /// - /// SSH_FXP_READLINK - /// - ReadLink = 19, - /// - /// SSH_FXP_SYMLINK - /// - SymLink = 20, - /// - /// SSH_FXP_LINK - /// - Link = 21, - /// - /// SSH_FXP_BLOCK - /// - Block = 22, - /// - /// SSH_FXP_UNBLOCK - /// - Unblock = 23, - - /// - /// SSH_FXP_STATUS - /// - Status = 101, - /// - /// SSH_FXP_HANDLE - /// - Handle = 102, - /// - /// SSH_FXP_DATA - /// - Data = 103, - /// - /// SSH_FXP_NAME - /// - Name = 104, - /// - /// SSH_FXP_ATTRS - /// - Attrs = 105, - - /// - /// SSH_FXP_EXTENDED - /// - Extended = 200, - /// - /// SSH_FXP_EXTENDED_REPLY - /// - ExtendedReply = 201 - - } -} + +namespace Renci.SshNet.Sftp +{ + internal enum SftpMessageTypes : byte + { + /// + /// SSH_FXP_INIT + /// + Init = 1, + /// + /// SSH_FXP_VERSION + /// + Version = 2, + /// + /// SSH_FXP_OPEN + /// + Open = 3, + /// + /// SSH_FXP_CLOSE + /// + Close = 4, + /// + /// SSH_FXP_READ + /// + Read = 5, + /// + /// SSH_FXP_WRITE + /// + Write = 6, + /// + /// SSH_FXP_LSTAT + /// + LStat = 7, + /// + /// SSH_FXP_FSTAT + /// + FStat = 8, + /// + /// SSH_FXP_SETSTAT + /// + SetStat = 9, + /// + /// SSH_FXP_FSETSTAT + /// + FSetStat = 10, + /// + /// SSH_FXP_OPENDIR + /// + OpenDir = 11, + /// + /// SSH_FXP_READDIR + /// + ReadDir = 12, + /// + /// SSH_FXP_REMOVE + /// + Remove = 13, + /// + /// SSH_FXP_MKDIR + /// + MkDir = 14, + /// + /// SSH_FXP_RMDIR + /// + RmDir = 15, + /// + /// SSH_FXP_REALPATH + /// + RealPath = 16, + /// + /// SSH_FXP_STAT + /// + Stat = 17, + /// + /// SSH_FXP_RENAME + /// + Rename = 18, + /// + /// SSH_FXP_READLINK + /// + ReadLink = 19, + /// + /// SSH_FXP_SYMLINK + /// + SymLink = 20, + /// + /// SSH_FXP_LINK + /// + Link = 21, + /// + /// SSH_FXP_BLOCK + /// + Block = 22, + /// + /// SSH_FXP_UNBLOCK + /// + Unblock = 23, + + /// + /// SSH_FXP_STATUS + /// + Status = 101, + /// + /// SSH_FXP_HANDLE + /// + Handle = 102, + /// + /// SSH_FXP_DATA + /// + Data = 103, + /// + /// SSH_FXP_NAME + /// + Name = 104, + /// + /// SSH_FXP_ATTRS + /// + Attrs = 105, + + /// + /// SSH_FXP_EXTENDED + /// + Extended = 200, + /// + /// SSH_FXP_EXTENDED_REPLY + /// + ExtendedReply = 201 + + } +} diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs index 765c4b1..60c7e55 100644 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ b/Renci.SshNet/Sftp/SftpSession.cs @@ -1,1111 +1,1159 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using System.Diagnostics; -using System.Collections.Generic; -using System.Globalization; -using Renci.SshNet.Sftp.Responses; -using Renci.SshNet.Sftp.Requests; - -namespace Renci.SshNet.Sftp -{ - public class SftpSession : SubsystemSession - { - private const int MAXIMUM_SUPPORTED_VERSION = 3; - - private const int MINIMUM_SUPPORTED_VERSION = 0; - - private Dictionary _requests = new Dictionary(); - - private List _data = new List(16 * 1024); - - private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); - - public IDictionary _supportedExtensions; - - /// - /// Gets remote working directory. - /// - public string WorkingDirectory { get; private set; } - - /// - /// Gets SFTP protocol version. - /// - public uint ProtocolVersion { get; private set; } - - private long _requestId; - /// - /// Gets the next request id for sftp session. - /// - public uint NextRequestId - { - get - { -#if WINDOWS_PHONE - lock (this) - { - this._requestId++; - } - - return (uint)this._requestId; -#else - return ((uint)Interlocked.Increment(ref this._requestId)); -#endif - } - } - - public SftpSession(Session session, TimeSpan operationTimeout, Encoding encoding) - : base(session, "sftp", operationTimeout, encoding) - { - } - - public void ChangeDirectory(string path) - { - var fullPath = this.GetCanonicalPath(path); - - var handle = this.RequestOpenDir(fullPath); - - this.RequestClose(handle); - - this.WorkingDirectory = fullPath; - } - - internal void SendMessage(SftpMessage sftpMessage) - { - var messageData = sftpMessage.GetBytes(); - - var data = new byte[4 + messageData.Length]; - - ((uint)messageData.Length).GetBytes().CopyTo(data, 0); - messageData.CopyTo(data, 4); - - this.SendData(data); - } - - /// - /// Resolves path into absolute path on the server. - /// - /// Path to resolve. - /// Absolute path - internal string GetCanonicalPath(string path) - { - var fullPath = GetFullRemotePath(path); - - var canonizedPath = string.Empty; - - var realPathFiles = this.RequestRealPath(fullPath, true); - - if (realPathFiles != null) - { - canonizedPath = realPathFiles.First().Key; - } - - if (!string.IsNullOrEmpty(canonizedPath)) - return canonizedPath; - - // Check for special cases - if (fullPath.EndsWith("/.", StringComparison.InvariantCultureIgnoreCase) || - fullPath.EndsWith("/..", StringComparison.InvariantCultureIgnoreCase) || - fullPath.Equals("/", StringComparison.InvariantCultureIgnoreCase) || - fullPath.IndexOf('/') < 0) - return fullPath; - - var pathParts = fullPath.Split(new char[] { '/' }); - - var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1); - - if (string.IsNullOrEmpty(partialFullPath)) - partialFullPath = "/"; - - realPathFiles = this.RequestRealPath(partialFullPath, true); - - if (realPathFiles != null) - { - canonizedPath = realPathFiles.First().Key; - } - - if (string.IsNullOrEmpty(canonizedPath)) - { - return fullPath; - } - else - { - var slash = string.Empty; - if (canonizedPath[canonizedPath.Length - 1] != '/') - slash = "/"; - return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Length - 1]); - } - } - - internal string GetFullRemotePath(string path) - { - var fullPath = path; - - if (!string.IsNullOrEmpty(path) && path[0] != '/' && this.WorkingDirectory != null) - { - if (this.WorkingDirectory[this.WorkingDirectory.Length - 1] == '/') - { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.WorkingDirectory, path); - } - else - { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", this.WorkingDirectory, path); - } - } - return fullPath; - } - - protected override void OnChannelOpen() - { - this.SendMessage(new SftpInitRequest(MAXIMUM_SUPPORTED_VERSION)); - - this.WaitHandle(this._sftpVersionConfirmed, this._operationTimeout); - - if (this.ProtocolVersion > MAXIMUM_SUPPORTED_VERSION || this.ProtocolVersion < MINIMUM_SUPPORTED_VERSION) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", this.ProtocolVersion)); - } - - // Resolve current directory - this.WorkingDirectory = this.RequestRealPath(".").First().Key; - } - - protected override void OnDataReceived(uint dataTypeCode, byte[] data) - { - // Add channel data to internal data holder - this._data.AddRange(data); - - while (this._data.Count > 4 + 1) - { - // Extract packet length - var packetLength = (this._data[0] << 24 | this._data[1] << 16 | this._data[2] << 8 | this._data[3]); - - // Check if complete packet data is available - if (this._data.Count < packetLength + 4) - { - // Wait for complete message to arrive first - break; - } - this._data.RemoveRange(0, 4); - - // Create buffer to hold packet data - var packetData = new byte[packetLength]; - - // Cope packet data to array - this._data.CopyTo(0, packetData, 0, packetLength); - - // Remove loaded data from _data holder - this._data.RemoveRange(0, packetLength); - - // Load SFTP Message and handle it - var response = SftpMessage.Load(this.ProtocolVersion, packetData, this.Encoding); - - try - { - var versionResponse = response as SftpVersionResponse; - if (versionResponse != null) - { - this.ProtocolVersion = versionResponse.Version; - this._supportedExtensions = versionResponse.Extentions; - - this._sftpVersionConfirmed.Set(); - } - else - { - this.HandleResponse(response as SftpResponse); - } - } - catch (Exception exp) - { - this.RaiseError(exp); - break; - } - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - if (this._sftpVersionConfirmed != null) - { - this._sftpVersionConfirmed.Dispose(); - this._sftpVersionConfirmed = null; - } - } - } - - private void SendRequest(SftpRequest request) - { - lock (this._requests) - { - this._requests.Add(request.RequestId, request); - } - - this.SendMessage(request); - } - - #region SFTP API functions - - /// - /// Performs SSH_FXP_OPEN request - /// - /// The path. - /// The flags. - /// if set to true returns null instead of throwing an exception. - /// File handle. - public byte[] RequestOpen(string path, Flags flags, bool nullOnError = false) - { - byte[] handle = null; - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpOpenRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, flags, - (response) => - { - handle = response.Handle; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return handle; - } - - /// - /// Performs SSH_FXP_CLOSE request. - /// - /// The handle. - public void RequestClose(byte[] handle) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpCloseRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_READ request. - /// - /// The handle. - /// The offset. - /// The length. - /// data array; null if EOF - public byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length) - { - SshException exception = null; - - byte[] data = new byte[0]; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, length, - (response) => - { - data = response.Data; - wait.Set(); - }, - (response) => - { - if (response.StatusCode != StatusCodes.Eof) - { - exception = this.GetSftpException(response); - } - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - - return data; - } - - /// - /// Performs SSH_FXP_WRITE request. - /// - /// The handle. - /// The offset. - /// The data to send. - /// The wait event handle if needed. - public void RequestWrite(byte[] handle, UInt64 offset, byte[] data, EventWaitHandle wait, Action writeCompleted = null) - { - SshException exception = null; - - var request = new SftpWriteRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, data, - (response) => - { - if (writeCompleted != null) - { - writeCompleted(response); - } - - exception = this.GetSftpException(response); - if (wait != null) - wait.Set(); - }); - - this.SendRequest(request); - - if (wait != null) - this.WaitHandle(wait, this._operationTimeout); - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_LSTAT request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - public SftpFileAttributes RequestLStat(string path, bool nullOnError = false) - { - SshException exception = null; - - SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpLStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return attributes; - } - - /// - /// Performs SSH_FXP_FSTAT request. - /// - /// The handle. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - public SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false) - { - SshException exception = null; - SftpFileAttributes attributes = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpFStatRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return attributes; - } - - /// - /// Performs SSH_FXP_SETSTAT request. - /// - /// The path. - /// The attributes. - public void RequestSetStat(string path, SftpFileAttributes attributes) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpSetStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, attributes, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_FSETSTAT request. - /// - /// The handle. - /// The attributes. - public void RequestFSetStat(byte[] handle, SftpFileAttributes attributes) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpFSetStatRequest(this.ProtocolVersion, this.NextRequestId, handle, attributes, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_OPENDIR request - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// File handle. - public byte[] RequestOpenDir(string path, bool nullOnError = false) - { - SshException exception = null; - - byte[] handle = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpOpenDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - handle = response.Handle; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return handle; - } - - /// - /// Performs SSH_FXP_READDIR request - /// - /// The handle. - /// - public KeyValuePair[] RequestReadDir(byte[] handle) - { - SshException exception = null; - - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadDirRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - result = response.Files; - wait.Set(); - }, - (response) => - { - if (response.StatusCode != StatusCodes.Eof) - { - exception = this.GetSftpException(response); - } - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - - return result; - } - - /// - /// Performs SSH_FXP_REMOVE request. - /// - /// The path. - public void RequestRemove(string path) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRemoveRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_MKDIR request. - /// - /// The path. - public void RequestMkDir(string path) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpMkDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_RMDIR request. - /// - /// The path. - public void RequestRmDir(string path) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRmDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_REALPATH request - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - public KeyValuePair[] RequestRealPath(string path, bool nullOnError = false) - { - SshException exception = null; - - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRealPathRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - result = response.Files; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return result; - } - - /// - /// Performs SSH_FXP_STAT request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - public SftpFileAttributes RequestStat(string path, bool nullOnError = false) - { - SshException exception = null; - - SftpFileAttributes attributes = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return attributes; - } - - /// - /// Performs SSH_FXP_RENAME request. - /// - /// The old path. - /// The new path. - public void RequestRename(string oldPath, string newPath) - { - if (this.ProtocolVersion < 2) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_READLINK request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - internal KeyValuePair[] RequestReadLink(string path, bool nullOnError = false) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadLinkRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - result = response.Files; - - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return result; - } - - /// - /// Performs SSH_FXP_SYMLINK request. - /// - /// The linkpath. - /// The targetpath. - internal void RequestSymLink(string linkpath, string targetpath) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpSymLinkRequest(this.ProtocolVersion, this.NextRequestId, linkpath, targetpath, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - #endregion - - #region SFTP Extended API functions - - /// - /// Performs posix-rename@openssh.com extended request. - /// - /// The old path. - /// The new path. - public void RequestPosixRename(string oldPath, string newPath) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new PosixRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs statvfs@openssh.com extended request. - /// - /// The path. - /// if set to true [null on error]. - /// - public SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - SftpFileSytemInformation information = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new StatVfsRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - information = response.GetReply().Information; - - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return information; - } - - /// - /// Performs fstatvfs@openssh.com extended request. - /// - /// The file handle. - /// if set to true [null on error]. - /// - /// - internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - SftpFileSytemInformation information = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new FStatVfsRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - information = response.GetReply().Information; - - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return information; - } - - /// - /// Performs hardlink@openssh.com extended request. - /// - /// The old path. - /// The new path. - internal void HardLink(string oldPath, string newPath) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new HardLinkRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - #endregion - - private SshException GetSftpException(SftpStatusResponse response) - { - if (response.StatusCode == StatusCodes.Ok) - { - return null; - } - if (response.StatusCode == StatusCodes.PermissionDenied) - { - return new SftpPermissionDeniedException(response.ErrorMessage); - } - else if (response.StatusCode == StatusCodes.NoSuchFile) - { - return new SftpPathNotFoundException(response.ErrorMessage); - } - else - { - return new SshException(response.ErrorMessage); - } - } - - private void HandleResponse(SftpResponse response) - { - SftpRequest request = null; - lock (this._requests) - { - this._requests.TryGetValue(response.ResponseId, out request); - if (request != null) - { - this._requests.Remove(response.ResponseId); - } - } - - if (request == null) - throw new InvalidOperationException("Invalid response."); - - request.Complete(response); - } - } -} +using System; +using System.Linq; +using System.Text; +using System.Threading; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using System.Collections.Generic; +using System.Globalization; +using Renci.SshNet.Sftp.Responses; +using Renci.SshNet.Sftp.Requests; + +namespace Renci.SshNet.Sftp +{ + internal class SftpSession : SubsystemSession + { + private const int MAXIMUM_SUPPORTED_VERSION = 3; + + private const int MINIMUM_SUPPORTED_VERSION = 0; + + private readonly Dictionary _requests = new Dictionary(); + + private readonly List _data = new List(16 * 1024); + + private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); + + private IDictionary _supportedExtensions; + + /// + /// Gets remote working directory. + /// + public string WorkingDirectory { get; private set; } + + /// + /// Gets SFTP protocol version. + /// + public uint ProtocolVersion { get; private set; } + + private long _requestId; + /// + /// Gets the next request id for sftp session. + /// + public uint NextRequestId + { + get + { +#if WINDOWS_PHONE + lock (this) + { + this._requestId++; + } + + return (uint)this._requestId; +#else + return ((uint)Interlocked.Increment(ref this._requestId)); +#endif + } + } + + public SftpSession(Session session, TimeSpan operationTimeout, Encoding encoding) + : base(session, "sftp", operationTimeout, encoding) + { + } + + public void ChangeDirectory(string path) + { + var fullPath = this.GetCanonicalPath(path); + + var handle = this.RequestOpenDir(fullPath); + + this.RequestClose(handle); + + this.WorkingDirectory = fullPath; + } + + internal void SendMessage(SftpMessage sftpMessage) + { + var messageData = sftpMessage.GetBytes(); + + var data = new byte[4 + messageData.Length]; + + ((uint)messageData.Length).GetBytes().CopyTo(data, 0); + messageData.CopyTo(data, 4); + + this.SendData(data); + } + + /// + /// Resolves path into absolute path on the server. + /// + /// Path to resolve. + /// Absolute path + internal string GetCanonicalPath(string path) + { + var fullPath = GetFullRemotePath(path); + + var canonizedPath = string.Empty; + + var realPathFiles = this.RequestRealPath(fullPath, true); + + if (realPathFiles != null) + { + canonizedPath = realPathFiles.First().Key; + } + + if (!string.IsNullOrEmpty(canonizedPath)) + return canonizedPath; + + // Check for special cases + if (fullPath.EndsWith("/.", StringComparison.InvariantCultureIgnoreCase) || + fullPath.EndsWith("/..", StringComparison.InvariantCultureIgnoreCase) || + fullPath.Equals("/", StringComparison.InvariantCultureIgnoreCase) || + fullPath.IndexOf('/') < 0) + return fullPath; + + var pathParts = fullPath.Split(new char[] { '/' }); + + var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1); + + if (string.IsNullOrEmpty(partialFullPath)) + partialFullPath = "/"; + + realPathFiles = this.RequestRealPath(partialFullPath, true); + + if (realPathFiles != null) + { + canonizedPath = realPathFiles.First().Key; + } + + if (string.IsNullOrEmpty(canonizedPath)) + { + return fullPath; + } + + var slash = string.Empty; + if (canonizedPath[canonizedPath.Length - 1] != '/') + slash = "/"; + return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Length - 1]); + } + + internal string GetFullRemotePath(string path) + { + var fullPath = path; + + if (!string.IsNullOrEmpty(path) && path[0] != '/' && this.WorkingDirectory != null) + { + if (this.WorkingDirectory[this.WorkingDirectory.Length - 1] == '/') + { + fullPath = string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.WorkingDirectory, path); + } + else + { + fullPath = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", this.WorkingDirectory, path); + } + } + return fullPath; + } + + protected override void OnChannelOpen() + { + this.SendMessage(new SftpInitRequest(MAXIMUM_SUPPORTED_VERSION)); + + this.WaitOnHandle(this._sftpVersionConfirmed, this._operationTimeout); + + if (this.ProtocolVersion > MAXIMUM_SUPPORTED_VERSION || this.ProtocolVersion < MINIMUM_SUPPORTED_VERSION) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", this.ProtocolVersion)); + } + + // Resolve current directory + this.WorkingDirectory = this.RequestRealPath(".").First().Key; + } + + protected override void OnDataReceived(uint dataTypeCode, byte[] data) + { + // Add channel data to internal data holder + this._data.AddRange(data); + + while (this._data.Count > 4 + 1) + { + // Extract packet length + var packetLength = (this._data[0] << 24 | this._data[1] << 16 | this._data[2] << 8 | this._data[3]); + + // Check if complete packet data is available + if (this._data.Count < packetLength + 4) + { + // Wait for complete message to arrive first + break; + } + this._data.RemoveRange(0, 4); + + // Create buffer to hold packet data + var packetData = new byte[packetLength]; + + // Cope packet data to array + this._data.CopyTo(0, packetData, 0, packetLength); + + // Remove loaded data from _data holder + this._data.RemoveRange(0, packetLength); + + // Load SFTP Message and handle it + var response = SftpMessage.Load(this.ProtocolVersion, packetData, this.Encoding); + + try + { + var versionResponse = response as SftpVersionResponse; + if (versionResponse != null) + { + this.ProtocolVersion = versionResponse.Version; + this._supportedExtensions = versionResponse.Extentions; + + this._sftpVersionConfirmed.Set(); + } + else + { + this.HandleResponse(response as SftpResponse); + } + } + catch (Exception exp) + { + this.RaiseError(exp); + break; + } + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + if (this._sftpVersionConfirmed != null) + { + this._sftpVersionConfirmed.Dispose(); + this._sftpVersionConfirmed = null; + } + } + } + + private void SendRequest(SftpRequest request) + { + lock (this._requests) + { + this._requests.Add(request.RequestId, request); + } + + this.SendMessage(request); + } + + #region SFTP API functions + + /// + /// Performs SSH_FXP_OPEN request + /// + /// The path. + /// The flags. + /// if set to true returns null instead of throwing an exception. + /// File handle. + internal byte[] RequestOpen(string path, Flags flags, bool nullOnError = false) + { + byte[] handle = null; + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpOpenRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, flags, + response => + { + handle = response.Handle; + wait.Set(); + }, + response => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (!nullOnError && exception != null) + { + throw exception; + } + + return handle; + } + + /// + /// Performs SSH_FXP_CLOSE request. + /// + /// The handle. + internal void RequestClose(byte[] handle) + { + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpCloseRequest(this.ProtocolVersion, this.NextRequestId, handle, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_READ request. + /// + /// The handle. + /// The offset. + /// The length. + /// data array; null if EOF + internal byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length) + { + SshException exception = null; + + var data = new byte[0]; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpReadRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, length, + (response) => + { + data = response.Data; + wait.Set(); + }, + (response) => + { + if (response.StatusCode != StatusCodes.Eof) + { + exception = this.GetSftpException(response); + } + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + + return data; + } + + /// + /// Performs SSH_FXP_WRITE request. + /// + /// The handle. + /// The offset. + /// The data to send. + /// The wait event handle if needed. + /// The callback to invoke when the write has completed. + internal void RequestWrite(byte[] handle, UInt64 offset, byte[] data, EventWaitHandle wait, Action writeCompleted = null) + { + SshException exception = null; + + var request = new SftpWriteRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, data, + (response) => + { + if (writeCompleted != null) + { + writeCompleted(response); + } + + exception = this.GetSftpException(response); + if (wait != null) + wait.Set(); + }); + + this.SendRequest(request); + + if (wait != null) + this.WaitOnHandle(wait, this._operationTimeout); + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_LSTAT request. + /// + /// The path. + /// if set to true returns null instead of throwing an exception. + /// + /// File attributes + /// + internal SftpFileAttributes RequestLStat(string path, bool nullOnError = false) + { + SshException exception = null; + + SftpFileAttributes attributes = null; + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpLStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + attributes = response.Attributes; + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + + return attributes; + } + + /// + /// Performs SSH_FXP_FSTAT request. + /// + /// The handle. + /// if set to true returns null instead of throwing an exception. + /// + /// File attributes + /// + internal SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false) + { + SshException exception = null; + SftpFileAttributes attributes = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpFStatRequest(this.ProtocolVersion, this.NextRequestId, handle, + (response) => + { + attributes = response.Attributes; + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + + return attributes; + } + + /// + /// Performs SSH_FXP_SETSTAT request. + /// + /// The path. + /// The attributes. + internal void RequestSetStat(string path, SftpFileAttributes attributes) + { + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpSetStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, attributes, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_FSETSTAT request. + /// + /// The handle. + /// The attributes. + internal void RequestFSetStat(byte[] handle, SftpFileAttributes attributes) + { + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpFSetStatRequest(this.ProtocolVersion, this.NextRequestId, handle, attributes, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_OPENDIR request + /// + /// The path. + /// if set to true returns null instead of throwing an exception. + /// File handle. + internal byte[] RequestOpenDir(string path, bool nullOnError = false) + { + SshException exception = null; + + byte[] handle = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpOpenDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + handle = response.Handle; + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (!nullOnError && exception != null) + { + throw exception; + } + + return handle; + } + + /// + /// Performs SSH_FXP_READDIR request + /// + /// The handle. + /// + internal KeyValuePair[] RequestReadDir(byte[] handle) + { + SshException exception = null; + + KeyValuePair[] result = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpReadDirRequest(this.ProtocolVersion, this.NextRequestId, handle, + (response) => + { + result = response.Files; + wait.Set(); + }, + (response) => + { + if (response.StatusCode != StatusCodes.Eof) + { + exception = this.GetSftpException(response); + } + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + + return result; + } + + /// + /// Performs SSH_FXP_REMOVE request. + /// + /// The path. + internal void RequestRemove(string path) + { + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpRemoveRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_MKDIR request. + /// + /// The path. + internal void RequestMkDir(string path) + { + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpMkDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_RMDIR request. + /// + /// The path. + internal void RequestRmDir(string path) + { + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpRmDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_REALPATH request + /// + /// The path. + /// if set to true returns null instead of throwing an exception. + /// + internal KeyValuePair[] RequestRealPath(string path, bool nullOnError = false) + { + SshException exception = null; + + KeyValuePair[] result = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpRealPathRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + result = response.Files; + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (!nullOnError && exception != null) + { + throw exception; + } + + return result; + } + + /// + /// Performs SSH_FXP_STAT request. + /// + /// The path. + /// if set to true returns null instead of throwing an exception. + /// + /// File attributes + /// + internal SftpFileAttributes RequestStat(string path, bool nullOnError = false) + { + SshException exception = null; + + SftpFileAttributes attributes = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + attributes = response.Attributes; + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (!nullOnError && exception != null) + { + throw exception; + } + + return attributes; + } + + /// + /// Performs SSH_FXP_RENAME request. + /// + /// The old path. + /// The new path. + internal void RequestRename(string oldPath, string newPath) + { + if (this.ProtocolVersion < 2) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); + } + + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs SSH_FXP_READLINK request. + /// + /// The path. + /// if set to true returns null instead of throwing an exception. + /// + internal KeyValuePair[] RequestReadLink(string path, bool nullOnError = false) + { + if (this.ProtocolVersion < 3) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); + } + + SshException exception = null; + + KeyValuePair[] result = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpReadLinkRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + result = response.Files; + + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (!nullOnError && exception != null) + { + throw exception; + } + + return result; + } + + /// + /// Performs SSH_FXP_SYMLINK request. + /// + /// The linkpath. + /// The targetpath. + internal void RequestSymLink(string linkpath, string targetpath) + { + if (this.ProtocolVersion < 3) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); + } + + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new SftpSymLinkRequest(this.ProtocolVersion, this.NextRequestId, linkpath, targetpath, this.Encoding, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + #endregion + + #region SFTP Extended API functions + + /// + /// Performs posix-rename@openssh.com extended request. + /// + /// The old path. + /// The new path. + internal void RequestPosixRename(string oldPath, string newPath) + { + if (this.ProtocolVersion < 3) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); + } + + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new PosixRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + if (!this._supportedExtensions.ContainsKey(request.Name)) + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + /// + /// Performs statvfs@openssh.com extended request. + /// + /// The path. + /// if set to true [null on error]. + /// + internal SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false) + { + if (this.ProtocolVersion < 3) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); + } + + SshException exception = null; + + SftpFileSytemInformation information = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new StatVfsRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, + (response) => + { + information = response.GetReply().Information; + + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + if (!this._supportedExtensions.ContainsKey(request.Name)) + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (!nullOnError && exception != null) + { + throw exception; + } + + return information; + } + + /// + /// Performs fstatvfs@openssh.com extended request. + /// + /// The file handle. + /// if set to true [null on error]. + /// + /// + internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false) + { + if (this.ProtocolVersion < 3) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); + } + + SshException exception = null; + + SftpFileSytemInformation information = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new FStatVfsRequest(this.ProtocolVersion, this.NextRequestId, handle, + (response) => + { + information = response.GetReply().Information; + + wait.Set(); + }, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + if (!this._supportedExtensions.ContainsKey(request.Name)) + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (!nullOnError && exception != null) + { + throw exception; + } + + return information; + } + + /// + /// Performs hardlink@openssh.com extended request. + /// + /// The old path. + /// The new path. + internal void HardLink(string oldPath, string newPath) + { + if (this.ProtocolVersion < 3) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); + } + + SshException exception = null; + + using (var wait = new AutoResetEvent(false)) + { + var request = new HardLinkRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, + (response) => + { + exception = this.GetSftpException(response); + wait.Set(); + }); + + if (!this._supportedExtensions.ContainsKey(request.Name)) + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); + + this.SendRequest(request); + + this.WaitOnHandle(wait, this._operationTimeout); + } + + if (exception != null) + { + throw exception; + } + } + + #endregion + + /// + /// Calculates the optimal size of the buffer to read data from the channel. + /// + /// The buffer size configured on the client. + /// + /// The optimal size of the buffer to read data from the channel. + /// + internal uint CalculateOptimalReadLength(uint bufferSize) + { + // a SSH_FXP_DATA message has 13 bytes of protocol fields: + // bytes 1 to 4: packet length + // byte 5: message type + // bytes 6 to 9: response id + // bytes 10 to 13: length of payload‏ + // + // most ssh servers limit the size of the payload of a SSH_MSG_CHANNEL_DATA + // response to 16 KB; if we requested 16 KB of data, then the SSH_FXP_DATA + // payload of the SSH_MSG_CHANNEL_DATA message would be too big (16 KB + 13 bytes), and + // as a result, the ssh server would split this into two responses: + // one containing 16384 bytes (13 bytes header, and 16371 bytes file data) + // and one with the remaining 13 bytes of file data + const uint lengthOfNonDataProtocolFields = 13u; + var maximumPacketSize = Channel.LocalPacketSize; + return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields; + } + + /// + /// Calculates the optimal size of the buffer to write data on the channel. + /// + /// The buffer size configured on the client. + /// The file handle. + /// + /// The optimal size of the buffer to write data on the channel. + /// + /// + /// Currently, we do not take the remote window size into account. + /// + internal uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle) + { + // 1-4: package length of SSH_FXP_WRITE message + // 5: message type + // 6-9: request id + // 10-13: handle length + // + // 14-21: offset + // 22-25: data length + var lengthOfNonDataProtocolFields = 25u + (uint)handle.Length; + var maximumPacketSize = Channel.RemotePacketSize; + return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields; + } + + private SshException GetSftpException(SftpStatusResponse response) + { + if (response.StatusCode == StatusCodes.Ok) + { + return null; + } + if (response.StatusCode == StatusCodes.PermissionDenied) + { + return new SftpPermissionDeniedException(response.ErrorMessage); + } + else if (response.StatusCode == StatusCodes.NoSuchFile) + { + return new SftpPathNotFoundException(response.ErrorMessage); + } + else + { + return new SshException(response.ErrorMessage); + } + } + + private void HandleResponse(SftpResponse response) + { + SftpRequest request; + lock (this._requests) + { + this._requests.TryGetValue(response.ResponseId, out request); + if (request != null) + { + this._requests.Remove(response.ResponseId); + } + } + + if (request == null) + throw new InvalidOperationException("Invalid response."); + + request.Complete(response); + } + } +} diff --git a/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs b/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs index 84c219c..38b824f 100644 --- a/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs +++ b/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs @@ -1,40 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; -using System.IO; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous directory synchronization operation. - /// - public class SftpSynchronizeDirectoriesAsyncResult : AsyncResult> - { - /// - /// Gets the number of files read so far. - /// - public int FilesRead { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpSynchronizeDirectoriesAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - - } - - /// - /// Updates asynchronous operation status information. - /// - /// The files read. - internal void Update(int filesRead) - { - this.FilesRead = filesRead; - } - } -} +using System; +using System.Collections.Generic; +using Renci.SshNet.Common; +using System.IO; + +namespace Renci.SshNet.Sftp +{ + /// + /// Encapsulates the results of an asynchronous directory synchronization operation. + /// + public class SftpSynchronizeDirectoriesAsyncResult : AsyncResult> + { + /// + /// Gets the number of files read so far. + /// + public int FilesRead { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The async callback. + /// The state. + public SftpSynchronizeDirectoriesAsyncResult(AsyncCallback asyncCallback, Object state) + : base(asyncCallback, state) + { + } + + /// + /// Updates asynchronous operation status information. + /// + /// The files read. + internal void Update(int filesRead) + { + this.FilesRead = filesRead; + } + } +} diff --git a/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs b/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs index 375d359..521481d 100644 --- a/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs +++ b/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs @@ -1,51 +1,46 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous upload operation. - /// - public class SftpUploadAsyncResult : AsyncResult - { - /// - /// Gets or sets a value indicating whether to cancel asynchronous upload operation - /// - /// - /// true if upload operation to be canceled; otherwise, false. - /// - /// - /// Upload operation will be canceled after finishing uploading current buffer. - /// - public bool IsUploadCanceled { get; set; } - - /// - /// Gets the number of uploaded bytes. - /// - public ulong UploadedBytes { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpUploadAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - - } - - /// - /// Updates asynchronous operation status information. - /// - /// Number of uploaded bytes. - internal void Update(ulong uploadedBytes) - { - this.UploadedBytes = uploadedBytes; - } - - } -} +using System; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Sftp +{ + /// + /// Encapsulates the results of an asynchronous upload operation. + /// + public class SftpUploadAsyncResult : AsyncResult + { + /// + /// Gets or sets a value indicating whether to cancel asynchronous upload operation + /// + /// + /// true if upload operation to be canceled; otherwise, false. + /// + /// + /// Upload operation will be canceled after finishing uploading current buffer. + /// + public bool IsUploadCanceled { get; set; } + + /// + /// Gets the number of uploaded bytes. + /// + public ulong UploadedBytes { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The async callback. + /// The state. + public SftpUploadAsyncResult(AsyncCallback asyncCallback, Object state) + : base(asyncCallback, state) + { + } + + /// + /// Updates asynchronous operation status information. + /// + /// Number of uploaded bytes. + internal void Update(ulong uploadedBytes) + { + this.UploadedBytes = uploadedBytes; + } + } +} diff --git a/Renci.SshNet/Sftp/StatusCodes.cs b/Renci.SshNet/Sftp/StatusCodes.cs index ecb8d44..f13700b 100644 --- a/Renci.SshNet/Sftp/StatusCodes.cs +++ b/Renci.SshNet/Sftp/StatusCodes.cs @@ -1,135 +1,135 @@ - -namespace Renci.SshNet.Sftp -{ - public enum StatusCodes : uint - { - /// - /// SSH_FX_OK - /// - Ok = 0, - /// - /// SSH_FX_EOF - /// - Eof = 1, - /// - /// SSH_FX_NO_SUCH_FILE - /// - NoSuchFile = 2, - /// - /// SSH_FX_PERMISSION_DENIED - /// - PermissionDenied = 3, - /// - /// SSH_FX_FAILURE - /// - Failure = 4, - /// - /// SSH_FX_BAD_MESSAGE - /// - BadMessage = 5, - /// - /// SSH_FX_NO_CONNECTION - /// - NoConnection = 6, - /// - /// SSH_FX_CONNECTION_LOST - /// - ConnectionLost = 7, - /// - /// SSH_FX_OP_UNSUPPORTED - /// - OperationUnsupported = 8, - /// - /// SSH_FX_INVALID_HANDLE - /// - InvalidHandle = 9, - /// - /// SSH_FX_NO_SUCH_PATH - /// - NoSuchPath = 10, - /// - /// SSH_FX_FILE_ALREADY_EXISTS - /// - FileAlreadyExists = 11, - /// - /// SSH_FX_WRITE_PROTECT - /// - WriteProtect = 12, - /// - /// SSH_FX_NO_MEDIA - /// - NoMedia = 13, - /// - /// SSH_FX_NO_SPACE_ON_FILESYSTEM - /// - NoSpaceOnFilesystem = 14, - /// - /// SSH_FX_QUOTA_EXCEEDED - /// - QuotaExceeded = 15, - /// - /// SSH_FX_UNKNOWN_PRINCIPAL - /// - UnknownPrincipal = 16, - /// - /// SSH_FX_LOCK_CONFLICT - /// - LockConflict = 17, - /// - /// SSH_FX_DIR_NOT_EMPTY - /// - DirNotEmpty = 18, - /// - /// SSH_FX_NOT_A_DIRECTORY - /// - NotDirectory = 19, - /// - /// SSH_FX_INVALID_FILENAME - /// - InvalidFilename = 20, - /// - /// SSH_FX_LINK_LOOP - /// - LinkLoop = 21, - /// - /// SSH_FX_CANNOT_DELETE - /// - CannotDelete = 22, - /// - /// SSH_FX_INVALID_PARAMETER - /// - InvalidParameter = 23, - /// - /// SSH_FX_FILE_IS_A_DIRECTORY - /// - FileIsADirectory = 24, - /// - /// SSH_FX_BYTE_RANGE_LOCK_CONFLICT - /// - ByteRangeLockConflict = 25, - /// - /// SSH_FX_BYTE_RANGE_LOCK_REFUSED - /// - ByteRangeLockRefused = 26, - /// - /// SSH_FX_DELETE_PENDING - /// - DeletePending = 27, - /// - /// SSH_FX_FILE_CORRUPT - /// - FileCorrupt = 28, - /// - /// SSH_FX_OWNER_INVALID - /// - OwnerInvalid = 29, - /// - /// SSH_FX_GROUP_INVALID - /// - GroupInvalid = 30, - /// - /// SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK - /// - NoMatchingByteRangeLock = 31, - } -} + +namespace Renci.SshNet.Sftp +{ + internal enum StatusCodes : uint + { + /// + /// SSH_FX_OK + /// + Ok = 0, + /// + /// SSH_FX_EOF + /// + Eof = 1, + /// + /// SSH_FX_NO_SUCH_FILE + /// + NoSuchFile = 2, + /// + /// SSH_FX_PERMISSION_DENIED + /// + PermissionDenied = 3, + /// + /// SSH_FX_FAILURE + /// + Failure = 4, + /// + /// SSH_FX_BAD_MESSAGE + /// + BadMessage = 5, + /// + /// SSH_FX_NO_CONNECTION + /// + NoConnection = 6, + /// + /// SSH_FX_CONNECTION_LOST + /// + ConnectionLost = 7, + /// + /// SSH_FX_OP_UNSUPPORTED + /// + OperationUnsupported = 8, + /// + /// SSH_FX_INVALID_HANDLE + /// + InvalidHandle = 9, + /// + /// SSH_FX_NO_SUCH_PATH + /// + NoSuchPath = 10, + /// + /// SSH_FX_FILE_ALREADY_EXISTS + /// + FileAlreadyExists = 11, + /// + /// SSH_FX_WRITE_PROTECT + /// + WriteProtect = 12, + /// + /// SSH_FX_NO_MEDIA + /// + NoMedia = 13, + /// + /// SSH_FX_NO_SPACE_ON_FILESYSTEM + /// + NoSpaceOnFilesystem = 14, + /// + /// SSH_FX_QUOTA_EXCEEDED + /// + QuotaExceeded = 15, + /// + /// SSH_FX_UNKNOWN_PRINCIPAL + /// + UnknownPrincipal = 16, + /// + /// SSH_FX_LOCK_CONFLICT + /// + LockConflict = 17, + /// + /// SSH_FX_DIR_NOT_EMPTY + /// + DirNotEmpty = 18, + /// + /// SSH_FX_NOT_A_DIRECTORY + /// + NotDirectory = 19, + /// + /// SSH_FX_INVALID_FILENAME + /// + InvalidFilename = 20, + /// + /// SSH_FX_LINK_LOOP + /// + LinkLoop = 21, + /// + /// SSH_FX_CANNOT_DELETE + /// + CannotDelete = 22, + /// + /// SSH_FX_INVALID_PARAMETER + /// + InvalidParameter = 23, + /// + /// SSH_FX_FILE_IS_A_DIRECTORY + /// + FileIsADirectory = 24, + /// + /// SSH_FX_BYTE_RANGE_LOCK_CONFLICT + /// + ByteRangeLockConflict = 25, + /// + /// SSH_FX_BYTE_RANGE_LOCK_REFUSED + /// + ByteRangeLockRefused = 26, + /// + /// SSH_FX_DELETE_PENDING + /// + DeletePending = 27, + /// + /// SSH_FX_FILE_CORRUPT + /// + FileCorrupt = 28, + /// + /// SSH_FX_OWNER_INVALID + /// + OwnerInvalid = 29, + /// + /// SSH_FX_GROUP_INVALID + /// + GroupInvalid = 30, + /// + /// SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK + /// + NoMatchingByteRangeLock = 31, + } +} diff --git a/Renci.SshNet/SftpClient.NET.cs b/Renci.SshNet/SftpClient.NET.cs index a10e001..392229e 100644 --- a/Renci.SshNet/SftpClient.NET.cs +++ b/Renci.SshNet/SftpClient.NET.cs @@ -1,177 +1,165 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.IO; -using Renci.SshNet.Sftp; -using System.Text; -using Renci.SshNet.Common; -using System.Globalization; -using System.Threading; -using System.Diagnostics.CodeAnalysis; - -namespace Renci.SshNet -{ - /// - /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. - /// - public partial class SftpClient : BaseClient - { - #region SynchronizeDirectories - - /// - /// Synchronizes the directories. - /// - /// The source path. - /// The destination path. - /// The search pattern. - /// List of uploaded files. - public IEnumerable SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern) - { - return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, null); - } - - /// - /// Begins the synchronize directories. - /// - /// The source path. - /// The destination path. - /// The search pattern. - /// The async callback. - /// The state. - /// - /// An that represents the asynchronous directory synchronization. - /// - /// sourceDir - /// destDir - public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state) - { - if (sourcePath == null) - throw new ArgumentNullException("sourceDir"); - - if (destinationPath.IsNullOrWhiteSpace()) - throw new ArgumentException("destDir"); - - var asyncResult = new SftpSynchronizeDirectoriesAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - var result = this.InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asyncResult); - - asyncResult.SetAsCompleted(result, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends the synchronize directories. - /// - /// The async result. - /// List of uploaded files. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - public IEnumerable EndSynchronizeDirectories(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpSynchronizeDirectoriesAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - return ar.EndInvoke(); - } - - private IEnumerable InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult asynchResult) - { - if (destinationPath.IsNullOrWhiteSpace()) - throw new ArgumentException("destinationPath"); - - if (!Directory.Exists(sourcePath)) - throw new FileNotFoundException(string.Format("Source directory not found: {0}", sourcePath)); - - IList uploadedFiles = new List(); - - DirectoryInfo sourceDirectory = new DirectoryInfo(sourcePath); - -#if SILVERLIGHT - var sourceFiles = sourceDirectory.EnumerateFiles(searchPattern); -#else - var sourceFiles = sourceDirectory.GetFiles(searchPattern); -#endif - - if (sourceFiles == null || sourceFiles.Count() <= 0) - return uploadedFiles; - - try - { - #region Existing Files at The Destination - - var destFiles = InternalListDirectory(destinationPath, null); - Dictionary destDict = new Dictionary(); - foreach (var destFile in destFiles) - { - if (destFile.IsDirectory) - continue; - destDict.Add(destFile.Name, destFile); - } - - #endregion - - #region Upload the difference - - bool isDifferent = false; - Flags uploadFlag = Flags.Write | Flags.Truncate | Flags.CreateNewOrOpen; - foreach (var localFile in sourceFiles) - { - isDifferent = !destDict.ContainsKey(localFile.Name); - - if (!isDifferent) - { - SftpFile temp = destDict[localFile.Name]; - // TODO: Use md5 to detect a difference - //ltang: File exists at the destination => Using filesize to detect the difference - isDifferent = localFile.Length != temp.Length; - } - - if (isDifferent) - { - var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name); - try - { - using (var file = File.OpenRead(localFile.FullName)) - { - this.InternalUploadFile(file, remoteFileName, uploadFlag, null, null); - } - - uploadedFiles.Add(localFile); - - if (asynchResult != null) - { - asynchResult.Update(uploadedFiles.Count); - } - } - catch (Exception ex) - { - throw new Exception(string.Format("Failed to upload {0} to {1}", localFile.FullName, remoteFileName), ex); - } - } - } - - #endregion - } - catch - { - throw; - } - return uploadedFiles; - } - - #endregion - } -} +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using Renci.SshNet.Sftp; +using System.Globalization; + +namespace Renci.SshNet +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClient : BaseClient + { + #region SynchronizeDirectories + + /// + /// Synchronizes the directories. + /// + /// The source path. + /// The destination path. + /// The search pattern. + /// List of uploaded files. + public IEnumerable SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern) + { + return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, null); + } + + /// + /// Begins the synchronize directories. + /// + /// The source path. + /// The destination path. + /// The search pattern. + /// The async callback. + /// The state. + /// + /// An that represents the asynchronous directory synchronization. + /// + /// is null. + /// is null or contains only whitespace. + public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state) + { + if (sourcePath == null) + throw new ArgumentNullException("sourcePath"); + if (destinationPath.IsNullOrWhiteSpace()) + throw new ArgumentException("destDir"); + + var asyncResult = new SftpSynchronizeDirectoriesAsyncResult(asyncCallback, state); + + this.ExecuteThread(() => + { + try + { + var result = this.InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asyncResult); + + asyncResult.SetAsCompleted(result, false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, false); + } + }); + + return asyncResult; + } + + /// + /// Ends the synchronize directories. + /// + /// The async result. + /// List of uploaded files. + /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. + public IEnumerable EndSynchronizeDirectories(IAsyncResult asyncResult) + { + var ar = asyncResult as SftpSynchronizeDirectoriesAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + return ar.EndInvoke(); + } + + private IEnumerable InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult asynchResult) + { + if (destinationPath.IsNullOrWhiteSpace()) + throw new ArgumentException("destinationPath"); + + if (!Directory.Exists(sourcePath)) + throw new FileNotFoundException(string.Format("Source directory not found: {0}", sourcePath)); + + IList uploadedFiles = new List(); + + DirectoryInfo sourceDirectory = new DirectoryInfo(sourcePath); + +#if SILVERLIGHT + var sourceFiles = sourceDirectory.EnumerateFiles(searchPattern); +#else + var sourceFiles = sourceDirectory.GetFiles(searchPattern); +#endif + + if (sourceFiles == null || !sourceFiles.Any()) + return uploadedFiles; + + #region Existing Files at The Destination + + var destFiles = InternalListDirectory(destinationPath, null); + Dictionary destDict = new Dictionary(); + foreach (var destFile in destFiles) + { + if (destFile.IsDirectory) + continue; + destDict.Add(destFile.Name, destFile); + } + + #endregion + + #region Upload the difference + + const Flags uploadFlag = Flags.Write | Flags.Truncate | Flags.CreateNewOrOpen; + foreach (var localFile in sourceFiles) + { + bool isDifferent = !destDict.ContainsKey(localFile.Name); + + if (!isDifferent) + { + SftpFile temp = destDict[localFile.Name]; + // TODO: Use md5 to detect a difference + //ltang: File exists at the destination => Using filesize to detect the difference + isDifferent = localFile.Length != temp.Length; + } + + if (isDifferent) + { + var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name); + try + { + using (var file = File.OpenRead(localFile.FullName)) + { + this.InternalUploadFile(file, remoteFileName, uploadFlag, null, null); + } + + uploadedFiles.Add(localFile); + + if (asynchResult != null) + { + asynchResult.Update(uploadedFiles.Count); + } + } + catch (Exception ex) + { + throw new Exception(string.Format("Failed to upload {0} to {1}", localFile.FullName, remoteFileName), ex); + } + } + } + + #endregion + + return uploadedFiles; + } + + #endregion + } +} diff --git a/Renci.SshNet/SftpClient.NET40.cs b/Renci.SshNet/SftpClient.NET40.cs index 25f5c41..f373c70 100644 --- a/Renci.SshNet/SftpClient.NET40.cs +++ b/Renci.SshNet/SftpClient.NET40.cs @@ -1,22 +1,21 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Renci.SshNet -{ - /// - /// - /// - public partial class SftpClient - { - /// - /// - /// - /// - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem((o) => { action(); }); - } - } +using System; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// + /// + public partial class SftpClient + { + /// + /// + /// + /// + /// is null. + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem(o => action()); + } + } } \ No newline at end of file diff --git a/Renci.SshNet/SftpClient.cs b/Renci.SshNet/SftpClient.cs index 64791a9..12ade58 100644 --- a/Renci.SshNet/SftpClient.cs +++ b/Renci.SshNet/SftpClient.cs @@ -1,1584 +1,1763 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.IO; -using Renci.SshNet.Sftp; -using System.Text; -using Renci.SshNet.Common; -using System.Globalization; -using System.Threading; -using System.Diagnostics.CodeAnalysis; - -namespace Renci.SshNet -{ - /// - /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. - /// - public partial class SftpClient : BaseClient - { - /// - /// Holds SftpSession instance that used to communicate to the SFTP server - /// - private SftpSession _sftpSession; - - private bool _disposeConnectionInfo; - - /// - /// Gets or sets the operation timeout. - /// - /// The operation timeout. - public TimeSpan OperationTimeout { get; set; } - - /// - /// Gets or sets the size of the buffer. - /// - /// The size of the buffer. - public uint BufferSize { get; set; } - - /// - /// Gets remote working directory. - /// - public string WorkingDirectory - { - get - { - if (this._sftpSession == null) - return null; - return this._sftpSession.WorkingDirectory; - } - } - - /// - /// Gets sftp protocol version. - /// - public int ProtocolVersion { get; private set; } - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// is null. - public SftpClient(ConnectionInfo connectionInfo) - : base(connectionInfo) - { - this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); - this.BufferSize = 1024 * 16; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid. -or- is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SftpClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password)) - { - this._disposeConnectionInfo = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid. -or- is null contains whitespace characters. - public SftpClient(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid. -or- is nunullll or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles)) - { - this._disposeConnectionInfo = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid. -or- is null or contains whitespace characters. - public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) - { - } - - #endregion - - /// - /// Changes remote directory to path. - /// - /// New directory path. - /// is null. - /// Client is not connected. - /// Permission to change directory denied by remote host. -or- A SSH command was denied by the server. - /// The path in was not found on the remote host. - /// A SSH error where is the message from the remote host. - public void ChangeDirectory(string path) - { - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - this._sftpSession.ChangeDirectory(path); - } - - /// - /// Changes permissions of file(s) to specified mode. - /// - /// File(s) path, may match multiple files. - /// The mode. - /// is null. - /// Client is not connected. - /// Permission to change permission on the path(s) was denied by the remote host. -or- A SSH command was denied by the server. - /// The path in was not found on the remote host. - /// A SSH error where is the message from the remote host. - public void ChangePermissions(string path, short mode) - { - var file = this.Get(path); - - file.SetPermissions(mode); - } - - /// - /// Creates remote directory specified by path. - /// - /// Directory path to create. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to create the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public void CreateDirectory(string path) - { - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException(path); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestMkDir(fullPath); - } - - /// - /// Deletes remote directory specified by path. - /// - /// Directory to be deleted path. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to delete the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public void DeleteDirectory(string path) - { - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestRmDir(fullPath); - } - - /// - /// Deletes remote file specified by path. - /// - /// File to be deleted path. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to delete the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public void DeleteFile(string path) - { - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestRemove(fullPath); - } - - /// - /// Renames remote file from old path to new path. - /// - /// Path to the old file location. - /// Path to the new file location. - /// is null. -or- or is null. - /// Client is not connected. - /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public void RenameFile(string oldPath, string newPath) - { - this.RenameFile(oldPath, newPath, false); - } - - /// - /// Renames remote file from old path to new path. - /// - /// Path to the old file location. - /// Path to the new file location. - /// if set to true then perform a posix rename. - /// oldPath - /// is null. -or- or is null. - /// Client is not connected. - /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public void RenameFile(string oldPath, string newPath, bool isPosix) - { - if (oldPath == null) - throw new ArgumentNullException("oldPath"); - - if (newPath == null) - throw new ArgumentNullException("newPath"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var oldFullPath = this._sftpSession.GetCanonicalPath(oldPath); - - var newFullPath = this._sftpSession.GetCanonicalPath(newPath); - - if (isPosix) - { - this._sftpSession.RequestPosixRename(oldFullPath, newFullPath); - } - else - { - this._sftpSession.RequestRename(oldFullPath, newFullPath); - } - } - - /// - /// Creates a symbolic link from old path to new path. - /// - /// The old path. - /// The new path. - /// is null. -or- is null or contains whitespace characters. - /// Client is not connected. - /// Permission to create the symbolic link was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public void SymbolicLink(string path, string linkPath) - { - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (linkPath.IsNullOrWhiteSpace()) - throw new ArgumentException("linkPath"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var linkFullPath = this._sftpSession.GetCanonicalPath(linkPath); - - this._sftpSession.RequestSymLink(fullPath, linkFullPath); - } - - /// - /// Retrieves list of files in remote directory. - /// - /// The path. - /// The list callback. - /// - /// List of directory entries - /// - /// is null. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public IEnumerable ListDirectory(string path, Action listCallback = null) - { - return InternalListDirectory(path, listCallback); - } - - /// - /// Begins an asynchronous operation of retrieving list of files in remote directory. - /// - /// The path. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The list callback. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state, Action listCallback = null) - { - var asyncResult = new SftpListDirectoryAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - var result = this.InternalListDirectory(path, (count) => - { - asyncResult.Update(count); - - if (listCallback != null) - { - listCallback(count); - } - }); - - asyncResult.SetAsCompleted(result, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends an asynchronous operation of retrieving list of files in remote directory. - /// - /// The pending asynchronous SFTP request. - /// - /// List of files - /// - /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. - public IEnumerable EndListDirectory(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpListDirectoryAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - return ar.EndInvoke(); - } - - /// - /// Gets reference to remote file or directory. - /// - /// The path. - /// Reference to file object. - /// path - /// Client is not connected. - /// is null. - public SftpFile Get(string path) - { - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var attributes = this._sftpSession.RequestLStat(fullPath); - - return new SftpFile(this._sftpSession, fullPath, attributes); - } - - /// - /// Checks whether file pr directory exists; - /// - /// The path. - /// true if directory or file exists; otherwise false. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - public bool Exists(string path) - { - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetFullRemotePath(path); - - if (this._sftpSession.RequestRealPath(fullPath, true) == null) - { - return false; - } - else - { - return true; - } - } - - /// - /// Downloads remote file specified by the path into the stream. - /// - /// File to download. - /// Stream to write the file into. - /// The download callback. - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public void DownloadFile(string path, Stream output, Action downloadCallback = null) - { - this.InternalDownloadFile(path, output, null, downloadCallback); - } - - /// - /// Begins an asynchronous file downloading into the stream. - /// - /// The path. - /// The output. - /// - /// An that references the asynchronous operation. - /// - /// path - /// output - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginDownloadFile(string path, Stream output) - { - return this.BeginDownloadFile(path, output, null, null, null); - } - - /// - /// Begins an asynchronous file downloading into the stream. - /// - /// The path. - /// The output. - /// The method to be called when the asynchronous write operation is completed. - /// - /// An that references the asynchronous operation. - /// - /// path - /// output - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback) - { - return this.BeginDownloadFile(path, output, asyncCallback, null, null); - } - - /// - /// Begins an asynchronous file downloading into the stream. - /// - /// The path. - /// The output. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The download callback. - /// - /// An that references the asynchronous operation. - /// - /// path - /// output - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state, Action downloadCallback = null) - { - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (output == null) - throw new ArgumentNullException("output"); - - var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - this.InternalDownloadFile(path, output, asyncResult, (offset) => - { - asyncResult.Update(offset); - - if (downloadCallback != null) - { - downloadCallback(offset); - } - }); - - asyncResult.SetAsCompleted(null, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends an asynchronous file downloading into the stream. - /// - /// The pending asynchronous SFTP request. - /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. - public void EndDownloadFile(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpDownloadAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - ar.EndInvoke(); - } - - /// - /// Uploads stream into remote file.. - /// - /// Data input stream. - /// Remote file path. - /// The upload callback. - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public void UploadFile(Stream input, string path, Action uploadCallback = null) - { - this.UploadFile(input, path, true, uploadCallback); - } - - /// - /// Uploads stream into remote file.. - /// - /// Data input stream. - /// Remote file path. - /// if set to true then existing file will be overwritten. - /// The upload callback. - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public void UploadFile(Stream input, string path, bool canOverride, Action uploadCallback = null) - { - var flags = Flags.Write | Flags.Truncate; - - if (canOverride) - flags |= Flags.CreateNewOrOpen; - else - flags |= Flags.CreateNew; - - this.InternalUploadFile(input, path, flags, null, uploadCallback); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path) - { - return this.BeginUploadFile(input, path, true, null, null, null); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// The method to be called when the asynchronous write operation is completed. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback) - { - return this.BeginUploadFile(input, path, true, asyncCallback, null, null); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The upload callback. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state, Action uploadCallback = null) - { - return this.BeginUploadFile(input, path, true, asyncCallback, state, uploadCallback); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// if set to true then existing file will be overwritten. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The upload callback. - /// - /// An that references the asynchronous operation. - /// - /// input - /// path - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback asyncCallback, object state, Action uploadCallback = null) - { - if (input == null) - throw new ArgumentNullException("input"); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - var flags = Flags.Write | Flags.Truncate; - - if (canOverride) - flags |= Flags.CreateNewOrOpen; - else - flags |= Flags.CreateNew; - - var asyncResult = new SftpUploadAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - this.InternalUploadFile(input, path, flags, asyncResult, (offset) => - { - asyncResult.Update(offset); - - if (uploadCallback != null) - { - uploadCallback(offset); - } - - }); - - asyncResult.SetAsCompleted(null, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends an asynchronous uploading the steam into remote file. - /// - /// The pending asynchronous SFTP request. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. - public void EndUploadFile(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpUploadAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - ar.EndInvoke(); - } - - /// - /// Gets status using statvfs@openssh.com request. - /// - /// The path. - /// Reference to object that contains file status information. - /// path - /// Client is not connected. - /// is null. - public SftpFileSytemInformation GetStatus(string path) - { - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - return this._sftpSession.RequestStatVfs(fullPath); - } - - #region File Methods - - /// - /// Appends lines to a file, and then closes the file. - /// - /// The file to append the lines to. The file is created if it does not already exist. - /// The lines to append to the file. - /// isnull -or- is null. - public void AppendAllLines(string path, IEnumerable contents) - { - if (contents == null) - throw new ArgumentNullException("contents"); - - using (var stream = this.AppendText(path)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Appends lines to a file by using a specified encoding, and then closes the file. - /// - /// The file to append the lines to. The file is created if it does not already exist. - /// The lines to append to the file. - /// The character encoding to use. - /// is null. -or- is null. -or- is null. - public void AppendAllLines(string path, IEnumerable contents, Encoding encoding) - { - if (contents == null) - throw new ArgumentNullException("contents"); - - using (var stream = this.AppendText(path, encoding)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Opens a file, appends the specified string to the file, and then closes the file. - /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. - /// - /// The file to append the specified string to. - /// The string to append to the file. - /// is null. -or- is null. - public void AppendAllText(string path, string contents) - { - using (var stream = this.AppendText(path)) - { - stream.Write(contents); - } - } - - /// - /// Opens a file, appends the specified string to the file, and then closes the file. - /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. - /// - /// The file to append the specified string to. - /// The string to append to the file. - /// The character encoding to use. - /// is null. -or- is null. -or- is null. - public void AppendAllText(string path, string contents, Encoding encoding) - { - using (var stream = this.AppendText(path, encoding)) - { - stream.Write(contents); - } - } - - /// - /// Creates a that appends UTF-8 encoded text to an existing file. - /// - /// The path to the file to append to. - /// A StreamWriter that appends UTF-8 encoded text to an existing file. - /// is null. - public StreamWriter AppendText(string path) - { - return this.AppendText(path, Encoding.UTF8); - } - - /// - /// Creates a that appends UTF-8 encoded text to an existing file. - /// - /// The path to the file to append to. - /// The character encoding to use. - /// - /// A StreamWriter that appends UTF-8 encoded text to an existing file. - /// - /// is null. -or- is null. - public StreamWriter AppendText(string path, Encoding encoding) - { - if (encoding == null) - throw new ArgumentNullException("encoding"); - - return new StreamWriter(new SftpFileStream(this._sftpSession, path, FileMode.Append, FileAccess.Write), encoding); - } - - /// - /// Creates or overwrites a file in the specified path. - /// - /// The path and name of the file to create. - /// A that provides read/write access to the file specified in path - /// is null. - public SftpFileStream Create(string path) - { - return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite); - } - - /// - /// Creates or overwrites the specified file. - /// - /// The path and name of the file to create. - /// The number of bytes buffered for reads and writes to the file. - /// A that provides read/write access to the file specified in path - /// is null. - public SftpFileStream Create(string path, int bufferSize) - { - return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite, bufferSize); - } - - /// - /// Creates or opens a file for writing UTF-8 encoded text. - /// - /// The file to be opened for writing. - /// A that writes to the specified file using UTF-8 encoding. - /// is null. - public StreamWriter CreateText(string path) - { - return new StreamWriter(this.OpenWrite(path), Encoding.UTF8); - } - - /// - /// Creates or opens a file for writing UTF-8 encoded text. - /// - /// The file to be opened for writing. - /// The character encoding to use. - /// A that writes to the specified file using UTF-8 encoding. - /// is null. - public StreamWriter CreateText(string path, Encoding encoding) - { - return new StreamWriter(this.OpenWrite(path), encoding); - } - - /// - /// Deletes the specified file or directory. An exception is not thrown if the specified file does not exist. - /// - /// The name of the file or directory to be deleted. Wildcard characters are not supported. - /// is null. - /// Client is not connected. - public void Delete(string path) - { - var file = this.Get(path); - - file.Delete(); - } - - /// - /// Returns the date and time the specified file or directory was last accessed. - /// - /// The file or directory for which to obtain access date and time information. - /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in local time. - /// is null. - /// Client is not connected. - public DateTime GetLastAccessTime(string path) - { - var file = this.Get(path); - - return file.LastAccessTime; - } - - /// - /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last accessed. - /// - /// The file or directory for which to obtain access date and time information. - /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in UTC time. - /// is null. - /// Client is not connected. - public DateTime GetLastAccessTimeUtc(string path) - { - var file = this.Get(path); - - return file.LastAccessTime.ToUniversalTime(); - } - - /// - /// Returns the date and time the specified file or directory was last written to. - /// - /// The file or directory for which to obtain write date and time information. - /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in local time. - /// is null. - /// Client is not connected. - public DateTime GetLastWriteTime(string path) - { - var file = this.Get(path); - - return file.LastWriteTime; - } - - /// - /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to. - /// - /// The file or directory for which to obtain write date and time information. - /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in UTC time. - /// is null. - /// Client is not connected. - public DateTime GetLastWriteTimeUtc(string path) - { - var file = this.Get(path); - - return file.LastWriteTime.ToUniversalTime(); - } - - /// - /// Opens a on the specified path with read/write access. - /// - /// The file to open. - /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. - /// An unshared that provides access to the specified file, with the specified mode and access. - /// is null. - public SftpFileStream Open(string path, FileMode mode) - { - return new SftpFileStream(this._sftpSession, path, mode, FileAccess.ReadWrite); - } - - /// - /// Opens a on the specified path, with the specified mode and access. - /// - /// The file to open. - /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. - /// A value that specifies the operations that can be performed on the file. - /// An unshared that provides access to the specified file, with the specified mode and access. - /// is null. - public SftpFileStream Open(string path, FileMode mode, FileAccess access) - { - return new SftpFileStream(this._sftpSession, path, mode, access); - } - - /// - /// Opens an existing file for reading. - /// - /// The file to be opened for reading. - /// A read-only System.IO.FileStream on the specified path. - /// is null. - public SftpFileStream OpenRead(string path) - { - return new SftpFileStream(this._sftpSession, path, FileMode.Open, FileAccess.Read); - } - - /// - /// Opens an existing UTF-8 encoded text file for reading. - /// - /// The file to be opened for reading. - /// A on the specified path. - /// is null. - public StreamReader OpenText(string path) - { - return new StreamReader(this.OpenRead(path), Encoding.UTF8); - } - - /// - /// Opens an existing file for writing. - /// - /// The file to be opened for writing. - /// An unshared object on the specified path with access. - /// is null. - public SftpFileStream OpenWrite(string path) - { - return new SftpFileStream(this._sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write); - } - - /// - /// Opens a binary file, reads the contents of the file into a byte array, and then closes the file. - /// - /// The file to open for reading. - /// A byte array containing the contents of the file. - /// is null. - public byte[] ReadAllBytes(string path) - { - using (var stream = this.OpenRead(path)) - { - var buffer = new byte[stream.Length]; - stream.Read(buffer, 0, buffer.Length); - return buffer; - } - } - - /// - /// Opens a text file, reads all lines of the file, and then closes the file. - /// - /// The file to open for reading. - /// A string array containing all lines of the file. - /// is null. - public string[] ReadAllLines(string path) - { - return this.ReadAllLines(path, Encoding.UTF8); - } - - /// - /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. - /// - /// The file to open for reading. - /// The encoding applied to the contents of the file. - /// A string array containing all lines of the file. - /// is null. - public string[] ReadAllLines(string path, Encoding encoding) - { - var lines = new List(); - using (var stream = new StreamReader(this.OpenRead(path), encoding)) - { - while (!stream.EndOfStream) - { - lines.Add(stream.ReadLine()); - } - } - return lines.ToArray(); - } - - /// - /// Opens a text file, reads all lines of the file, and then closes the file. - /// - /// The file to open for reading. - /// A string containing all lines of the file. - /// is null. - public string ReadAllText(string path) - { - return this.ReadAllText(path, Encoding.UTF8); - } - - /// - /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. - /// - /// The file to open for reading. - /// The encoding applied to the contents of the file. - /// A string containing all lines of the file. - /// is null. - public string ReadAllText(string path, Encoding encoding) - { - var lines = new List(); - using (var stream = new StreamReader(this.OpenRead(path), encoding)) - { - return stream.ReadToEnd(); - } - } - - /// - /// Reads the lines of a file. - /// - /// The file to read. - /// The lines of the file. - /// is null. - public IEnumerable ReadLines(string path) - { - return this.ReadAllLines(path); - } - - /// - /// Read the lines of a file that has a specified encoding. - /// - /// The file to read. - /// The encoding that is applied to the contents of the file. - /// The lines of the file. - /// is null. - public IEnumerable ReadLines(string path, Encoding encoding) - { - return this.ReadAllLines(path, encoding); - } - - /// - /// Sets the date and time the specified file was last accessed. - /// - /// The file for which to set the access date and time information. - /// A containing the value to set for the last access date and time of path. This value is expressed in local time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastAccessTime(string path, DateTime lastAccessTime) - { - throw new NotImplementedException(); - } - - /// - /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last accessed. - /// - /// The file for which to set the access date and time information. - /// A containing the value to set for the last access date and time of path. This value is expressed in UTC time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) - { - throw new NotImplementedException(); - } - - /// - /// Sets the date and time that the specified file was last written to. - /// - /// The file for which to set the date and time information. - /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in local time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastWriteTime(string path, DateTime lastWriteTime) - { - throw new NotImplementedException(); - } - - /// - /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last written to. - /// - /// The file for which to set the date and time information. - /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in UTC time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) - { - throw new NotImplementedException(); - } - - /// - /// Creates a new file, writes the specified byte array to the file, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The bytes to write to the file. - /// is null. - public void WriteAllBytes(string path, byte[] bytes) - { - using (var stream = this.OpenWrite(path)) - { - stream.Write(bytes, 0, bytes.Length); - } - } - - /// - /// Creates a new file, writes a collection of strings to the file, and then closes the file. - /// - /// The file to write to. - /// The lines to write to the file. - /// is null. - public void WriteAllLines(string path, IEnumerable contents) - { - this.WriteAllLines(path, contents, Encoding.UTF8); - } - - /// - /// Creates a new file, write the specified string array to the file, and then closes the file. - /// - /// The file to write to. - /// The string array to write to the file. - /// is null. - public void WriteAllLines(string path, string[] contents) - { - this.WriteAllLines(path, contents, Encoding.UTF8); - } - - /// - /// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file. - /// - /// The file to write to. - /// The lines to write to the file. - /// The character encoding to use. - /// is null. - public void WriteAllLines(string path, IEnumerable contents, Encoding encoding) - { - using (var stream = this.CreateText(path, encoding)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file. - /// - /// The file to write to. - /// The string array to write to the file. - /// An object that represents the character encoding applied to the string array. - /// is null. - public void WriteAllLines(string path, string[] contents, Encoding encoding) - { - using (var stream = this.CreateText(path, encoding)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Creates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The string to write to the file. - /// is null. - public void WriteAllText(string path, string contents) - { - using (var stream = this.CreateText(path)) - { - stream.Write(contents); - } - } - - /// - /// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The string to write to the file. - /// The encoding to apply to the string. - /// is null. - public void WriteAllText(string path, string contents, Encoding encoding) - { - using (var stream = this.CreateText(path, encoding)) - { - stream.Write(contents); - } - } - - /// - /// Gets the of the file on the path. - /// - /// The path to the file. - /// The of the file on the path. - /// is null. - public SftpFileAttributes GetAttributes(string path) - { - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - return this._sftpSession.RequestLStat(fullPath); - } - - /// - /// Sets the specified of the file on the specified path. - /// - /// The path to the file. - /// The desired . - /// is null. - public void SetAttributes(string path, SftpFileAttributes fileAttributes) - { - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestSetStat(fullPath, fileAttributes); - } - - // Please don't forget this when you implement these methods: is null. - //public FileSecurity GetAccessControl(string path); - //public FileSecurity GetAccessControl(string path, AccessControlSections includeSections); - //public DateTime GetCreationTime(string path); - //public DateTime GetCreationTimeUtc(string path); - //public void SetAccessControl(string path, FileSecurity fileSecurity); - //public void SetCreationTime(string path, DateTime creationTime); - //public void SetCreationTimeUtc(string path, DateTime creationTimeUtc); - - #endregion - - /// - /// Internals the list directory. - /// - /// The path. - /// The list callback. - /// - /// path - /// is null. - /// Client not connected. - private IEnumerable InternalListDirectory(string path, Action listCallback) - { - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var handle = this._sftpSession.RequestOpenDir(fullPath); - - var basePath = fullPath; - - if (!basePath.EndsWith("/")) - basePath = string.Format("{0}/", fullPath); - - var result = new List(); - - var files = this._sftpSession.RequestReadDir(handle); - - while (files != null) - { - result.AddRange(from f in files - select new SftpFile(this._sftpSession, string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), f.Value)); - - // Call callback to report number of files read - if (listCallback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => { listCallback(result.Count); }); - } - - files = this._sftpSession.RequestReadDir(handle); - } - - this._sftpSession.RequestClose(handle); - - return result; - } - - /// - /// Internals the download file. - /// - /// The path. - /// The output. - /// The download callback. - /// output - /// path - /// is null or contains whitespace. - /// is null. - /// Client not connected. - private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult asyncResult, Action downloadCallback) - { - if (output == null) - throw new ArgumentNullException("output"); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var handle = this._sftpSession.RequestOpen(fullPath, Flags.Read); - - ulong offset = 0; - - var data = this._sftpSession.RequestRead(handle, offset, this.BufferSize); - - // Read data while available - while (data.Length > 0) - { - // Cancel download - if (asyncResult != null && asyncResult.IsDownloadCanceled) - break; - - output.Write(data, 0, data.Length); - - output.Flush(); - - offset += (ulong)data.Length; - - // Call callback to report number of bytes read - if (downloadCallback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => { downloadCallback(offset); }); - } - - data = this._sftpSession.RequestRead(handle, offset, this.BufferSize); - } - - this._sftpSession.RequestClose(handle); - } - - /// - /// Internals the upload file. - /// - /// The input. - /// The path. - /// The flags. - /// The upload callback. - /// input - /// path - /// is null. - /// is null or contains whitespace. - /// Client not connected. - private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult asyncResult, Action uploadCallback) - { - if (input == null) - throw new ArgumentNullException("input"); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var handle = this._sftpSession.RequestOpen(fullPath, flags); - - ulong offset = 0; - - var buffer = new byte[this.BufferSize]; - - var bytesRead = input.Read(buffer, 0, buffer.Length); - var expectedResponses = 0; - var responseReceivedWaitHandle = new AutoResetEvent(false); - - do - { - // Cancel upload - if (asyncResult != null && asyncResult.IsUploadCanceled) - break; - - if (bytesRead > 0) - { - if (bytesRead < this.BufferSize) - { - // Replace buffer for last chunk of data - var data = new byte[bytesRead]; - Buffer.BlockCopy(buffer, 0, data, 0, bytesRead); - buffer = data; - } - - var writtenBytes = offset + (ulong)buffer.Length; - this._sftpSession.RequestWrite(handle, offset, buffer, null, (s) => - { - if (s.StatusCode == StatusCodes.Ok) - { - Interlocked.Decrement(ref expectedResponses); - responseReceivedWaitHandle.Set(); - - // Call callback to report number of bytes written - if (uploadCallback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => { uploadCallback(writtenBytes); }); - } - } - }); - Interlocked.Increment(ref expectedResponses); - - offset += (uint)bytesRead; - - bytesRead = input.Read(buffer, 0, buffer.Length); - } - else if (expectedResponses > 0) - { - // Wait for expectedResponses to change - this._sftpSession.WaitHandle(responseReceivedWaitHandle, this.OperationTimeout); - } - } while (expectedResponses > 0 || bytesRead > 0); - - this._sftpSession.RequestClose(handle); - } - - partial void ExecuteThread(Action action); - - /// - /// Called when client is connected to the server. - /// - protected override void OnConnected() - { - base.OnConnected(); - - this._sftpSession = new SftpSession(this.Session, this.OperationTimeout, this.ConnectionInfo.Encoding); - - this._sftpSession.Connect(); - - // Resolve current running version - this.ProtocolVersion = (int)this._sftpSession.ProtocolVersion; - } - - /// - /// Called when client is disconnecting from the server. - /// - protected override void OnDisconnecting() - { - base.OnDisconnecting(); - - this._sftpSession.Disconnect(); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - if (this._sftpSession != null) - { - this._sftpSession.Dispose(); - this._sftpSession = null; - } - - if (this._disposeConnectionInfo) - ((IDisposable)this.ConnectionInfo).Dispose(); - - base.Dispose(disposing); - } - } -} +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using Renci.SshNet.Sftp; +using System.Text; +using Renci.SshNet.Common; +using System.Globalization; +using System.Threading; +using System.Diagnostics.CodeAnalysis; + +namespace Renci.SshNet +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClient : BaseClient + { + /// + /// Holds the instance that used to communicate to the + /// SFTP server. + /// + private SftpSession _sftpSession; + + /// + /// Holds the operation timeout. + /// + private TimeSpan _operationTimeout; + + /// + /// Holds the size of the buffer. + /// + private uint _bufferSize; + + /// + /// Gets or sets the operation timeout. + /// + /// + /// The timeout to wait until an operation completes. The default value is negative + /// one (-1) milliseconds, which indicates an infinite time-out period. + /// + /// The method was called after the client was disposed. + public TimeSpan OperationTimeout + { + get + { + CheckDisposed(); + return _operationTimeout; + } + set + { + CheckDisposed(); + _operationTimeout = value; + } + } + + /// + /// Gets or sets the maximum size of the buffer in bytes. + /// + /// + /// The size of the buffer. The default buffer size is 65536 bytes (64 KB). + /// + /// + /// + /// For write operations, this limits the size of the payload for + /// individual SSH_FXP_WRITE messages. The actual size is always + /// capped at the maximum packet size supported by the peer + /// (minus the size of protocol fields). + /// + /// + /// For read operations, this controls the size of the payload which + /// is requested from the peer in each SSH_FXP_READ message. The peer + /// will send the requested number of bytes in one or more SSH_FXP_DATA + /// messages. To optimize the size of the SSH packets sent by the peer, + /// the actual requested size will take into account the size of the + /// SSH_FXP_DATA protocol fields. + /// + /// + /// The size of the each indivual SSH_FXP_DATA message is limited to the + /// local maximum packet size of the channel, which is set to 64 KB + /// for SSH.NET. However, the peer can limit this even further. + /// + /// + /// The method was called after the client was disposed. + public uint BufferSize + { + get + { + CheckDisposed(); + return _bufferSize; + } + set + { + CheckDisposed(); + _bufferSize = value; + } + } + + /// + /// Gets remote working directory. + /// + /// Client is not connected. + /// The method was called after the client was disposed. + public string WorkingDirectory + { + get + { + CheckDisposed(); + if (_sftpSession == null) + throw new SshConnectionException("Client not connected."); + return _sftpSession.WorkingDirectory; + } + } + + /// + /// Gets sftp protocol version. + /// + /// Client is not connected. + /// The method was called after the client was disposed. + public int ProtocolVersion + { + get + { + CheckDisposed(); + if (_sftpSession == null) + throw new SshConnectionException("Client not connected."); + return (int) _sftpSession.ProtocolVersion; + } + } + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// is null. + public SftpClient(ConnectionInfo connectionInfo) + : this(connectionInfo, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid. -or- is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public SftpClient(string host, int port, string username, string password) + : this(new PasswordConnectionInfo(host, port, username, password), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid. -or- is null contains whitespace characters. + public SftpClient(string host, string username, string password) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid. -or- is nunullll or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid. -or- is null or contains whitespace characters. + public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// Specified whether this instance owns the connection info. + /// is null. + /// + /// If is true, then the + /// connection info will be disposed when this instance is disposed. + /// + private SftpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo) + : base(connectionInfo, ownsConnectionInfo) + { + this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); + this.BufferSize = 1024 * 64; + } + + #endregion + + /// + /// Changes remote directory to path. + /// + /// New directory path. + /// is null. + /// Client is not connected. + /// Permission to change directory denied by remote host. -or- A SSH command was denied by the server. + /// The path in was not found on the remote host. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void ChangeDirectory(string path) + { + CheckDisposed(); + + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + this._sftpSession.ChangeDirectory(path); + } + + /// + /// Changes permissions of file(s) to specified mode. + /// + /// File(s) path, may match multiple files. + /// The mode. + /// is null. + /// Client is not connected. + /// Permission to change permission on the path(s) was denied by the remote host. -or- A SSH command was denied by the server. + /// The path in was not found on the remote host. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void ChangePermissions(string path, short mode) + { + var file = this.Get(path); + file.SetPermissions(mode); + } + + /// + /// Creates remote directory specified by path. + /// + /// Directory path to create. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to create the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void CreateDirectory(string path) + { + CheckDisposed(); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException(path); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestMkDir(fullPath); + } + + /// + /// Deletes remote directory specified by path. + /// + /// Directory to be deleted path. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to delete the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void DeleteDirectory(string path) + { + CheckDisposed(); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestRmDir(fullPath); + } + + /// + /// Deletes remote file specified by path. + /// + /// File to be deleted path. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to delete the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void DeleteFile(string path) + { + CheckDisposed(); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestRemove(fullPath); + } + + /// + /// Renames remote file from old path to new path. + /// + /// Path to the old file location. + /// Path to the new file location. + /// is null. -or- or is null. + /// Client is not connected. + /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void RenameFile(string oldPath, string newPath) + { + this.RenameFile(oldPath, newPath, false); + } + + /// + /// Renames remote file from old path to new path. + /// + /// Path to the old file location. + /// Path to the new file location. + /// if set to true then perform a posix rename. + /// is null. -or- or is null. + /// Client is not connected. + /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void RenameFile(string oldPath, string newPath, bool isPosix) + { + CheckDisposed(); + + if (oldPath == null) + throw new ArgumentNullException("oldPath"); + + if (newPath == null) + throw new ArgumentNullException("newPath"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var oldFullPath = this._sftpSession.GetCanonicalPath(oldPath); + + var newFullPath = this._sftpSession.GetCanonicalPath(newPath); + + if (isPosix) + { + this._sftpSession.RequestPosixRename(oldFullPath, newFullPath); + } + else + { + this._sftpSession.RequestRename(oldFullPath, newFullPath); + } + } + + /// + /// Creates a symbolic link from old path to new path. + /// + /// The old path. + /// The new path. + /// is null. -or- is null or contains whitespace characters. + /// Client is not connected. + /// Permission to create the symbolic link was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public void SymbolicLink(string path, string linkPath) + { + CheckDisposed(); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (linkPath.IsNullOrWhiteSpace()) + throw new ArgumentException("linkPath"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var linkFullPath = this._sftpSession.GetCanonicalPath(linkPath); + + this._sftpSession.RequestSymLink(fullPath, linkFullPath); + } + + /// + /// Retrieves list of files in remote directory. + /// + /// The path. + /// The list callback. + /// + /// List of directory entries + /// + /// is null. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public IEnumerable ListDirectory(string path, Action listCallback = null) + { + CheckDisposed(); + + return InternalListDirectory(path, listCallback); + } + + /// + /// Begins an asynchronous operation of retrieving list of files in remote directory. + /// + /// The path. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The list callback. + /// + /// An that references the asynchronous operation. + /// + /// The method was called after the client was disposed. + public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state, Action listCallback = null) + { + CheckDisposed(); + + var asyncResult = new SftpListDirectoryAsyncResult(asyncCallback, state); + + this.ExecuteThread(() => + { + try + { + var result = this.InternalListDirectory(path, count => + { + asyncResult.Update(count); + + if (listCallback != null) + { + listCallback(count); + } + }); + + asyncResult.SetAsCompleted(result, false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, false); + } + }); + + return asyncResult; + } + + /// + /// Ends an asynchronous operation of retrieving list of files in remote directory. + /// + /// The pending asynchronous SFTP request. + /// + /// List of files + /// + /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. + public IEnumerable EndListDirectory(IAsyncResult asyncResult) + { + var ar = asyncResult as SftpListDirectoryAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + return ar.EndInvoke(); + } + + /// + /// Gets reference to remote file or directory. + /// + /// The path. + /// Reference to file object. + /// Client is not connected. + /// is null. + /// The method was called after the client was disposed. + public SftpFile Get(string path) + { + CheckDisposed(); + + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var attributes = this._sftpSession.RequestLStat(fullPath); + + return new SftpFile(this._sftpSession, fullPath, attributes); + } + + /// + /// Checks whether file pr directory exists; + /// + /// The path. + /// true if directory or file exists; otherwise false. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + public bool Exists(string path) + { + CheckDisposed(); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetFullRemotePath(path); + + if (this._sftpSession.RequestRealPath(fullPath, true) == null) + { + return false; + } + return true; + } + + /// + /// Downloads remote file specified by the path into the stream. + /// + /// File to download. + /// Stream to write the file into. + /// The download callback. + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public void DownloadFile(string path, Stream output, Action downloadCallback = null) + { + CheckDisposed(); + + this.InternalDownloadFile(path, output, null, downloadCallback); + } + + /// + /// Begins an asynchronous file downloading into the stream. + /// + /// The path. + /// The output. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginDownloadFile(string path, Stream output) + { + return this.BeginDownloadFile(path, output, null, null, null); + } + + /// + /// Begins an asynchronous file downloading into the stream. + /// + /// The path. + /// The output. + /// The method to be called when the asynchronous write operation is completed. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback) + { + return this.BeginDownloadFile(path, output, asyncCallback, null, null); + } + + /// + /// Begins an asynchronous file downloading into the stream. + /// + /// The path. + /// The output. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The download callback. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state, Action downloadCallback = null) + { + CheckDisposed(); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (output == null) + throw new ArgumentNullException("output"); + + var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state); + + this.ExecuteThread(() => + { + try + { + this.InternalDownloadFile(path, output, asyncResult, offset => + { + asyncResult.Update(offset); + + if (downloadCallback != null) + { + downloadCallback(offset); + } + }); + + asyncResult.SetAsCompleted(null, false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, false); + } + }); + + return asyncResult; + } + + /// + /// Ends an asynchronous file downloading into the stream. + /// + /// The pending asynchronous SFTP request. + /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. + public void EndDownloadFile(IAsyncResult asyncResult) + { + var ar = asyncResult as SftpDownloadAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + ar.EndInvoke(); + } + + /// + /// Uploads stream into remote file.. + /// + /// Data input stream. + /// Remote file path. + /// The upload callback. + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public void UploadFile(Stream input, string path, Action uploadCallback = null) + { + this.UploadFile(input, path, true, uploadCallback); + } + + /// + /// Uploads stream into remote file.. + /// + /// Data input stream. + /// Remote file path. + /// if set to true then existing file will be overwritten. + /// The upload callback. + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public void UploadFile(Stream input, string path, bool canOverride, Action uploadCallback = null) + { + CheckDisposed(); + + var flags = Flags.Write | Flags.Truncate; + + if (canOverride) + flags |= Flags.CreateNewOrOpen; + else + flags |= Flags.CreateNew; + + this.InternalUploadFile(input, path, flags, null, uploadCallback); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path) + { + return this.BeginUploadFile(input, path, true, null, null, null); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// The method to be called when the asynchronous write operation is completed. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback) + { + return this.BeginUploadFile(input, path, true, asyncCallback, null, null); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The upload callback. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state, Action uploadCallback = null) + { + return this.BeginUploadFile(input, path, true, asyncCallback, state, uploadCallback); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// if set to true then existing file will be overwritten. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The upload callback. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback asyncCallback, object state, Action uploadCallback = null) + { + CheckDisposed(); + + if (input == null) + throw new ArgumentNullException("input"); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + var flags = Flags.Write | Flags.Truncate; + + if (canOverride) + flags |= Flags.CreateNewOrOpen; + else + flags |= Flags.CreateNew; + + var asyncResult = new SftpUploadAsyncResult(asyncCallback, state); + + this.ExecuteThread(() => + { + try + { + this.InternalUploadFile(input, path, flags, asyncResult, offset => + { + asyncResult.Update(offset); + + if (uploadCallback != null) + { + uploadCallback(offset); + } + + }); + + asyncResult.SetAsCompleted(null, false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, false); + } + }); + + return asyncResult; + } + + /// + /// Ends an asynchronous uploading the steam into remote file. + /// + /// The pending asynchronous SFTP request. + /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. + /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. + public void EndUploadFile(IAsyncResult asyncResult) + { + var ar = asyncResult as SftpUploadAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + ar.EndInvoke(); + } + + /// + /// Gets status using statvfs@openssh.com request. + /// + /// The path. + /// Reference to object that contains file status information. + /// Client is not connected. + /// is null. + /// The method was called after the client was disposed. + public SftpFileSytemInformation GetStatus(string path) + { + CheckDisposed(); + + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + return this._sftpSession.RequestStatVfs(fullPath); + } + + #region File Methods + + /// + /// Appends lines to a file, and then closes the file. + /// + /// The file to append the lines to. The file is created if it does not already exist. + /// The lines to append to the file. + /// isnull -or- is null. + /// The method was called after the client was disposed. + public void AppendAllLines(string path, IEnumerable contents) + { + CheckDisposed(); + + if (contents == null) + throw new ArgumentNullException("contents"); + + using (var stream = this.AppendText(path)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Appends lines to a file by using a specified encoding, and then closes the file. + /// + /// The file to append the lines to. The file is created if it does not already exist. + /// The lines to append to the file. + /// The character encoding to use. + /// is null. -or- is null. -or- is null. + /// The method was called after the client was disposed. + public void AppendAllLines(string path, IEnumerable contents, Encoding encoding) + { + CheckDisposed(); + + if (contents == null) + throw new ArgumentNullException("contents"); + + using (var stream = this.AppendText(path, encoding)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Opens a file, appends the specified string to the file, and then closes the file. + /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. + /// + /// The file to append the specified string to. + /// The string to append to the file. + /// is null. -or- is null. + /// The method was called after the client was disposed. + public void AppendAllText(string path, string contents) + { + using (var stream = this.AppendText(path)) + { + stream.Write(contents); + } + } + + /// + /// Opens a file, appends the specified string to the file, and then closes the file. + /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. + /// + /// The file to append the specified string to. + /// The string to append to the file. + /// The character encoding to use. + /// is null. -or- is null. -or- is null. + /// The method was called after the client was disposed. + public void AppendAllText(string path, string contents, Encoding encoding) + { + using (var stream = this.AppendText(path, encoding)) + { + stream.Write(contents); + } + } + + /// + /// Creates a that appends UTF-8 encoded text to an existing file. + /// + /// The path to the file to append to. + /// A StreamWriter that appends UTF-8 encoded text to an existing file. + /// is null. + /// The method was called after the client was disposed. + public StreamWriter AppendText(string path) + { + return this.AppendText(path, Encoding.UTF8); + } + + /// + /// Creates a that appends UTF-8 encoded text to an existing file. + /// + /// The path to the file to append to. + /// The character encoding to use. + /// + /// A StreamWriter that appends UTF-8 encoded text to an existing file. + /// + /// is null. -or- is null. + /// The method was called after the client was disposed. + public StreamWriter AppendText(string path, Encoding encoding) + { + CheckDisposed(); + + if (encoding == null) + throw new ArgumentNullException("encoding"); + + return new StreamWriter(new SftpFileStream(this._sftpSession, path, FileMode.Append, FileAccess.Write), encoding); + } + + /// + /// Creates or overwrites a file in the specified path. + /// + /// The path and name of the file to create. + /// A that provides read/write access to the file specified in path + /// is null. + /// The method was called after the client was disposed. + public SftpFileStream Create(string path) + { + CheckDisposed(); + + return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite); + } + + /// + /// Creates or overwrites the specified file. + /// + /// The path and name of the file to create. + /// The number of bytes buffered for reads and writes to the file. + /// A that provides read/write access to the file specified in path + /// is null. + /// The method was called after the client was disposed. + public SftpFileStream Create(string path, int bufferSize) + { + CheckDisposed(); + + return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite, bufferSize); + } + + /// + /// Creates or opens a file for writing UTF-8 encoded text. + /// + /// The file to be opened for writing. + /// A that writes to the specified file using UTF-8 encoding. + /// is null. + /// The method was called after the client was disposed. + public StreamWriter CreateText(string path) + { + return CreateText(path, Encoding.UTF8); + } + + /// + /// Creates or opens a file for writing UTF-8 encoded text. + /// + /// The file to be opened for writing. + /// The character encoding to use. + /// A that writes to the specified file using UTF-8 encoding. + /// is null. + /// The method was called after the client was disposed. + public StreamWriter CreateText(string path, Encoding encoding) + { + CheckDisposed(); + + return new StreamWriter(this.OpenWrite(path), encoding); + } + + /// + /// Deletes the specified file or directory. An exception is not thrown if the specified file does not exist. + /// + /// The name of the file or directory to be deleted. Wildcard characters are not supported. + /// is null. + /// Client is not connected. + /// The method was called after the client was disposed. + public void Delete(string path) + { + var file = this.Get(path); + file.Delete(); + } + + /// + /// Returns the date and time the specified file or directory was last accessed. + /// + /// The file or directory for which to obtain access date and time information. + /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in local time. + /// is null. + /// Client is not connected. + /// The method was called after the client was disposed. + public DateTime GetLastAccessTime(string path) + { + var file = this.Get(path); + return file.LastAccessTime; + } + + /// + /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last accessed. + /// + /// The file or directory for which to obtain access date and time information. + /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in UTC time. + /// is null. + /// Client is not connected. + /// The method was called after the client was disposed. + public DateTime GetLastAccessTimeUtc(string path) + { + var lastAccessTime = GetLastAccessTime(path); + return lastAccessTime.ToUniversalTime(); + } + + /// + /// Returns the date and time the specified file or directory was last written to. + /// + /// The file or directory for which to obtain write date and time information. + /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in local time. + /// is null. + /// Client is not connected. + /// The method was called after the client was disposed. + public DateTime GetLastWriteTime(string path) + { + var file = this.Get(path); + return file.LastWriteTime; + } + + /// + /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to. + /// + /// The file or directory for which to obtain write date and time information. + /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in UTC time. + /// is null. + /// Client is not connected. + /// The method was called after the client was disposed. + public DateTime GetLastWriteTimeUtc(string path) + { + var lastWriteTime = GetLastWriteTime(path); + return lastWriteTime.ToUniversalTime(); + } + + /// + /// Opens a on the specified path with read/write access. + /// + /// The file to open. + /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. + /// An unshared that provides access to the specified file, with the specified mode and access. + /// is null. + /// The method was called after the client was disposed. + public SftpFileStream Open(string path, FileMode mode) + { + return Open(path, mode, FileAccess.ReadWrite); + } + + /// + /// Opens a on the specified path, with the specified mode and access. + /// + /// The file to open. + /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. + /// A value that specifies the operations that can be performed on the file. + /// An unshared that provides access to the specified file, with the specified mode and access. + /// is null. + /// The method was called after the client was disposed. + public SftpFileStream Open(string path, FileMode mode, FileAccess access) + { + CheckDisposed(); + + return new SftpFileStream(this._sftpSession, path, mode, access, (int) _bufferSize); + } + + /// + /// Opens an existing file for reading. + /// + /// The file to be opened for reading. + /// A read-only System.IO.FileStream on the specified path. + /// is null. + /// The method was called after the client was disposed. + public SftpFileStream OpenRead(string path) + { + return Open(path, FileMode.Open, FileAccess.Read); + } + + /// + /// Opens an existing UTF-8 encoded text file for reading. + /// + /// The file to be opened for reading. + /// A on the specified path. + /// is null. + /// The method was called after the client was disposed. + public StreamReader OpenText(string path) + { + return new StreamReader(this.OpenRead(path), Encoding.UTF8); + } + + /// + /// Opens an existing file for writing. + /// + /// The file to be opened for writing. + /// An unshared object on the specified path with access. + /// is null. + /// The method was called after the client was disposed. + public SftpFileStream OpenWrite(string path) + { + CheckDisposed(); + + return new SftpFileStream(this._sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write, + (int) _bufferSize); + } + + /// + /// Opens a binary file, reads the contents of the file into a byte array, and then closes the file. + /// + /// The file to open for reading. + /// A byte array containing the contents of the file. + /// is null. + /// The method was called after the client was disposed. + public byte[] ReadAllBytes(string path) + { + using (var stream = this.OpenRead(path)) + { + var buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + return buffer; + } + } + + /// + /// Opens a text file, reads all lines of the file, and then closes the file. + /// + /// The file to open for reading. + /// A string array containing all lines of the file. + /// is null. + /// The method was called after the client was disposed. + public string[] ReadAllLines(string path) + { + return this.ReadAllLines(path, Encoding.UTF8); + } + + /// + /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. + /// + /// The file to open for reading. + /// The encoding applied to the contents of the file. + /// A string array containing all lines of the file. + /// is null. + /// The method was called after the client was disposed. + public string[] ReadAllLines(string path, Encoding encoding) + { + var lines = new List(); + using (var stream = new StreamReader(this.OpenRead(path), encoding)) + { + while (!stream.EndOfStream) + { + lines.Add(stream.ReadLine()); + } + } + return lines.ToArray(); + } + + /// + /// Opens a text file, reads all lines of the file, and then closes the file. + /// + /// The file to open for reading. + /// A string containing all lines of the file. + /// is null. + /// The method was called after the client was disposed. + public string ReadAllText(string path) + { + return this.ReadAllText(path, Encoding.UTF8); + } + + /// + /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. + /// + /// The file to open for reading. + /// The encoding applied to the contents of the file. + /// A string containing all lines of the file. + /// is null. + /// The method was called after the client was disposed. + public string ReadAllText(string path, Encoding encoding) + { + using (var stream = new StreamReader(this.OpenRead(path), encoding)) + { + return stream.ReadToEnd(); + } + } + + /// + /// Reads the lines of a file. + /// + /// The file to read. + /// The lines of the file. + /// is null. + /// The method was called after the client was disposed. + public IEnumerable ReadLines(string path) + { + return this.ReadAllLines(path); + } + + /// + /// Read the lines of a file that has a specified encoding. + /// + /// The file to read. + /// The encoding that is applied to the contents of the file. + /// The lines of the file. + /// is null. + /// The method was called after the client was disposed. + public IEnumerable ReadLines(string path, Encoding encoding) + { + return this.ReadAllLines(path, encoding); + } + + /// + /// Sets the date and time the specified file was last accessed. + /// + /// The file for which to set the access date and time information. + /// A containing the value to set for the last access date and time of path. This value is expressed in local time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastAccessTime(string path, DateTime lastAccessTime) + { + throw new NotImplementedException(); + } + + /// + /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last accessed. + /// + /// The file for which to set the access date and time information. + /// A containing the value to set for the last access date and time of path. This value is expressed in UTC time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) + { + throw new NotImplementedException(); + } + + /// + /// Sets the date and time that the specified file was last written to. + /// + /// The file for which to set the date and time information. + /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in local time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastWriteTime(string path, DateTime lastWriteTime) + { + throw new NotImplementedException(); + } + + /// + /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last written to. + /// + /// The file for which to set the date and time information. + /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in UTC time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) + { + throw new NotImplementedException(); + } + + /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. If the target file already exists, it is overwritten. + /// + /// The file to write to. + /// The bytes to write to the file. + /// is null. + /// The method was called after the client was disposed. + public void WriteAllBytes(string path, byte[] bytes) + { + using (var stream = this.OpenWrite(path)) + { + stream.Write(bytes, 0, bytes.Length); + } + } + + /// + /// Creates a new file, writes a collection of strings to the file, and then closes the file. + /// + /// The file to write to. + /// The lines to write to the file. + /// is null. + /// The method was called after the client was disposed. + public void WriteAllLines(string path, IEnumerable contents) + { + this.WriteAllLines(path, contents, Encoding.UTF8); + } + + /// + /// Creates a new file, write the specified string array to the file, and then closes the file. + /// + /// The file to write to. + /// The string array to write to the file. + /// is null. + /// The method was called after the client was disposed. + public void WriteAllLines(string path, string[] contents) + { + this.WriteAllLines(path, contents, Encoding.UTF8); + } + + /// + /// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file. + /// + /// The file to write to. + /// The lines to write to the file. + /// The character encoding to use. + /// is null. + /// The method was called after the client was disposed. + public void WriteAllLines(string path, IEnumerable contents, Encoding encoding) + { + using (var stream = this.CreateText(path, encoding)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file. + /// + /// The file to write to. + /// The string array to write to the file. + /// An object that represents the character encoding applied to the string array. + /// is null. + /// The method was called after the client was disposed. + public void WriteAllLines(string path, string[] contents, Encoding encoding) + { + using (var stream = this.CreateText(path, encoding)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Creates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten. + /// + /// The file to write to. + /// The string to write to the file. + /// is null. + /// The method was called after the client was disposed. + public void WriteAllText(string path, string contents) + { + using (var stream = this.CreateText(path)) + { + stream.Write(contents); + } + } + + /// + /// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten. + /// + /// The file to write to. + /// The string to write to the file. + /// The encoding to apply to the string. + /// is null. + /// The method was called after the client was disposed. + public void WriteAllText(string path, string contents, Encoding encoding) + { + using (var stream = this.CreateText(path, encoding)) + { + stream.Write(contents); + } + } + + /// + /// Gets the of the file on the path. + /// + /// The path to the file. + /// The of the file on the path. + /// is null. + /// The method was called after the client was disposed. + public SftpFileAttributes GetAttributes(string path) + { + CheckDisposed(); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + return this._sftpSession.RequestLStat(fullPath); + } + + /// + /// Sets the specified of the file on the specified path. + /// + /// The path to the file. + /// The desired . + /// is null. + /// The method was called after the client was disposed. + public void SetAttributes(string path, SftpFileAttributes fileAttributes) + { + CheckDisposed(); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestSetStat(fullPath, fileAttributes); + } + + // Please don't forget this when you implement these methods: is null. + //public FileSecurity GetAccessControl(string path); + //public FileSecurity GetAccessControl(string path, AccessControlSections includeSections); + //public DateTime GetCreationTime(string path); + //public DateTime GetCreationTimeUtc(string path); + //public void SetAccessControl(string path, FileSecurity fileSecurity); + //public void SetCreationTime(string path, DateTime creationTime); + //public void SetCreationTimeUtc(string path, DateTime creationTimeUtc); + + #endregion + + /// + /// Internals the list directory. + /// + /// The path. + /// The list callback. + /// + /// path + /// is null. + /// Client not connected. + private IEnumerable InternalListDirectory(string path, Action listCallback) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var handle = this._sftpSession.RequestOpenDir(fullPath); + + var basePath = fullPath; + + if (!basePath.EndsWith("/")) + basePath = string.Format("{0}/", fullPath); + + var result = new List(); + + var files = this._sftpSession.RequestReadDir(handle); + + while (files != null) + { + result.AddRange(from f in files + select new SftpFile(this._sftpSession, string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), f.Value)); + + // Call callback to report number of files read + if (listCallback != null) + { + // Execute callback on different thread + this.ExecuteThread(() => listCallback(result.Count)); + } + + files = this._sftpSession.RequestReadDir(handle); + } + + this._sftpSession.RequestClose(handle); + + return result; + } + + /// + /// Internals the download file. + /// + /// The path. + /// The output. + /// An that references the asynchronous request. + /// The download callback. + /// is null. + /// is null or contains whitespace. + /// Client not connected. + private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult asyncResult, Action downloadCallback) + { + if (output == null) + throw new ArgumentNullException("output"); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var handle = this._sftpSession.RequestOpen(fullPath, Flags.Read); + + ulong offset = 0; + + var optimalReadLength = _sftpSession.CalculateOptimalReadLength(_bufferSize); + + var data = this._sftpSession.RequestRead(handle, offset, optimalReadLength); + + // Read data while available + while (data.Length > 0) + { + // Cancel download + if (asyncResult != null && asyncResult.IsDownloadCanceled) + break; + + output.Write(data, 0, data.Length); + + output.Flush(); + + offset += (ulong)data.Length; + + // Call callback to report number of bytes read + if (downloadCallback != null) + { + // Execute callback on different thread + this.ExecuteThread(() => { downloadCallback(offset); }); + } + + data = this._sftpSession.RequestRead(handle, offset, optimalReadLength); + } + + this._sftpSession.RequestClose(handle); + } + + /// + /// Internals the upload file. + /// + /// The input. + /// The path. + /// The flags. + /// An that references the asynchronous request. + /// The upload callback. + /// is null. + /// is null or contains whitespace. + /// Client not connected. + private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult asyncResult, Action uploadCallback) + { + if (input == null) + throw new ArgumentNullException("input"); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var handle = this._sftpSession.RequestOpen(fullPath, flags); + + ulong offset = 0; + + // create buffer of optimal length + var buffer = new byte[_sftpSession.CalculateOptimalWriteLength(_bufferSize, handle)]; + + var bytesRead = input.Read(buffer, 0, buffer.Length); + var expectedResponses = 0; + var responseReceivedWaitHandle = new AutoResetEvent(false); + + do + { + // Cancel upload + if (asyncResult != null && asyncResult.IsUploadCanceled) + break; + + if (bytesRead > 0) + { + if (bytesRead < buffer.Length) + { + // Replace buffer for last chunk of data + var data = new byte[bytesRead]; + Buffer.BlockCopy(buffer, 0, data, 0, bytesRead); + buffer = data; + } + + var writtenBytes = offset + (ulong)buffer.Length; + this._sftpSession.RequestWrite(handle, offset, buffer, null, s => + { + if (s.StatusCode == StatusCodes.Ok) + { + Interlocked.Decrement(ref expectedResponses); + responseReceivedWaitHandle.Set(); + + // Call callback to report number of bytes written + if (uploadCallback != null) + { + // Execute callback on different thread + this.ExecuteThread(() => uploadCallback(writtenBytes)); + } + } + }); + Interlocked.Increment(ref expectedResponses); + + offset += (uint)bytesRead; + + bytesRead = input.Read(buffer, 0, buffer.Length); + } + else if (expectedResponses > 0) + { + // Wait for expectedResponses to change + this._sftpSession.WaitOnHandle(responseReceivedWaitHandle, this.OperationTimeout); + } + } while (expectedResponses > 0 || bytesRead > 0); + + this._sftpSession.RequestClose(handle); + } + + partial void ExecuteThread(Action action); + + /// + /// Called when client is connected to the server. + /// + protected override void OnConnected() + { + base.OnConnected(); + + this._sftpSession = new SftpSession(this.Session, this.OperationTimeout, this.ConnectionInfo.Encoding); + this._sftpSession.Connect(); + } + + /// + /// Called when client is disconnecting from the server. + /// + protected override void OnDisconnecting() + { + base.OnDisconnecting(); + + // disconnect and dispose the SFTP session + // the dispose is necessary since we create a new SFTP session + // on each connect + if (_sftpSession != null) + { + this._sftpSession.Disconnect(); + this._sftpSession.Dispose(); + this._sftpSession = null; + } + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected override void Dispose(bool disposing) + { + if (this._sftpSession != null) + { + this._sftpSession.Dispose(); + this._sftpSession = null; + } + + base.Dispose(disposing); + } + } +} diff --git a/Renci.SshNet/Shell.NET40.cs b/Renci.SshNet/Shell.NET40.cs index 0a08836..e3dfcf8 100644 --- a/Renci.SshNet/Shell.NET40.cs +++ b/Renci.SshNet/Shell.NET40.cs @@ -1,18 +1,17 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Renci.SshNet -{ - /// - /// Represents instance of the SSH shell object - /// - public partial class Shell - { - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem((o) => { action(); }); - } - } -} +using System; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Represents instance of the SSH shell object + /// + public partial class Shell + { + /// is null. + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem(o => action()); + } + } +} diff --git a/Renci.SshNet/Shell.cs b/Renci.SshNet/Shell.cs index 0b21b12..4ae1f2b 100644 --- a/Renci.SshNet/Shell.cs +++ b/Renci.SshNet/Shell.cs @@ -1,350 +1,346 @@ -using System; -using System.Linq; -using System.Diagnostics; -using System.IO; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Connection; -using System.Collections.Generic; - -namespace Renci.SshNet -{ - /// - /// Represents instance of the SSH shell object - /// - public partial class Shell : IDisposable - { - private readonly Session _session; - - private ChannelSession _channel; - - private EventWaitHandle _channelClosedWaitHandle; - - private Stream _input; - - private string _terminalName; - - private uint _columns; - - private uint _rows; - - private uint _width; - - private uint _height; - - private IDictionary _terminalModes; - - private EventWaitHandle _dataReaderTaskCompleted; - - private Stream _outputStream; - - private Stream _extendedOutputStream; - - private int _bufferSize; - - /// - /// Gets a value indicating whether this shell is started. - /// - /// - /// true if started is started; otherwise, false. - /// - public bool IsStarted { get; private set; } - - /// - /// Occurs when shell is starting. - /// - public event EventHandler Starting; - - /// - /// Occurs when shell is started. - /// - public event EventHandler Started; - - /// - /// Occurs when shell is stopping. - /// - public event EventHandler Stopping; - - /// - /// Occurs when shell is stopped. - /// - public event EventHandler Stopped; - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccurred; - - /// - /// Initializes a new instance of the class. - /// - /// The session. - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode. - /// Size of the buffer for output stream. - internal Shell(Session session, Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) - { - this._session = session; - this._input = input; - this._outputStream = output; - this._extendedOutputStream = extendedOutput; - this._terminalName = terminalName; - this._columns = columns; - this._rows = rows; - this._width = width; - this._height = height; - this._terminalModes = terminalModes; - this._bufferSize = bufferSize; - } - - /// - /// Starts this shell. - /// - /// Shell is started. - public void Start() - { - if (this.IsStarted) - { - throw new SshException("Shell is started."); - } - - if (this.Starting != null) - { - this.Starting(this, new EventArgs()); - } - - this._channel = this._session.CreateChannel(); - this._channel.DataReceived += Channel_DataReceived; - this._channel.ExtendedDataReceived += Channel_ExtendedDataReceived; - this._channel.Closed += Channel_Closed; - this._session.Disconnected += Session_Disconnected; - this._session.ErrorOccured += Session_ErrorOccured; - - this._channel.Open(); - this._channel.SendPseudoTerminalRequest(this._terminalName, this._columns, this._rows, this._width, this._height, this._terminalModes); - this._channel.SendShellRequest(); - - this._channelClosedWaitHandle = new AutoResetEvent(false); - - // Start input stream listener - this._dataReaderTaskCompleted = new ManualResetEvent(false); - this.ExecuteThread(() => - { - try - { - var buffer = new byte[this._bufferSize]; - - while (this._channel.IsOpen) - { - var asyncResult = this._input.BeginRead(buffer, 0, buffer.Length, delegate(IAsyncResult result) - { - // If input stream is closed and disposed already dont finish reading the stream - if (this._input == null) - return; - - var read = this._input.EndRead(result); - if (read > 0) - { - this._channel.SendData(buffer.Take(read).ToArray()); - } - - }, null); - - EventWaitHandle.WaitAny(new WaitHandle[] { asyncResult.AsyncWaitHandle, this._channelClosedWaitHandle }); - - if (asyncResult.IsCompleted) - continue; - else - break; - } - } - catch (Exception exp) - { - this.RaiseError(new ExceptionEventArgs(exp)); - } - finally - { - this._dataReaderTaskCompleted.Set(); - } - }); - - this.IsStarted = true; - - if (this.Started != null) - { - this.Started(this, new EventArgs()); - } - } - - /// - /// Stops this shell. - /// - /// Shell is not started. - public void Stop() - { - if (!this.IsStarted) - { - throw new SshException("Shell is not started."); - } - - // If channel is open then close it to cause Channel_Closed method to be called - if (this._channel != null && this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - } - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - this.RaiseError(e); - } - - private void RaiseError(ExceptionEventArgs e) - { - var handler = this.ErrorOccurred; - if (handler != null) - { - handler(this, e); - } - } - - private void Session_Disconnected(object sender, System.EventArgs e) - { - this.Stop(); - } - - private void Channel_ExtendedDataReceived(object sender, Common.ChannelDataEventArgs e) - { - if (this._extendedOutputStream != null) - { - this._extendedOutputStream.Write(e.Data, 0, e.Data.Length); - } - } - - private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e) - { - if (this._outputStream != null) - { - this._outputStream.Write(e.Data, 0, e.Data.Length); - } - } - - private void Channel_Closed(object sender, Common.ChannelEventArgs e) - { - if (this.Stopping != null) - { - // Handle event on different thread - this.ExecuteThread(() => { this.Stopping(this, new EventArgs()); }); - } - - if (this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - - this._channelClosedWaitHandle.Set(); - - this._input.Dispose(); - this._input = null; - - this._dataReaderTaskCompleted.WaitOne(this._session.ConnectionInfo.Timeout); - this._dataReaderTaskCompleted.Dispose(); - this._dataReaderTaskCompleted = null; - - this._channel.DataReceived -= Channel_DataReceived; - this._channel.ExtendedDataReceived -= Channel_ExtendedDataReceived; - this._channel.Closed -= Channel_Closed; - this._session.Disconnected -= Session_Disconnected; - this._session.ErrorOccured -= Session_ErrorOccured; - - if (this.Stopped != null) - { - // Handle event on different thread - this.ExecuteThread(() => { this.Stopped(this, new EventArgs()); }); - } - - this._channel = null; - } - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _disposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._disposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - if (this._channelClosedWaitHandle != null) - { - this._channelClosedWaitHandle.Dispose(); - this._channelClosedWaitHandle = null; - } - - if (this._channel != null) - { - this._channel.Dispose(); - this._channel = null; - } - - if (this._dataReaderTaskCompleted != null) - { - this._dataReaderTaskCompleted.Dispose(); - this._dataReaderTaskCompleted = null; - } - } - - // Note disposing has been done. - this._disposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~Shell() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - } -} +using System; +using System.Linq; +using System.IO; +using System.Threading; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using System.Collections.Generic; + +namespace Renci.SshNet +{ + /// + /// Represents instance of the SSH shell object + /// + public partial class Shell : IDisposable + { + private readonly Session _session; + + private ChannelSession _channel; + + private EventWaitHandle _channelClosedWaitHandle; + + private Stream _input; + + private readonly string _terminalName; + + private readonly uint _columns; + + private readonly uint _rows; + + private readonly uint _width; + + private readonly uint _height; + + private readonly IDictionary _terminalModes; + + private EventWaitHandle _dataReaderTaskCompleted; + + private readonly Stream _outputStream; + + private readonly Stream _extendedOutputStream; + + private readonly int _bufferSize; + + /// + /// Gets a value indicating whether this shell is started. + /// + /// + /// true if started is started; otherwise, false. + /// + public bool IsStarted { get; private set; } + + /// + /// Occurs when shell is starting. + /// + public event EventHandler Starting; + + /// + /// Occurs when shell is started. + /// + public event EventHandler Started; + + /// + /// Occurs when shell is stopping. + /// + public event EventHandler Stopping; + + /// + /// Occurs when shell is stopped. + /// + public event EventHandler Stopped; + + /// + /// Occurs when an error occurred. + /// + public event EventHandler ErrorOccurred; + + /// + /// Initializes a new instance of the class. + /// + /// The session. + /// The input. + /// The output. + /// The extended output. + /// Name of the terminal. + /// The columns. + /// The rows. + /// The width. + /// The height. + /// The terminal modes. + /// Size of the buffer for output stream. + internal Shell(Session session, Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) + { + this._session = session; + this._input = input; + this._outputStream = output; + this._extendedOutputStream = extendedOutput; + this._terminalName = terminalName; + this._columns = columns; + this._rows = rows; + this._width = width; + this._height = height; + this._terminalModes = terminalModes; + this._bufferSize = bufferSize; + } + + /// + /// Starts this shell. + /// + /// Shell is started. + public void Start() + { + if (this.IsStarted) + { + throw new SshException("Shell is started."); + } + + if (this.Starting != null) + { + this.Starting(this, new EventArgs()); + } + + this._channel = this._session.CreateClientChannel(); + this._channel.DataReceived += Channel_DataReceived; + this._channel.ExtendedDataReceived += Channel_ExtendedDataReceived; + this._channel.Closed += Channel_Closed; + this._session.Disconnected += Session_Disconnected; + this._session.ErrorOccured += Session_ErrorOccured; + + this._channel.Open(); + this._channel.SendPseudoTerminalRequest(this._terminalName, this._columns, this._rows, this._width, this._height, this._terminalModes); + this._channel.SendShellRequest(); + + this._channelClosedWaitHandle = new AutoResetEvent(false); + + // Start input stream listener + this._dataReaderTaskCompleted = new ManualResetEvent(false); + this.ExecuteThread(() => + { + try + { + var buffer = new byte[this._bufferSize]; + + while (this._channel.IsOpen) + { + var asyncResult = this._input.BeginRead(buffer, 0, buffer.Length, delegate(IAsyncResult result) + { + // If input stream is closed and disposed already dont finish reading the stream + if (this._input == null) + return; + + var read = this._input.EndRead(result); + if (read > 0) + { + this._channel.SendData(buffer.Take(read).ToArray()); + } + + }, null); + + EventWaitHandle.WaitAny(new WaitHandle[] { asyncResult.AsyncWaitHandle, this._channelClosedWaitHandle }); + + if (asyncResult.IsCompleted) + continue; + break; + } + } + catch (Exception exp) + { + this.RaiseError(new ExceptionEventArgs(exp)); + } + finally + { + this._dataReaderTaskCompleted.Set(); + } + }); + + this.IsStarted = true; + + if (this.Started != null) + { + this.Started(this, new EventArgs()); + } + } + + /// + /// Stops this shell. + /// + /// Shell is not started. + public void Stop() + { + if (!this.IsStarted) + { + throw new SshException("Shell is not started."); + } + + // If channel is open then close it to cause Channel_Closed method to be called + if (this._channel != null && this._channel.IsOpen) + { + this._channel.SendEof(); + + this._channel.Close(); + } + } + + private void Session_ErrorOccured(object sender, ExceptionEventArgs e) + { + this.RaiseError(e); + } + + private void RaiseError(ExceptionEventArgs e) + { + var handler = this.ErrorOccurred; + if (handler != null) + { + handler(this, e); + } + } + + private void Session_Disconnected(object sender, EventArgs e) + { + this.Stop(); + } + + private void Channel_ExtendedDataReceived(object sender, ChannelDataEventArgs e) + { + if (this._extendedOutputStream != null) + { + this._extendedOutputStream.Write(e.Data, 0, e.Data.Length); + } + } + + private void Channel_DataReceived(object sender, ChannelDataEventArgs e) + { + if (this._outputStream != null) + { + this._outputStream.Write(e.Data, 0, e.Data.Length); + } + } + + private void Channel_Closed(object sender, ChannelEventArgs e) + { + if (this.Stopping != null) + { + // Handle event on different thread + this.ExecuteThread(() => this.Stopping(this, new EventArgs())); + } + + if (this._channel.IsOpen) + { + this._channel.SendEof(); + + this._channel.Close(); + } + + this._channelClosedWaitHandle.Set(); + + this._input.Dispose(); + this._input = null; + + this._dataReaderTaskCompleted.WaitOne(this._session.ConnectionInfo.Timeout); + this._dataReaderTaskCompleted.Dispose(); + this._dataReaderTaskCompleted = null; + + this._channel.DataReceived -= Channel_DataReceived; + this._channel.ExtendedDataReceived -= Channel_ExtendedDataReceived; + this._channel.Closed -= Channel_Closed; + this._session.Disconnected -= Session_Disconnected; + this._session.ErrorOccured -= Session_ErrorOccured; + + if (this.Stopped != null) + { + // Handle event on different thread + this.ExecuteThread(() => this.Stopped(this, new EventArgs())); + } + + this._channel = null; + } + + partial void ExecuteThread(Action action); + + #region IDisposable Members + + private bool _disposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._disposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + if (this._channelClosedWaitHandle != null) + { + this._channelClosedWaitHandle.Dispose(); + this._channelClosedWaitHandle = null; + } + + if (this._channel != null) + { + this._channel.Dispose(); + this._channel = null; + } + + if (this._dataReaderTaskCompleted != null) + { + this._dataReaderTaskCompleted.Dispose(); + this._dataReaderTaskCompleted = null; + } + } + + // Note disposing has been done. + this._disposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~Shell() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + } +} diff --git a/Renci.SshNet/ShellStream.NET40.cs b/Renci.SshNet/ShellStream.NET40.cs index 6512f92..9ade4f0 100644 --- a/Renci.SshNet/ShellStream.NET40.cs +++ b/Renci.SshNet/ShellStream.NET40.cs @@ -1,18 +1,17 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Renci.SshNet -{ - /// - /// Represents instance of the SSH shell object - /// - public partial class ShellStream - { - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem((o) => { action(); }); - } - } -} +using System; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Represents instance of the SSH shell object + /// + public partial class ShellStream + { + /// is null. + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem(o => action()); + } + } +} diff --git a/Renci.SshNet/ShellStream.cs b/Renci.SshNet/ShellStream.cs index 77b7bb9..0b723df 100644 --- a/Renci.SshNet/ShellStream.cs +++ b/Renci.SshNet/ShellStream.cs @@ -1,759 +1,758 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using System.Threading; -using System.Text.RegularExpressions; - -namespace Renci.SshNet -{ - /// - /// Contains operation for working with SSH Shell. - /// - public partial class ShellStream : Stream - { - private readonly Session _session; - - private readonly int _bufferSize = 1024; - - private ChannelSession _channel; - - private Encoding _encoding; - - private Queue _incoming; - - private Queue _outgoing; - - private AutoResetEvent _dataReceived = new AutoResetEvent(false); - - /// - /// Occurs when data was received. - /// - public event EventHandler DataReceived; - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccurred; - - /// - /// Gets a value that indicates whether data is available on the to be read. - /// - /// - /// true if data is available to be read; otherwise, false. - /// - public bool DataAvailable - { - get - { - lock (this._incoming) - { - return this._incoming.Count > 0; - } - } - } - - internal ShellStream(Session session, string terminalName, uint columns, uint rows, uint width, uint height, int maxLines, IDictionary terminalModeValues) - { - this._encoding = session.ConnectionInfo.Encoding; - this._session = session; - this._incoming = new Queue(); - this._outgoing = new Queue(); - - this._channel = this._session.CreateChannel(); - this._channel.DataReceived += new EventHandler(Channel_DataReceived); - this._channel.Closed += new EventHandler(Channel_Closed); - this._session.Disconnected += new EventHandler(Session_Disconnected); - this._session.ErrorOccured += new EventHandler(Session_ErrorOccured); - - this._channel.Open(); - this._channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues); - this._channel.SendShellRequest(); - } - - #region Stream overide methods - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get { return true; } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get { return false; } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get { return true; } - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// An I/O error occurs. - public override void Flush() - { - if (this._channel == null) - { - throw new ObjectDisposedException("ShellStream"); - } - this._channel.SendData(this._outgoing.ToArray()); - this._outgoing.Clear(); - } - - /// - /// Gets the length in bytes of the stream. - /// - /// A long value representing the length of the stream in bytes. - /// - /// A class derived from Stream does not support seeking. - /// - /// Methods were called after the stream was closed. - public override long Length - { - get - { - lock (this._incoming) - { - return this._incoming.Count; - } - } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// - /// An I/O error occurs. - /// - /// The stream does not support seeking. - /// - /// Methods were called after the stream was closed. - public override long Position - { - get { return 0; } - set { throw new NotSupportedException(); } - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// - /// The sum of and is larger than the buffer length. - /// - /// - /// is null. - /// - /// - /// or is negative. - /// - /// An I/O error occurs. - /// - /// The stream does not support reading. - /// - /// Methods were called after the stream was closed. - public override int Read(byte[] buffer, int offset, int count) - { - var i = 0; - - lock (this._incoming) - { - for (; i < count && this._incoming.Count > 0; i++) - { - buffer[offset + i] = this._incoming.Dequeue(); - } - } - - return i; - } - - /// - /// This method is not supported. - /// - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// - /// The new position within the current stream. - /// - /// An I/O error occurs. - /// - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// - /// Methods were called after the stream was closed. - public override long Seek(long offset, System.IO.SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// This method is not supported. - /// - /// The desired length of the current stream in bytes. - /// An I/O error occurs. - /// - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// - /// Methods were called after the stream was closed. - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies bytes from to the current stream. - /// The zero-based byte offset in at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// The sum of and is greater than the buffer length. - /// - /// - /// is null. - /// - /// - /// or is negative. - /// - /// An I/O error occurs. - /// - /// The stream does not support writing. - /// - /// Methods were called after the stream was closed. - public override void Write(byte[] buffer, int offset, int count) - { - foreach (var b in buffer.Skip(offset).Take(count).ToArray()) - { - if (this._outgoing.Count < this._bufferSize) - { - this._outgoing.Enqueue(b); - continue; - } - - this.Flush(); - } - } - - #endregion - - /// - /// Expects the specified expression and performs action when one is found. - /// - /// The expected expressions and actions to perform. - public void Expect(params ExpectAction[] expectActions) - { - this.Expect(TimeSpan.Zero, expectActions); - } - - /// - /// Expects the specified expression and performs action when one is found. - /// - /// Time to wait for input. - /// The expected expressions and actions to perform, if the specified time elapsed and expected condition have not met, that method will exit without executing any action. - public void Expect(TimeSpan timeout, params ExpectAction[] expectActions) - { - var expectedFound = false; - var text = string.Empty; - - do - { - lock (this._incoming) - { - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - if (text.Length > 0) - { - foreach (var expectAction in expectActions) - { - var match = expectAction.Expect.Match(text); - - if (match.Success) - { - var result = text.Substring(0, match.Index + match.Length); - - for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) - { - // Remove processed items from the queue - this._incoming.Dequeue(); - } - - expectAction.Action(result); - expectedFound = true; - } - } - } - } - - if (!expectedFound) - { - if (timeout != null) - { - if (!this._dataReceived.WaitOne(timeout)) - { - return; - } - } - else - { - this._dataReceived.WaitOne(); - } - } - } - while (!expectedFound); - } - - /// - /// Begins the expect. - /// - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(params ExpectAction[] expectActions) - { - return this.BeginExpect(TimeSpan.Zero, null, null, expectActions); - } - - /// - /// Begins the expect. - /// - /// The callback. - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(AsyncCallback callback, params ExpectAction[] expectActions) - { - return this.BeginExpect(TimeSpan.Zero, callback, null, expectActions); - } - - /// - /// Begins the expect. - /// - /// The callback. - /// The state. - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(AsyncCallback callback, object state, params ExpectAction[] expectActions) - { - return this.BeginExpect(TimeSpan.Zero, callback, state, expectActions); - } - - /// - /// Begins the expect. - /// - /// The timeout. - /// The callback. - /// The state. - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(TimeSpan timeout, AsyncCallback callback, object state, params ExpectAction[] expectActions) - { - var text = string.Empty; - - // Create new AsyncResult object - var asyncResult = new ExpectAsyncResult(callback, state); - - // Execute callback on different thread - this.ExecuteThread(() => - { - string expectActionResult = null; - try - { - - do - { - lock (this._incoming) - { - - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - if (text.Length > 0) - { - foreach (var expectAction in expectActions) - { - var match = expectAction.Expect.Match(text); - - if (match.Success) - { - var result = text.Substring(0, match.Index + match.Length); - - for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) - { - // Remove processed items from the queue - this._incoming.Dequeue(); - } - - expectAction.Action(result); - - if (callback != null) - { - callback(asyncResult); - } - expectActionResult = result; - } - } - } - } - - if (expectActionResult != null) - break; - - if (timeout != null) - { - if (!this._dataReceived.WaitOne(timeout)) - { - if (callback != null) - { - callback(asyncResult); - } - break; - } - } - else - { - this._dataReceived.WaitOne(); - } - } while (true); - - asyncResult.SetAsCompleted(expectActionResult, true); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, true); - } - }); - - return asyncResult; - } - - /// - /// Ends the execute. - /// - /// The async result. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - public string EndExpect(IAsyncResult asyncResult) - { - var ar = asyncResult as ExpectAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - return ar.EndInvoke(); - } - - /// - /// Expects the expression specified by text. - /// - /// The text to expect. - /// - /// Text available in the shell that ends with expected text. - /// - public string Expect(string text) - { - return this.Expect(new Regex(Regex.Escape(text)), TimeSpan.FromMilliseconds(-1)); - } - - /// - /// Expects the expression specified by text. - /// - /// The text to expect. - /// Time to wait for input. - /// - /// Text available in the shell that ends with expected text, if the specified time elapsed returns null. - /// - public string Expect(string text, TimeSpan timeout) - { - return this.Expect(new Regex(Regex.Escape(text)), timeout); - } - - /// - /// Expects the expression specified by regular expression. - /// - /// The regular expression to expect. - /// Text available in the shell that contains all the text that ends with expected expression. - public string Expect(Regex regex) - { - return this.Expect(regex, TimeSpan.Zero); - } - - /// - /// Expects the expression specified by regular expression. - /// - /// The regular expression to expect. - /// Time to wait for input. - /// - /// Text available in the shell that contains all the text that ends with expected expression, if the specified time elapsed returns null. - /// - public string Expect(Regex regex, TimeSpan timeout) - { - // TODO: Refactor this method, will deda lock - var text = string.Empty; - - while (true) - { - lock (this._incoming) - { - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - var match = regex.Match(text); - - if (match.Success) - { - // Remove processed items from the queue - for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) - { - this._incoming.Dequeue(); - } - break; - } - } - - if (timeout != null) - { - if (!this._dataReceived.WaitOne(timeout)) - { - return null; - } - } - else - { - this._dataReceived.WaitOne(); - } - - } - - return text; - } - - /// - /// Reads the line from the shell. If line is not available it will block the execution and will wait for new line. - /// - /// The line read from the shell. - public string ReadLine() - { - return this.ReadLine(TimeSpan.Zero); - } - - /// - /// Reads the line from the shell. If line is not available it will block the execution and will wait for new line. - /// - /// Time to wait for input. - /// - /// The line read from the shell, if the specified time elapsed returns null. - /// - public string ReadLine(TimeSpan timeout) - { - var text = string.Empty; - - while (true) - { - lock (this._incoming) - { - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - var index = text.IndexOf("\r\n"); - - if (index >= 0) - { - text = text.Substring(0, index); - - // Remove processed items from the queue - for (int i = 0; i < index + 2 && this._incoming.Count > 0; i++) - { - this._incoming.Dequeue(); - } - break; - } - } - - if (timeout != null) - { - if (!this._dataReceived.WaitOne(timeout)) - { - return null; - } - } - else - { - this._dataReceived.WaitOne(); - } - - } - - return text; - } - - /// - /// Reads text available in the shell. - /// - /// The text available in the shell. - public string Read() - { - var text = string.Empty; - - lock (this._incoming) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - this._incoming.Clear(); - } - - return text.ToString(); - } - - /// - /// Writes the specified text to the shell. - /// - /// The text to be written to the shell. - public void Write(string text) - { - if (this._channel == null) - { - throw new ObjectDisposedException("ShellStream"); - } - - var data = this._encoding.GetBytes(text); - this._channel.SendData(data); - } - - /// - /// Writes the line to the shell. - /// - /// The line to be written to the shell. - public void WriteLine(string line) - { - var commandText = string.Format("{0}{1}", line, "\r\n"); - this.Write(commandText); - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._channel != null) - { - this._channel.Dispose(); - this._channel = null; - } - - if (this._dataReceived != null) - { - this._dataReceived.Dispose(); - this._dataReceived = null; - } - - if (this._session != null) - { - this._session.Disconnected -= new EventHandler(Session_Disconnected); - this._session.ErrorOccured -= new EventHandler(Session_ErrorOccured); - } - } - - /// - /// Waits for the handle to be signaled or for an error to occurs. - /// - /// The wait handle. - protected void WaitHandle(WaitHandle waitHandle) - { - this._session.WaitHandle(waitHandle); - } - - partial void ExecuteThread(Action action); - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - this.OnRaiseError(e); - } - - private void Session_Disconnected(object sender, EventArgs e) - { - // If channel is open then close it to cause Channel_Closed method to be called - if (this._channel != null && this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - } - - private void Channel_Closed(object sender, ChannelEventArgs e) - { - // TODO: Do we need to call dispose here ?? - this.Dispose(); - } - - private void Channel_DataReceived(object sender, ChannelDataEventArgs e) - { - lock (this._incoming) - { - foreach (var b in e.Data) - { - this._incoming.Enqueue(b); - } - } - - this._dataReceived.Set(); - - this.OnDataReceived(e.Data); - } - - private void OnRaiseError(ExceptionEventArgs e) - { - var handler = this.ErrorOccurred; - if (handler != null) - { - handler(this, e); - } - } - - private void OnDataReceived(byte[] data) - { - var handler = this.DataReceived; - if (handler != null) - { - handler(this, new ShellDataEventArgs(data)); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using System.Threading; +using System.Text.RegularExpressions; + +namespace Renci.SshNet +{ + /// + /// Contains operation for working with SSH Shell. + /// + public partial class ShellStream : Stream + { + private readonly Session _session; + + private const int _bufferSize = 1024; + + private ChannelSession _channel; + + private readonly Encoding _encoding; + + private readonly Queue _incoming; + + private readonly Queue _outgoing; + + private AutoResetEvent _dataReceived = new AutoResetEvent(false); + + /// + /// Occurs when data was received. + /// + public event EventHandler DataReceived; + + /// + /// Occurs when an error occurred. + /// + public event EventHandler ErrorOccurred; + + /// + /// Gets a value that indicates whether data is available on the to be read. + /// + /// + /// true if data is available to be read; otherwise, false. + /// + public bool DataAvailable + { + get + { + lock (this._incoming) + { + return this._incoming.Count > 0; + } + } + } + + internal ShellStream(Session session, string terminalName, uint columns, uint rows, uint width, uint height, int maxLines, IDictionary terminalModeValues) + { + this._encoding = session.ConnectionInfo.Encoding; + this._session = session; + this._incoming = new Queue(); + this._outgoing = new Queue(); + + this._channel = this._session.CreateClientChannel(); + this._channel.DataReceived += Channel_DataReceived; + this._channel.Closed += Channel_Closed; + this._session.Disconnected += Session_Disconnected; + this._session.ErrorOccured += Session_ErrorOccured; + + this._channel.Open(); + this._channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues); + this._channel.SendShellRequest(); + } + + #region Stream overide methods + + /// + /// Gets a value indicating whether the current stream supports reading. + /// + /// true if the stream supports reading; otherwise, false. + public override bool CanRead + { + get { return true; } + } + + /// + /// Gets a value indicating whether the current stream supports seeking. + /// + /// true if the stream supports seeking; otherwise, false. + public override bool CanSeek + { + get { return false; } + } + + /// + /// Gets a value indicating whether the current stream supports writing. + /// + /// true if the stream supports writing; otherwise, false. + public override bool CanWrite + { + get { return true; } + } + + /// + /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. + /// + /// An I/O error occurs. + public override void Flush() + { + if (this._channel == null) + { + throw new ObjectDisposedException("ShellStream"); + } + this._channel.SendData(this._outgoing.ToArray()); + this._outgoing.Clear(); + } + + /// + /// Gets the length in bytes of the stream. + /// + /// A long value representing the length of the stream in bytes. + /// + /// A class derived from Stream does not support seeking. + /// + /// Methods were called after the stream was closed. + public override long Length + { + get + { + lock (this._incoming) + { + return this._incoming.Count; + } + } + } + + /// + /// Gets or sets the position within the current stream. + /// + /// The current position within the stream. + /// + /// An I/O error occurs. + /// + /// The stream does not support seeking. + /// + /// Methods were called after the stream was closed. + public override long Position + { + get { return 0; } + set { throw new NotSupportedException(); } + } + + /// + /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. + /// + /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. + /// The zero-based byte offset in at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// + /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. + /// + /// The sum of and is larger than the buffer length. + /// + /// + /// is null. + /// + /// + /// or is negative. + /// + /// An I/O error occurs. + /// + /// The stream does not support reading. + /// + /// Methods were called after the stream was closed. + public override int Read(byte[] buffer, int offset, int count) + { + var i = 0; + + lock (this._incoming) + { + for (; i < count && this._incoming.Count > 0; i++) + { + buffer[offset + i] = this._incoming.Dequeue(); + } + } + + return i; + } + + /// + /// This method is not supported. + /// + /// A byte offset relative to the parameter. + /// A value of type indicating the reference point used to obtain the new position. + /// + /// The new position within the current stream. + /// + /// An I/O error occurs. + /// + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// + /// Methods were called after the stream was closed. + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// This method is not supported. + /// + /// The desired length of the current stream in bytes. + /// An I/O error occurs. + /// + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// + /// Methods were called after the stream was closed. + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. + /// + /// An array of bytes. This method copies bytes from to the current stream. + /// The zero-based byte offset in at which to begin copying bytes to the current stream. + /// The number of bytes to be written to the current stream. + /// The sum of and is greater than the buffer length. + /// + /// + /// is null. + /// + /// + /// or is negative. + /// + /// An I/O error occurs. + /// + /// The stream does not support writing. + /// + /// Methods were called after the stream was closed. + public override void Write(byte[] buffer, int offset, int count) + { + foreach (var b in buffer.Skip(offset).Take(count).ToArray()) + { + if (this._outgoing.Count < _bufferSize) + { + this._outgoing.Enqueue(b); + continue; + } + + this.Flush(); + } + } + + #endregion + + /// + /// Expects the specified expression and performs action when one is found. + /// + /// The expected expressions and actions to perform. + public void Expect(params ExpectAction[] expectActions) + { + this.Expect(TimeSpan.Zero, expectActions); + } + + /// + /// Expects the specified expression and performs action when one is found. + /// + /// Time to wait for input. + /// The expected expressions and actions to perform, if the specified time elapsed and expected condition have not met, that method will exit without executing any action. + public void Expect(TimeSpan timeout, params ExpectAction[] expectActions) + { + var expectedFound = false; + var text = string.Empty; + + do + { + lock (this._incoming) + { + if (this._incoming.Count > 0) + { + text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); + } + + if (text.Length > 0) + { + foreach (var expectAction in expectActions) + { + var match = expectAction.Expect.Match(text); + + if (match.Success) + { + var result = text.Substring(0, match.Index + match.Length); + + for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) + { + // Remove processed items from the queue + this._incoming.Dequeue(); + } + + expectAction.Action(result); + expectedFound = true; + } + } + } + } + + if (!expectedFound) + { + if (timeout.Ticks > 0) + { + if (!this._dataReceived.WaitOne(timeout)) + { + return; + } + } + else + { + this._dataReceived.WaitOne(); + } + } + } + while (!expectedFound); + } + + /// + /// Begins the expect. + /// + /// The expect actions. + /// + /// An that references the asynchronous operation. + /// + public IAsyncResult BeginExpect(params ExpectAction[] expectActions) + { + return this.BeginExpect(TimeSpan.Zero, null, null, expectActions); + } + + /// + /// Begins the expect. + /// + /// The callback. + /// The expect actions. + /// + /// An that references the asynchronous operation. + /// + public IAsyncResult BeginExpect(AsyncCallback callback, params ExpectAction[] expectActions) + { + return this.BeginExpect(TimeSpan.Zero, callback, null, expectActions); + } + + /// + /// Begins the expect. + /// + /// The callback. + /// The state. + /// The expect actions. + /// + /// An that references the asynchronous operation. + /// + public IAsyncResult BeginExpect(AsyncCallback callback, object state, params ExpectAction[] expectActions) + { + return this.BeginExpect(TimeSpan.Zero, callback, state, expectActions); + } + + /// + /// Begins the expect. + /// + /// The timeout. + /// The callback. + /// The state. + /// The expect actions. + /// + /// An that references the asynchronous operation. + /// + public IAsyncResult BeginExpect(TimeSpan timeout, AsyncCallback callback, object state, params ExpectAction[] expectActions) + { + var text = string.Empty; + + // Create new AsyncResult object + var asyncResult = new ExpectAsyncResult(callback, state); + + // Execute callback on different thread + this.ExecuteThread(() => + { + string expectActionResult = null; + try + { + + do + { + lock (this._incoming) + { + + if (this._incoming.Count > 0) + { + text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); + } + + if (text.Length > 0) + { + foreach (var expectAction in expectActions) + { + var match = expectAction.Expect.Match(text); + + if (match.Success) + { + var result = text.Substring(0, match.Index + match.Length); + + for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) + { + // Remove processed items from the queue + this._incoming.Dequeue(); + } + + expectAction.Action(result); + + if (callback != null) + { + callback(asyncResult); + } + expectActionResult = result; + } + } + } + } + + if (expectActionResult != null) + break; + + if (timeout.Ticks > 0) + { + if (!this._dataReceived.WaitOne(timeout)) + { + if (callback != null) + { + callback(asyncResult); + } + break; + } + } + else + { + this._dataReceived.WaitOne(); + } + } while (true); + + asyncResult.SetAsCompleted(expectActionResult, true); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, true); + } + }); + + return asyncResult; + } + + /// + /// Ends the execute. + /// + /// The async result. + /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. + public string EndExpect(IAsyncResult asyncResult) + { + var ar = asyncResult as ExpectAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + return ar.EndInvoke(); + } + + /// + /// Expects the expression specified by text. + /// + /// The text to expect. + /// + /// Text available in the shell that ends with expected text. + /// + public string Expect(string text) + { + return this.Expect(new Regex(Regex.Escape(text)), TimeSpan.FromMilliseconds(-1)); + } + + /// + /// Expects the expression specified by text. + /// + /// The text to expect. + /// Time to wait for input. + /// + /// Text available in the shell that ends with expected text, if the specified time elapsed returns null. + /// + public string Expect(string text, TimeSpan timeout) + { + return this.Expect(new Regex(Regex.Escape(text)), timeout); + } + + /// + /// Expects the expression specified by regular expression. + /// + /// The regular expression to expect. + /// Text available in the shell that contains all the text that ends with expected expression. + public string Expect(Regex regex) + { + return this.Expect(regex, TimeSpan.Zero); + } + + /// + /// Expects the expression specified by regular expression. + /// + /// The regular expression to expect. + /// Time to wait for input. + /// + /// Text available in the shell that contains all the text that ends with expected expression, if the specified time elapsed returns null. + /// + public string Expect(Regex regex, TimeSpan timeout) + { + // TODO: Refactor this method, will deda lock + var text = string.Empty; + + while (true) + { + lock (this._incoming) + { + if (this._incoming.Count > 0) + { + text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); + } + + var match = regex.Match(text); + + if (match.Success) + { + // Remove processed items from the queue + for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) + { + this._incoming.Dequeue(); + } + break; + } + } + + if (timeout.Ticks > 0) + { + if (!this._dataReceived.WaitOne(timeout)) + { + return null; + } + } + else + { + this._dataReceived.WaitOne(); + } + + } + + return text; + } + + /// + /// Reads the line from the shell. If line is not available it will block the execution and will wait for new line. + /// + /// The line read from the shell. + public string ReadLine() + { + return this.ReadLine(TimeSpan.Zero); + } + + /// + /// Reads the line from the shell. If line is not available it will block the execution and will wait for new line. + /// + /// Time to wait for input. + /// + /// The line read from the shell, if the specified time elapsed returns null. + /// + public string ReadLine(TimeSpan timeout) + { + var text = string.Empty; + + while (true) + { + lock (this._incoming) + { + if (this._incoming.Count > 0) + { + text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); + } + + var index = text.IndexOf("\r\n"); + + if (index >= 0) + { + text = text.Substring(0, index); + + // Remove processed items from the queue + for (int i = 0; i < index + 2 && this._incoming.Count > 0; i++) + { + this._incoming.Dequeue(); + } + break; + } + } + + if (timeout.Ticks > 0) + { + if (!this._dataReceived.WaitOne(timeout)) + { + return null; + } + } + else + { + this._dataReceived.WaitOne(); + } + + } + + return text; + } + + /// + /// Reads text available in the shell. + /// + /// The text available in the shell. + public string Read() + { + string text; + + lock (this._incoming) + { + text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); + this._incoming.Clear(); + } + + return text; + } + + /// + /// Writes the specified text to the shell. + /// + /// The text to be written to the shell. + public void Write(string text) + { + if (this._channel == null) + { + throw new ObjectDisposedException("ShellStream"); + } + + var data = this._encoding.GetBytes(text); + this._channel.SendData(data); + } + + /// + /// Writes the line to the shell. + /// + /// The line to be written to the shell. + public void WriteLine(string line) + { + var commandText = string.Format("{0}{1}", line, "\r"); + this.Write(commandText); + } + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (this._channel != null) + { + this._channel.Dispose(); + this._channel = null; + } + + if (this._dataReceived != null) + { + this._dataReceived.Dispose(); + this._dataReceived = null; + } + + if (this._session != null) + { + this._session.Disconnected -= Session_Disconnected; + this._session.ErrorOccured -= Session_ErrorOccured; + } + } + + /// + /// Waits for the handle to be signaled or for an error to occurs. + /// + /// The wait handle. + protected void WaitOnHandle(WaitHandle waitHandle) + { + this._session.WaitOnHandle(waitHandle); + } + + partial void ExecuteThread(Action action); + + private void Session_ErrorOccured(object sender, ExceptionEventArgs e) + { + this.OnRaiseError(e); + } + + private void Session_Disconnected(object sender, EventArgs e) + { + // If channel is open then close it to cause Channel_Closed method to be called + if (this._channel != null && this._channel.IsOpen) + { + this._channel.SendEof(); + + this._channel.Close(); + } + } + + private void Channel_Closed(object sender, ChannelEventArgs e) + { + // TODO: Do we need to call dispose here ?? + this.Dispose(); + } + + private void Channel_DataReceived(object sender, ChannelDataEventArgs e) + { + lock (this._incoming) + { + foreach (var b in e.Data) + this._incoming.Enqueue(b); + } + + if (_dataReceived != null) + _dataReceived.Set(); + + this.OnDataReceived(e.Data); + } + + private void OnRaiseError(ExceptionEventArgs e) + { + var handler = this.ErrorOccurred; + if (handler != null) + { + handler(this, e); + } + } + + private void OnDataReceived(byte[] data) + { + var handler = this.DataReceived; + if (handler != null) + { + handler(this, new ShellDataEventArgs(data)); + } + } + } +} diff --git a/Renci.SshNet/SshClient.cs b/Renci.SshNet/SshClient.cs index e30b5aa..23c8039 100644 --- a/Renci.SshNet/SshClient.cs +++ b/Renci.SshNet/SshClient.cs @@ -1,416 +1,424 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Collections.ObjectModel; -using System.Text; -using System.Diagnostics.CodeAnalysis; -using Renci.SshNet.Common; - -namespace Renci.SshNet -{ - /// - /// Provides client connection to SSH server. - /// - public class SshClient : BaseClient - { - /// - /// Holds the list of forwarded ports - /// - private List _forwardedPorts = new List(); - - /// - /// If true, causes the connectionInfo object to be disposed. - /// - private bool _disposeConnectionInfo; - - private Stream _inputStream; - - /// - /// Gets the list of forwarded ports. - /// - public IEnumerable ForwardedPorts - { - get - { - return this._forwardedPorts.AsReadOnly(); - } - } - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// - /// - /// - /// - /// - /// - /// is null. - public SshClient(ConnectionInfo connectionInfo) - : base(connectionInfo) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "C2A000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SshClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password)) - { - this._disposeConnectionInfo = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication password. - /// - /// - /// - /// is null. - /// is invalid, or is null or contains whitespace characters. - public SshClient(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication private key file(s) . - /// - /// - /// - /// - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles)) - { - this._disposeConnectionInfo = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication private key file(s) . - /// - /// - /// - /// - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - public SshClient(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) - { - } - - #endregion - - /// - /// Called when client is disconnecting from the server. - /// - protected override void OnDisconnecting() - { - base.OnDisconnecting(); - - foreach (var port in this._forwardedPorts) - { - port.Stop(); - } - } - - /// - /// Adds the forwarded port. - /// - /// The port. - /// - /// - /// - /// - /// Forwarded port is already added to a different client. - /// is null. - /// Client is not connected. - public void AddForwardedPort(ForwardedPort port) - { - if (port == null) - throw new ArgumentNullException("port"); - - if (port.Session != null && port.Session != this.Session) - throw new InvalidOperationException("Forwarded port is already added to a different client."); - - port.Session = this.Session; - - this._forwardedPorts.Add(port); - } - - /// - /// Stops and removes the forwarded port from the list. - /// - /// Forwarded port. - /// is null. - public void RemoveForwardedPort(ForwardedPort port) - { - if (port == null) - throw new ArgumentNullException("port"); - - // Stop port forwarding before removing it - port.Stop(); - - port.Session = null; - - this._forwardedPorts.Remove(port); - } - - /// - /// Creates the command to be executed. - /// - /// The command text. - /// object. - /// Client is not connected. - public SshCommand CreateCommand(string commandText) - { - return this.CreateCommand(commandText, this.ConnectionInfo.Encoding); - } - - /// - /// Creates the command to be executed with specified encoding. - /// - /// The command text. - /// The encoding to use for results. - /// object which uses specified encoding. - /// TThis method will change current default encoding. - /// Client is not connected. - /// or is null. - public SshCommand CreateCommand(string commandText, Encoding encoding) - { - this.ConnectionInfo.Encoding = encoding; - return new SshCommand(this.Session, commandText); - } - - /// - /// Creates and executes the command. - /// - /// The command text. - /// Returns an instance of with execution results. - /// This method internally uses asynchronous calls. - /// - /// - /// - /// - /// CommandText property is empty. - /// Invalid Operation - An existing channel was used to execute this command. - /// Asynchronous operation is already in progress. - /// Client is not connected. - /// is null. - public SshCommand RunCommand(string commandText) - { - var cmd = this.CreateCommand(commandText); - cmd.Execute(); - return cmd; - } - - /// - /// Creates the shell. - /// - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode. - /// Size of the internal read buffer. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) - { - return new Shell(this.Session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize); - } - - /// - /// Creates the shell. - /// - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes) - { - return this.CreateShell(input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024); - } - - /// - /// Creates the shell. - /// - /// The input. - /// The output. - /// The extended output. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Stream input, Stream output, Stream extendedOutput) - { - return this.CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); - } - - /// - /// Creates the shell. - /// - /// The encoding to use to send the input. - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode. - /// Size of the internal read buffer. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) - { - this._inputStream = new MemoryStream(); - var writer = new StreamWriter(this._inputStream, encoding); - writer.Write(input); - writer.Flush(); - this._inputStream.Seek(0, SeekOrigin.Begin); - - return this.CreateShell(this._inputStream, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize); - } - - /// - /// Creates the shell. - /// - /// The encoding. - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal modes. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes) - { - return this.CreateShell(encoding, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024); - } - - /// - /// Creates the shell. - /// - /// The encoding. - /// The input. - /// The output. - /// The extended output. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput) - { - return this.CreateShell(encoding, input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); - } - - /// - /// Creates the shell stream. - /// - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// Size of the buffer. - /// - /// Reference to Created ShellStream object. - /// - public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize) - { - return this.CreateShellStream(terminalName, columns, rows, width, height, bufferSize, null); - } - - /// - /// Creates the shell stream. - /// - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// Size of the buffer. - /// The terminal mode values. - /// - /// Reference to Created ShellStream object. - /// - public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary terminalModeValues) - { - return new ShellStream(this.Session, terminalName, columns, rows, width, height, bufferSize, terminalModeValues); - } - - protected override void OnDisconnected() - { - base.OnDisconnected(); - - foreach (var forwardedPort in this._forwardedPorts.ToArray()) - { - this.RemoveForwardedPort(forwardedPort); - } - - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._disposeConnectionInfo) - ((IDisposable)this.ConnectionInfo).Dispose(); - - if (this._inputStream != null) - { - this._inputStream.Dispose(); - this._inputStream = null; - } - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Diagnostics.CodeAnalysis; +using Renci.SshNet.Common; + +namespace Renci.SshNet +{ + /// + /// Provides client connection to SSH server. + /// + public class SshClient : BaseClient + { + /// + /// Holds the list of forwarded ports + /// + private readonly List _forwardedPorts; + + private Stream _inputStream; + + /// + /// Gets the list of forwarded ports. + /// + public IEnumerable ForwardedPorts + { + get + { + return this._forwardedPorts.AsReadOnly(); + } + } + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// + /// + /// + /// + /// + /// + /// is null. + public SshClient(ConnectionInfo connectionInfo) + : this(connectionInfo, false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid, or is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "C2A000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public SshClient(string host, int port, string username, string password) + : this(new PasswordConnectionInfo(host, port, username, password), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication password. + /// + /// + /// + /// is null. + /// is invalid, or is null or contains whitespace characters. + public SshClient(string host, string username, string password) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication private key file(s) . + /// + /// + /// + /// + /// is null. + /// is invalid, -or- is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication private key file(s) . + /// + /// + /// + /// + /// is null. + /// is invalid, -or- is null or contains whitespace characters. + public SshClient(string host, string username, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// Specified whether this instance owns the connection info. + /// is null. + /// + /// If is true, then the + /// connection info will be disposed when this instance is disposed. + /// + private SshClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo) + : base(connectionInfo, ownsConnectionInfo) + { + _forwardedPorts = new List(); + } + + #endregion + + /// + /// Called when client is disconnecting from the server. + /// + protected override void OnDisconnecting() + { + base.OnDisconnecting(); + + foreach (var port in this._forwardedPorts) + { + port.Stop(); + } + } + + /// + /// Adds the forwarded port. + /// + /// The port. + /// + /// + /// + /// + /// Forwarded port is already added to a different client. + /// is null. + /// Client is not connected. + public void AddForwardedPort(ForwardedPort port) + { + if (port == null) + throw new ArgumentNullException("port"); + + if (port.Session != null && port.Session != this.Session) + throw new InvalidOperationException("Forwarded port is already added to a different client."); + + port.Session = this.Session; + + this._forwardedPorts.Add(port); + } + + /// + /// Stops and removes the forwarded port from the list. + /// + /// Forwarded port. + /// is null. + public void RemoveForwardedPort(ForwardedPort port) + { + if (port == null) + throw new ArgumentNullException("port"); + + // Stop port forwarding before removing it + port.Stop(); + + port.Session = null; + + this._forwardedPorts.Remove(port); + } + + /// + /// Creates the command to be executed. + /// + /// The command text. + /// object. + /// Client is not connected. + public SshCommand CreateCommand(string commandText) + { + return this.CreateCommand(commandText, this.ConnectionInfo.Encoding); + } + + /// + /// Creates the command to be executed with specified encoding. + /// + /// The command text. + /// The encoding to use for results. + /// object which uses specified encoding. + /// TThis method will change current default encoding. + /// Client is not connected. + /// or is null. + public SshCommand CreateCommand(string commandText, Encoding encoding) + { + this.ConnectionInfo.Encoding = encoding; + return new SshCommand(this.Session, commandText); + } + + /// + /// Creates and executes the command. + /// + /// The command text. + /// Returns an instance of with execution results. + /// This method internally uses asynchronous calls. + /// + /// + /// + /// + /// CommandText property is empty. + /// Invalid Operation - An existing channel was used to execute this command. + /// Asynchronous operation is already in progress. + /// Client is not connected. + /// is null. + public SshCommand RunCommand(string commandText) + { + var cmd = this.CreateCommand(commandText); + cmd.Execute(); + return cmd; + } + + /// + /// Creates the shell. + /// + /// The input. + /// The output. + /// The extended output. + /// Name of the terminal. + /// The columns. + /// The rows. + /// The width. + /// The height. + /// The terminal mode. + /// Size of the internal read buffer. + /// + /// Returns a representation of a object. + /// + public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) + { + return new Shell(this.Session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize); + } + + /// + /// Creates the shell. + /// + /// The input. + /// The output. + /// The extended output. + /// Name of the terminal. + /// The columns. + /// The rows. + /// The width. + /// The height. + /// The terminal mode. + /// + /// Returns a representation of a object. + /// + public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes) + { + return this.CreateShell(input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024); + } + + /// + /// Creates the shell. + /// + /// The input. + /// The output. + /// The extended output. + /// + /// Returns a representation of a object. + /// + public Shell CreateShell(Stream input, Stream output, Stream extendedOutput) + { + return this.CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); + } + + /// + /// Creates the shell. + /// + /// The encoding to use to send the input. + /// The input. + /// The output. + /// The extended output. + /// Name of the terminal. + /// The columns. + /// The rows. + /// The width. + /// The height. + /// The terminal mode. + /// Size of the internal read buffer. + /// + /// Returns a representation of a object. + /// + public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) + { + this._inputStream = new MemoryStream(); + var writer = new StreamWriter(this._inputStream, encoding); + writer.Write(input); + writer.Flush(); + this._inputStream.Seek(0, SeekOrigin.Begin); + + return this.CreateShell(this._inputStream, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize); + } + + /// + /// Creates the shell. + /// + /// The encoding. + /// The input. + /// The output. + /// The extended output. + /// Name of the terminal. + /// The columns. + /// The rows. + /// The width. + /// The height. + /// The terminal modes. + /// + /// Returns a representation of a object. + /// + public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes) + { + return this.CreateShell(encoding, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024); + } + + /// + /// Creates the shell. + /// + /// The encoding. + /// The input. + /// The output. + /// The extended output. + /// + /// Returns a representation of a object. + /// + public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput) + { + return this.CreateShell(encoding, input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); + } + + /// + /// Creates the shell stream. + /// + /// Name of the terminal. + /// The columns. + /// The rows. + /// The width. + /// The height. + /// Size of the buffer. + /// + /// Reference to Created ShellStream object. + /// + public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize) + { + return this.CreateShellStream(terminalName, columns, rows, width, height, bufferSize, null); + } + + /// + /// Creates the shell stream. + /// + /// Name of the terminal. + /// The columns. + /// The rows. + /// The width. + /// The height. + /// Size of the buffer. + /// The terminal mode values. + /// + /// Reference to Created ShellStream object. + /// + public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary terminalModeValues) + { + return new ShellStream(this.Session, terminalName, columns, rows, width, height, bufferSize, terminalModeValues); + } + + /// + /// Stops forwarded ports. + /// + protected override void OnDisconnected() + { + base.OnDisconnected(); + + foreach (var forwardedPort in this._forwardedPorts.ToArray()) + { + this.RemoveForwardedPort(forwardedPort); + } + + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (this._inputStream != null) + { + this._inputStream.Dispose(); + this._inputStream = null; + } + } + } +} diff --git a/Renci.SshNet/SshCommand.NET40.cs b/Renci.SshNet/SshCommand.NET40.cs index ec94eba..9677e14 100644 --- a/Renci.SshNet/SshCommand.NET40.cs +++ b/Renci.SshNet/SshCommand.NET40.cs @@ -1,18 +1,17 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Renci.SshNet -{ - /// - /// Represents SSH command that can be executed. - /// - public partial class SshCommand - { - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem((o) => { action(); }); - } - } -} +using System; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Represents SSH command that can be executed. + /// + public partial class SshCommand + { + /// is null. + partial void ExecuteThread(Action action) + { + ThreadPool.QueueUserWorkItem(o => action()); + } + } +} diff --git a/Renci.SshNet/SshCommand.cs b/Renci.SshNet/SshCommand.cs index f369c35..c12d715 100644 --- a/Renci.SshNet/SshCommand.cs +++ b/Renci.SshNet/SshCommand.cs @@ -1,581 +1,575 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Connection; -using Renci.SshNet.Messages.Transport; -using System.Globalization; - -namespace Renci.SshNet -{ - /// - /// Represents SSH command that can be executed. - /// - public partial class SshCommand : IDisposable - { - private Session _session; - - private ChannelSession _channel; - - private CommandAsyncResult _asyncResult; - - private AsyncCallback _callback; - - private EventWaitHandle _sessionErrorOccuredWaitHandle = new AutoResetEvent(false); - - private Exception _exception; - - private bool _hasError; - - private object _endExecuteLock = new object(); - - /// - /// Gets the command text. - /// - public string CommandText { get; private set; } - - /// - /// Gets or sets the command timeout. - /// - /// - /// The command timeout. - /// - /// - /// - /// - public TimeSpan CommandTimeout { get; set; } - - /// - /// Gets the command exit status. - /// - /// - /// - /// - public int ExitStatus { get; private set; } - - /// - /// Gets the output stream. - /// - /// - /// - /// - public Stream OutputStream { get; private set; } - - /// - /// Gets the extended output stream. - /// - /// - /// - /// - public Stream ExtendedOutputStream { get; private set; } - - private StringBuilder _result; - /// - /// Gets the command execution result. - /// - /// - /// - /// - public string Result - { - get - { - if (this._result == null) - { - this._result = new StringBuilder(); - } - - if (this.OutputStream != null && this.OutputStream.Length > 0) - { - using (var sr = new StreamReader(this.OutputStream, this._session.ConnectionInfo.Encoding)) - { - this._result.Append(sr.ReadToEnd()); - } - } - - return this._result.ToString(); - } - } - - private StringBuilder _error; - /// - /// Gets the command execution error. - /// - /// - /// - /// - public string Error - { - get - { - if (this._hasError) - { - if (this._error == null) - { - this._error = new StringBuilder(); - } - - if (this.ExtendedOutputStream != null && this.ExtendedOutputStream.Length > 0) - { - using (var sr = new StreamReader(this.ExtendedOutputStream, this._session.ConnectionInfo.Encoding)) - { - this._error.Append(sr.ReadToEnd()); - } - } - - return this._error.ToString(); - } - else - return string.Empty; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The session. - /// The command text. - /// The encoding. - /// Either , or is null. - public SshCommand(Session session, string commandText) - { - if (session == null) - throw new ArgumentNullException("session"); - - if (commandText == null) - throw new ArgumentNullException("commandText"); - - this._session = session; - this.CommandText = commandText; - this.CommandTimeout = new TimeSpan(0, 0, 0, 0, -1); - - this._session.Disconnected += Session_Disconnected; - this._session.ErrorOccured += Session_ErrorOccured; - } - - /// - /// Begins an asynchronous command execution. - /// - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// - /// - /// - /// Asynchronous operation is already in progress. - /// Invalid operation. - /// CommandText property is empty. - /// Client is not connected. - /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. - public IAsyncResult BeginExecute() - { - return this.BeginExecute(null, null); - } - - /// - /// Begins an asynchronous command execution. - /// - /// An optional asynchronous callback, to be called when the command execution is complete. - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// Asynchronous operation is already in progress. - /// Invalid operation. - /// CommandText property is empty. - /// Client is not connected. - /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. - public IAsyncResult BeginExecute(AsyncCallback callback) - { - return this.BeginExecute(callback, null); - } - - /// - /// Begins an asynchronous command execution. - /// - /// An optional asynchronous callback, to be called when the command execution is complete. - /// A user-provided object that distinguishes this particular asynchronous read request from other requests. - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// Asynchronous operation is already in progress. - /// Invalid operation. - /// CommandText property is empty. - /// Client is not connected. - /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. - public IAsyncResult BeginExecute(AsyncCallback callback, object state) - { - // Prevent from executing BeginExecute before calling EndExecute - if (this._asyncResult != null) - { - throw new InvalidOperationException("Asynchronous operation is already in progress."); - } - - // Create new AsyncResult object - this._asyncResult = new CommandAsyncResult(this) - { - AsyncWaitHandle = new ManualResetEvent(false), - IsCompleted = false, - AsyncState = state, - }; - - // When command re-executed again, create a new channel - if (this._channel != null) - { - throw new SshException("Invalid operation."); - } - - this.CreateChannel(); - - if (string.IsNullOrEmpty(this.CommandText)) - throw new ArgumentException("CommandText property is empty."); - - this._callback = callback; - - this._channel.Open(); - - // Send channel command request - this._channel.SendExecRequest(this.CommandText); - - return _asyncResult; - } - - /// - /// Begins an asynchronous command execution. 22 - /// - /// The command text. - /// An optional asynchronous callback, to be called when the command execution is complete. - /// A user-provided object that distinguishes this particular asynchronous read request from other requests. - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// Client is not connected. - /// Operation has timed out. - public IAsyncResult BeginExecute(string commandText, AsyncCallback callback, object state) - { - this.CommandText = commandText; - - return BeginExecute(callback, state); - } - - /// - /// Waits for the pending asynchronous command execution to complete. - /// - /// The reference to the pending asynchronous request to finish. - /// Command execution result. - /// - /// - /// - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - public string EndExecute(IAsyncResult asyncResult) - { - if (this._asyncResult == asyncResult && this._asyncResult != null) - { - lock (this._endExecuteLock) - { - if (this._asyncResult != null) - { - // Make sure that operation completed if not wait for it to finish - this.WaitHandle(this._asyncResult.AsyncWaitHandle); - - if (this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - - this._channel = null; - - this._asyncResult = null; - - return this.Result; - } - } - } - - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - } - - /// - /// Executes command specified by property. - /// - /// Command execution result - /// - /// - /// - /// - /// - /// Client is not connected. - /// Operation has timed out. - public string Execute() - { - return this.EndExecute(this.BeginExecute(null, null)); - } - - /// - /// Cancels command execution in asynchronous scenarios. - /// - public void CancelAsync() - { - if (this._channel != null && this._channel.IsOpen && this._asyncResult != null) - { - this._channel.Close(); - } - } - - /// - /// Executes the specified command text. - /// - /// The command text. - /// Command execution result - /// Client is not connected. - /// Operation has timed out. - public string Execute(string commandText) - { - this.CommandText = commandText; - - return this.Execute(); - } - - private void CreateChannel() - { - this._channel = this._session.CreateChannel(); - this._channel.DataReceived += Channel_DataReceived; - this._channel.ExtendedDataReceived += Channel_ExtendedDataReceived; - this._channel.RequestReceived += Channel_RequestReceived; - this._channel.Closed += Channel_Closed; - - // Dispose of streams if already exists - if (this.OutputStream != null) - { - this.OutputStream.Dispose(); - this.OutputStream = null; - } - - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Dispose(); - this.ExtendedOutputStream = null; - } - - // Initialize output streams and StringBuilders - this.OutputStream = new PipeStream(); - this.ExtendedOutputStream = new PipeStream(); - - this._result = null; - this._error = null; - } - - private void Session_Disconnected(object sender, EventArgs e) - { - // If objected is disposed or being disposed don't handle this event - if (this._isDisposed) - return; - - this._exception = new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); - - this._sessionErrorOccuredWaitHandle.Set(); - } - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - // If objected is disposed or being disposed don't handle this event - if (this._isDisposed) - return; - - this._exception = e.Exception; - - this._sessionErrorOccuredWaitHandle.Set(); - } - - private void Channel_Closed(object sender, Common.ChannelEventArgs e) - { - if (this.OutputStream != null) - { - this.OutputStream.Flush(); - } - - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Flush(); - } - - this._asyncResult.IsCompleted = true; - - if (this._callback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => { this._callback(this._asyncResult); }); - } - ((EventWaitHandle)this._asyncResult.AsyncWaitHandle).Set(); - } - - private void Channel_RequestReceived(object sender, Common.ChannelRequestEventArgs e) - { - Message replyMessage = new ChannelFailureMessage(this._channel.LocalChannelNumber); - - if (e.Info is ExitStatusRequestInfo) - { - ExitStatusRequestInfo exitStatusInfo = e.Info as ExitStatusRequestInfo; - - this.ExitStatus = (int)exitStatusInfo.ExitStatus; - - replyMessage = new ChannelSuccessMessage(this._channel.LocalChannelNumber); - } - - if (e.Info.WantReply) - { - this._session.SendMessage(replyMessage); - } - } - - private void Channel_ExtendedDataReceived(object sender, Common.ChannelDataEventArgs e) - { - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Write(e.Data, 0, e.Data.Length); - this.ExtendedOutputStream.Flush(); - } - - if (e.DataTypeCode == 1) - { - this._hasError = true; - } - } - - private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e) - { - if (this.OutputStream != null) - { - this.OutputStream.Write(e.Data, 0, e.Data.Length); - this.OutputStream.Flush(); - } - - if (this._asyncResult != null) - { - lock (this._asyncResult) - { - this._asyncResult.BytesReceived += e.Data.Length; - } - } - } - - /// Command '{0}' has timed out. - /// The actual command will be included in the exception message. - private void WaitHandle(WaitHandle waitHandle) - { - var waitHandles = new WaitHandle[] - { - this._sessionErrorOccuredWaitHandle, - waitHandle, - }; - - switch (EventWaitHandle.WaitAny(waitHandles, this.CommandTimeout)) - { - case 0: - throw this._exception; - case System.Threading.WaitHandle.WaitTimeout: - throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", this.CommandText)); - default: - break; - } - } - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _isDisposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - - this._session.Disconnected -= Session_Disconnected; - this._session.ErrorOccured -= Session_ErrorOccured; - - // Dispose managed ResourceMessages. - if (this.OutputStream != null) - { - this.OutputStream.Dispose(); - this.OutputStream = null; - } - - // Dispose managed ResourceMessages. - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Dispose(); - this.ExtendedOutputStream = null; - } - - // Dispose managed ResourceMessages. - if (this._sessionErrorOccuredWaitHandle != null) - { - this._sessionErrorOccuredWaitHandle.Dispose(); - this._sessionErrorOccuredWaitHandle = null; - } - - // Dispose managed ResourceMessages. - if (this._channel != null) - { - this._channel.DataReceived -= Channel_DataReceived; - this._channel.ExtendedDataReceived -= Channel_ExtendedDataReceived; - this._channel.RequestReceived -= Channel_RequestReceived; - this._channel.Closed -= Channel_Closed; - - this._channel.Dispose(); - this._channel = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~SshCommand() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} +using System; +using System.IO; +using System.Text; +using System.Threading; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using Renci.SshNet.Messages; +using Renci.SshNet.Messages.Connection; +using Renci.SshNet.Messages.Transport; +using System.Globalization; + +namespace Renci.SshNet +{ + /// + /// Represents SSH command that can be executed. + /// + public partial class SshCommand : IDisposable + { + private readonly Session _session; + + private ChannelSession _channel; + + private CommandAsyncResult _asyncResult; + + private AsyncCallback _callback; + + private EventWaitHandle _sessionErrorOccuredWaitHandle = new AutoResetEvent(false); + + private Exception _exception; + + private bool _hasError; + + private readonly object _endExecuteLock = new object(); + + /// + /// Gets the command text. + /// + public string CommandText { get; private set; } + + /// + /// Gets or sets the command timeout. + /// + /// + /// The command timeout. + /// + /// + /// + /// + public TimeSpan CommandTimeout { get; set; } + + /// + /// Gets the command exit status. + /// + /// + /// + /// + public int ExitStatus { get; private set; } + + /// + /// Gets the output stream. + /// + /// + /// + /// + public Stream OutputStream { get; private set; } + + /// + /// Gets the extended output stream. + /// + /// + /// + /// + public Stream ExtendedOutputStream { get; private set; } + + private StringBuilder _result; + /// + /// Gets the command execution result. + /// + /// + /// + /// + public string Result + { + get + { + if (this._result == null) + { + this._result = new StringBuilder(); + } + + if (this.OutputStream != null && this.OutputStream.Length > 0) + { + using (var sr = new StreamReader(this.OutputStream, this._session.ConnectionInfo.Encoding)) + { + this._result.Append(sr.ReadToEnd()); + } + } + + return this._result.ToString(); + } + } + + private StringBuilder _error; + /// + /// Gets the command execution error. + /// + /// + /// + /// + public string Error + { + get + { + if (this._hasError) + { + if (this._error == null) + { + this._error = new StringBuilder(); + } + + if (this.ExtendedOutputStream != null && this.ExtendedOutputStream.Length > 0) + { + using (var sr = new StreamReader(this.ExtendedOutputStream, this._session.ConnectionInfo.Encoding)) + { + this._error.Append(sr.ReadToEnd()); + } + } + + return this._error.ToString(); + } + return string.Empty; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The session. + /// The command text. + /// Either , is null. + internal SshCommand(Session session, string commandText) + { + if (session == null) + throw new ArgumentNullException("session"); + if (commandText == null) + throw new ArgumentNullException("commandText"); + + this._session = session; + this.CommandText = commandText; + this.CommandTimeout = new TimeSpan(0, 0, 0, 0, -1); + + this._session.Disconnected += Session_Disconnected; + this._session.ErrorOccured += Session_ErrorOccured; + } + + /// + /// Begins an asynchronous command execution. + /// + /// + /// An that represents the asynchronous command execution, which could still be pending. + /// + /// + /// + /// + /// Asynchronous operation is already in progress. + /// Invalid operation. + /// CommandText property is empty. + /// Client is not connected. + /// Operation has timed out. + /// Asynchronous operation is already in progress. + /// CommandText property is empty. + public IAsyncResult BeginExecute() + { + return this.BeginExecute(null, null); + } + + /// + /// Begins an asynchronous command execution. + /// + /// An optional asynchronous callback, to be called when the command execution is complete. + /// + /// An that represents the asynchronous command execution, which could still be pending. + /// + /// Asynchronous operation is already in progress. + /// Invalid operation. + /// CommandText property is empty. + /// Client is not connected. + /// Operation has timed out. + /// Asynchronous operation is already in progress. + /// CommandText property is empty. + public IAsyncResult BeginExecute(AsyncCallback callback) + { + return this.BeginExecute(callback, null); + } + + /// + /// Begins an asynchronous command execution. + /// + /// An optional asynchronous callback, to be called when the command execution is complete. + /// A user-provided object that distinguishes this particular asynchronous read request from other requests. + /// + /// An that represents the asynchronous command execution, which could still be pending. + /// + /// Asynchronous operation is already in progress. + /// Invalid operation. + /// CommandText property is empty. + /// Client is not connected. + /// Operation has timed out. + /// Asynchronous operation is already in progress. + /// CommandText property is empty. + public IAsyncResult BeginExecute(AsyncCallback callback, object state) + { + // Prevent from executing BeginExecute before calling EndExecute + if (this._asyncResult != null) + { + throw new InvalidOperationException("Asynchronous operation is already in progress."); + } + + // Create new AsyncResult object + this._asyncResult = new CommandAsyncResult(this) + { + AsyncWaitHandle = new ManualResetEvent(false), + IsCompleted = false, + AsyncState = state, + }; + + // When command re-executed again, create a new channel + if (this._channel != null) + { + throw new SshException("Invalid operation."); + } + + this.CreateChannel(); + + if (string.IsNullOrEmpty(this.CommandText)) + throw new ArgumentException("CommandText property is empty."); + + this._callback = callback; + + this._channel.Open(); + + // Send channel command request + this._channel.SendExecRequest(this.CommandText); + + return _asyncResult; + } + + /// + /// Begins an asynchronous command execution. 22 + /// + /// The command text. + /// An optional asynchronous callback, to be called when the command execution is complete. + /// A user-provided object that distinguishes this particular asynchronous read request from other requests. + /// + /// An that represents the asynchronous command execution, which could still be pending. + /// + /// Client is not connected. + /// Operation has timed out. + public IAsyncResult BeginExecute(string commandText, AsyncCallback callback, object state) + { + this.CommandText = commandText; + + return BeginExecute(callback, state); + } + + /// + /// Waits for the pending asynchronous command execution to complete. + /// + /// The reference to the pending asynchronous request to finish. + /// Command execution result. + /// + /// + /// + /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. + /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. + public string EndExecute(IAsyncResult asyncResult) + { + if (this._asyncResult == asyncResult && this._asyncResult != null) + { + lock (this._endExecuteLock) + { + if (this._asyncResult != null) + { + // Make sure that operation completed if not wait for it to finish + this.WaitOnHandle(this._asyncResult.AsyncWaitHandle); + + if (this._channel.IsOpen) + { + this._channel.SendEof(); + + this._channel.Close(); + } + + this._channel = null; + + this._asyncResult = null; + + return this.Result; + } + } + } + + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + } + + /// + /// Executes command specified by property. + /// + /// Command execution result + /// + /// + /// + /// + /// + /// Client is not connected. + /// Operation has timed out. + public string Execute() + { + return this.EndExecute(this.BeginExecute(null, null)); + } + + /// + /// Cancels command execution in asynchronous scenarios. + /// + public void CancelAsync() + { + if (this._channel != null && this._channel.IsOpen && this._asyncResult != null) + { + this._channel.Close(); + } + } + + /// + /// Executes the specified command text. + /// + /// The command text. + /// Command execution result + /// Client is not connected. + /// Operation has timed out. + public string Execute(string commandText) + { + this.CommandText = commandText; + + return this.Execute(); + } + + private void CreateChannel() + { + this._channel = this._session.CreateClientChannel(); + this._channel.DataReceived += Channel_DataReceived; + this._channel.ExtendedDataReceived += Channel_ExtendedDataReceived; + this._channel.RequestReceived += Channel_RequestReceived; + this._channel.Closed += Channel_Closed; + + // Dispose of streams if already exists + if (this.OutputStream != null) + { + this.OutputStream.Dispose(); + this.OutputStream = null; + } + + if (this.ExtendedOutputStream != null) + { + this.ExtendedOutputStream.Dispose(); + this.ExtendedOutputStream = null; + } + + // Initialize output streams and StringBuilders + this.OutputStream = new PipeStream(); + this.ExtendedOutputStream = new PipeStream(); + + this._result = null; + this._error = null; + } + + private void Session_Disconnected(object sender, EventArgs e) + { + // If objected is disposed or being disposed don't handle this event + if (this._isDisposed) + return; + + this._exception = new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); + + this._sessionErrorOccuredWaitHandle.Set(); + } + + private void Session_ErrorOccured(object sender, ExceptionEventArgs e) + { + // If objected is disposed or being disposed don't handle this event + if (this._isDisposed) + return; + + this._exception = e.Exception; + + this._sessionErrorOccuredWaitHandle.Set(); + } + + private void Channel_Closed(object sender, ChannelEventArgs e) + { + if (this.OutputStream != null) + { + this.OutputStream.Flush(); + } + + if (this.ExtendedOutputStream != null) + { + this.ExtendedOutputStream.Flush(); + } + + this._asyncResult.IsCompleted = true; + + if (this._callback != null) + { + // Execute callback on different thread + this.ExecuteThread(() => this._callback(this._asyncResult)); + } + ((EventWaitHandle)this._asyncResult.AsyncWaitHandle).Set(); + } + + private void Channel_RequestReceived(object sender, ChannelRequestEventArgs e) + { + Message replyMessage = new ChannelFailureMessage(this._channel.LocalChannelNumber); + + if (e.Info is ExitStatusRequestInfo) + { + ExitStatusRequestInfo exitStatusInfo = e.Info as ExitStatusRequestInfo; + + this.ExitStatus = (int)exitStatusInfo.ExitStatus; + + replyMessage = new ChannelSuccessMessage(this._channel.LocalChannelNumber); + } + + if (e.Info.WantReply) + { + this._session.SendMessage(replyMessage); + } + } + + private void Channel_ExtendedDataReceived(object sender, ChannelDataEventArgs e) + { + if (this.ExtendedOutputStream != null) + { + this.ExtendedOutputStream.Write(e.Data, 0, e.Data.Length); + this.ExtendedOutputStream.Flush(); + } + + if (e.DataTypeCode == 1) + { + this._hasError = true; + } + } + + private void Channel_DataReceived(object sender, ChannelDataEventArgs e) + { + if (this.OutputStream != null) + { + this.OutputStream.Write(e.Data, 0, e.Data.Length); + this.OutputStream.Flush(); + } + + if (this._asyncResult != null) + { + lock (this._asyncResult) + { + this._asyncResult.BytesReceived += e.Data.Length; + } + } + } + + /// Command '{0}' has timed out. + /// The actual command will be included in the exception message. + private void WaitOnHandle(WaitHandle waitHandle) + { + var waitHandles = new[] + { + this._sessionErrorOccuredWaitHandle, + waitHandle + }; + + switch (WaitHandle.WaitAny(waitHandles, this.CommandTimeout)) + { + case 0: + throw this._exception; + case WaitHandle.WaitTimeout: + throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", this.CommandText)); + } + } + + partial void ExecuteThread(Action action); + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged ResourceMessages. + if (disposing) + { + + this._session.Disconnected -= Session_Disconnected; + this._session.ErrorOccured -= Session_ErrorOccured; + + // Dispose managed ResourceMessages. + if (this.OutputStream != null) + { + this.OutputStream.Dispose(); + this.OutputStream = null; + } + + // Dispose managed ResourceMessages. + if (this.ExtendedOutputStream != null) + { + this.ExtendedOutputStream.Dispose(); + this.ExtendedOutputStream = null; + } + + // Dispose managed ResourceMessages. + if (this._sessionErrorOccuredWaitHandle != null) + { + this._sessionErrorOccuredWaitHandle.Dispose(); + this._sessionErrorOccuredWaitHandle = null; + } + + // Dispose managed ResourceMessages. + if (this._channel != null) + { + this._channel.DataReceived -= Channel_DataReceived; + this._channel.ExtendedDataReceived -= Channel_ExtendedDataReceived; + this._channel.RequestReceived -= Channel_RequestReceived; + this._channel.Closed -= Channel_Closed; + + this._channel.Dispose(); + this._channel = null; + } + } + + // Note disposing has been done. + this._isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~SshCommand() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/SubsystemSession.cs b/Renci.SshNet/SubsystemSession.cs index cf9ed58..97ed6dd 100644 --- a/Renci.SshNet/SubsystemSession.cs +++ b/Renci.SshNet/SubsystemSession.cs @@ -1,263 +1,267 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using System.Diagnostics; -using System.Collections.Generic; -using System.Globalization; -using Renci.SshNet.Sftp.Responses; -using Renci.SshNet.Sftp.Requests; -using Renci.SshNet.Messages.Connection; - -namespace Renci.SshNet.Sftp -{ - /// - /// Base class for SSH subsystem implementations - /// - public abstract class SubsystemSession : IDisposable - { - private Session _session; - - private string _subsystemName; - - private ChannelSession _channel; - - private Exception _exception; - - private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(false); - - private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false); - - /// - /// Specifies a timeout to wait for operation to complete - /// - protected TimeSpan _operationTimeout; - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccurred; - - /// - /// Occurs when session has been disconnected form the server. - /// - public event EventHandler Disconnected; - - /// - /// Gets the channel number. - /// - protected uint ChannelNumber { get; private set; } - - protected Encoding Encoding { get; private set; } - - /// - /// Initializes a new instance of the SubsystemSession class. - /// - /// The session. - /// Name of the subsystem. - /// The operation timeout. - /// session - /// or is null. - public SubsystemSession(Session session, string subsystemName, TimeSpan operationTimeout, Encoding encoding) - { - if (session == null) - throw new ArgumentNullException("session"); - - if (subsystemName == null) - throw new ArgumentNullException("subsystemName"); - - this._session = session; - this._subsystemName = subsystemName; - this._operationTimeout = operationTimeout; - this.Encoding = encoding; - } - - /// - /// Connects subsystem on SSH channel. - /// - public void Connect() - { - this._channel = this._session.CreateChannel(); - - this._session.ErrorOccured += Session_ErrorOccured; - this._session.Disconnected += Session_Disconnected; - this._channel.DataReceived += Channel_DataReceived; - this._channel.Closed += Channel_Closed; - - this._channel.Open(); - - this.ChannelNumber = this._channel.RemoteChannelNumber; - - this._channel.SendSubsystemRequest(_subsystemName); - - this.OnChannelOpen(); - } - - /// - /// Disconnects subsystem channel. - /// - public void Disconnect() - { - this._channel.SendEof(); - - this._channel.Close(); - } - - /// - /// Sends data to the subsystem. - /// - /// The data to be sent. - public void SendData(byte[] data) - { - this._channel.SendData(data); - } - - /// - /// Called when channel is open. - /// - protected abstract void OnChannelOpen(); - - /// - /// Called when data is received. - /// - /// The data type code. - /// The data. - protected abstract void OnDataReceived(uint dataTypeCode, byte[] data); - - /// - /// Raises the error. - /// - /// The error. - protected void RaiseError(Exception error) - { - this._exception = error; - - this._errorOccuredWaitHandle.Set(); - - if (this.ErrorOccurred != null) - { - this.ErrorOccurred(this, new ExceptionEventArgs(error)); - } - } - - private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e) - { - this.OnDataReceived(e.DataTypeCode, e.Data); - } - - private void Channel_Closed(object sender, Common.ChannelEventArgs e) - { - this._channelClosedWaitHandle.Set(); - } - - internal void WaitHandle(WaitHandle waitHandle, TimeSpan operationTimeout) - { - var waitHandles = new WaitHandle[] - { - this._errorOccuredWaitHandle, - this._channelClosedWaitHandle, - waitHandle, - }; - - switch (EventWaitHandle.WaitAny(waitHandles, operationTimeout)) - { - case 0: - throw this._exception; - case 1: - throw new SshException("Channel was closed."); - case System.Threading.WaitHandle.WaitTimeout: - throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Operation has timed out.")); - default: - break; - } - } - - private void Session_Disconnected(object sender, EventArgs e) - { - if (this.Disconnected != null) - { - this.Disconnected(this, new EventArgs()); - } - - this.RaiseError(new SshException("Connection was lost")); - } - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - this.RaiseError(e.Exception); - } - - #region IDisposable Members - - private bool _isDisposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - if (this._channel != null) - { - this._channel.DataReceived -= Channel_DataReceived; - - this._channel.Dispose(); - this._channel = null; - } - - this._session.ErrorOccured -= Session_ErrorOccured; - this._session.Disconnected -= Session_Disconnected; - - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._errorOccuredWaitHandle != null) - { - this._errorOccuredWaitHandle.Dispose(); - this._errorOccuredWaitHandle = null; - } - - if (this._channelClosedWaitHandle != null) - { - this._channelClosedWaitHandle.Dispose(); - this._channelClosedWaitHandle = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Finalizes an instance of the class. - /// - ~SubsystemSession() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} +using System; +using System.Globalization; +using System.Text; +using System.Threading; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Connection; + +namespace Renci.SshNet.Sftp +{ + /// + /// Base class for SSH subsystem implementations + /// + public abstract class SubsystemSession : IDisposable + { + private readonly Session _session; + private readonly string _subsystemName; + private ChannelSession _channel; + private Exception _exception; + private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(false); + private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false); + + /// + /// Specifies a timeout to wait for operation to complete + /// + protected TimeSpan _operationTimeout; + + /// + /// Occurs when an error occurred. + /// + public event EventHandler ErrorOccurred; + + /// + /// Occurs when session has been disconnected form the server. + /// + public event EventHandler Disconnected; + + /// + /// Gets the channel associated with this session. + /// + /// + /// The channel associated with this session. + /// + internal ChannelSession Channel + { + get { return _channel; } + } + + /// + /// Gets the character encoding to use. + /// + protected Encoding Encoding { get; private set; } + + /// + /// Initializes a new instance of the SubsystemSession class. + /// + /// The session. + /// Name of the subsystem. + /// The operation timeout. + /// The character encoding to use. + /// or or is null. + protected SubsystemSession(Session session, string subsystemName, TimeSpan operationTimeout, Encoding encoding) + { + if (session == null) + throw new ArgumentNullException("session"); + if (subsystemName == null) + throw new ArgumentNullException("subsystemName"); + if (encoding == null) + throw new ArgumentNullException("encoding"); + + this._session = session; + this._subsystemName = subsystemName; + this._operationTimeout = operationTimeout; + this.Encoding = encoding; + } + + /// + /// Connects subsystem on SSH channel. + /// + public void Connect() + { + this._channel = this._session.CreateClientChannel(); + + this._session.ErrorOccured += Session_ErrorOccured; + this._session.Disconnected += Session_Disconnected; + this._channel.DataReceived += Channel_DataReceived; + this._channel.Closed += Channel_Closed; + this._channel.Open(); + this._channel.SendSubsystemRequest(_subsystemName); + this.OnChannelOpen(); + } + + /// + /// Disconnects subsystem channel. + /// + public void Disconnect() + { + this._channel.SendEof(); + this._channel.Close(); + } + + /// + /// Sends data to the subsystem. + /// + /// The data to be sent. + public void SendData(byte[] data) + { + this._channel.SendData(data); + } + + /// + /// Called when channel is open. + /// + protected abstract void OnChannelOpen(); + + /// + /// Called when data is received. + /// + /// The data type code. + /// The data. + protected abstract void OnDataReceived(uint dataTypeCode, byte[] data); + + /// + /// Raises the error. + /// + /// The error. + protected void RaiseError(Exception error) + { + this._exception = error; + + var errorOccuredWaitHandle = _errorOccuredWaitHandle; + if (errorOccuredWaitHandle != null) + errorOccuredWaitHandle.Set(); + SignalErrorOccurred(error); + } + + private void Channel_DataReceived(object sender, ChannelDataEventArgs e) + { + this.OnDataReceived(e.DataTypeCode, e.Data); + } + + private void Channel_Closed(object sender, ChannelEventArgs e) + { + var channelClosedWaitHandle = _channelClosedWaitHandle; + if (channelClosedWaitHandle != null) + channelClosedWaitHandle.Set(); + } + + internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan operationTimeout) + { + var waitHandles = new[] + { + this._errorOccuredWaitHandle, + this._channelClosedWaitHandle, + waitHandle + }; + + switch (WaitHandle.WaitAny(waitHandles, operationTimeout)) + { + case 0: + throw this._exception; + case 1: + throw new SshException("Channel was closed."); + case WaitHandle.WaitTimeout: + throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Operation has timed out.")); + } + } + + private void Session_Disconnected(object sender, EventArgs e) + { + SignalDisconnected(); + this.RaiseError(new SshException("Connection was lost")); + } + + private void Session_ErrorOccured(object sender, ExceptionEventArgs e) + { + this.RaiseError(e.Exception); + } + + private void SignalErrorOccurred(Exception error) + { + var errorOccurred = ErrorOccurred; + if (errorOccurred != null) + { + errorOccurred(this, new ExceptionEventArgs(error)); + } + } + + private void SignalDisconnected() + { + var disconnected = Disconnected; + if (disconnected != null) + { + disconnected(this, new EventArgs()); + } + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._isDisposed) + { + if (this._channel != null) + { + this._channel.DataReceived -= Channel_DataReceived; + this._channel.Closed -= Channel_Closed; + this._channel.Dispose(); + this._channel = null; + } + + this._session.ErrorOccured -= Session_ErrorOccured; + this._session.Disconnected -= Session_Disconnected; + + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + if (this._errorOccuredWaitHandle != null) + { + this._errorOccuredWaitHandle.Dispose(); + this._errorOccuredWaitHandle = null; + } + + if (this._channelClosedWaitHandle != null) + { + this._channelClosedWaitHandle.Dispose(); + this._channelClosedWaitHandle = null; + } + } + + // Note disposing has been done. + _isDisposed = true; + } + } + + /// + /// Finalizes an instance of the class. + /// + ~SubsystemSession() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/patch/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/patch/Renci.SshNet/Sftp/SftpSession.cs deleted file mode 100644 index d7e5a85..0000000 --- a/Renci.SshNet/patch/Renci.SshNet/Sftp/SftpSession.cs +++ /dev/null @@ -1,994 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using System.Diagnostics; -using System.Collections.Generic; -using System.Globalization; -using Renci.SshNet.Sftp.Responses; -using Renci.SshNet.Sftp.Requests; - -namespace Renci.SshNet.Sftp -{ - internal class SftpSession : SubsystemSession - { - private Dictionary _requests = new Dictionary(); - - private List _data = new List(32 * 1024); - - private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); - - /// - /// Gets remote working directory. - /// - public string WorkingDirectory { get; private set; } - - /// - /// Gets SFTP protocol version. - /// - public int ProtocolVersion { get; private set; } - - private long _requestId; - /// - /// Gets the next request id for sftp session. - /// - public uint NextRequestId - { - get - { -#if WINDOWS_PHONE - lock (this) - { - this._requestId++; - } - - return (uint)this._requestId; -#else - return ((uint)Interlocked.Increment(ref this._requestId)); -#endif - } - } - - public SftpSession(Session session, TimeSpan operationTimeout) - : base(session, "sftp", operationTimeout) - { - } - - public void ChangeDirectory(string path) - { - var fullPath = this.GetCanonicalPath(path); - - var handle = this.RequestOpenDir(fullPath); - - this.RequestClose(handle); - - this.WorkingDirectory = fullPath; - } - - internal void SendMessage(SftpMessage sftpMessage) - { - var messageData = sftpMessage.GetBytes(); - - var data = new byte[4 + messageData.Length]; - - ((uint)messageData.Length).GetBytes().CopyTo(data, 0); - messageData.CopyTo(data, 4); - - this.SendData(data); - } - - /// - /// Resolves path into absolute path on the server. - /// - /// Path to resolve. - /// Absolute path - internal string GetCanonicalPath(string path) - { - var fullPath = path; - - if (!string.IsNullOrEmpty(path) && path[0] != '/' && this.WorkingDirectory != null) - { - if (this.WorkingDirectory[this.WorkingDirectory.Length - 1] == '/') - { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.WorkingDirectory, path); - } - else - { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", this.WorkingDirectory, path); - } - } - - var canonizedPath = string.Empty; - - var realPathFiles = this.RequestRealPath(fullPath, true); - - if (realPathFiles != null) - { - canonizedPath = realPathFiles.First().Key; - } - - if (!string.IsNullOrEmpty(canonizedPath)) - return canonizedPath; - - // Check for special cases - if (fullPath.EndsWith("/.", StringComparison.InvariantCultureIgnoreCase) || - fullPath.EndsWith("/..", StringComparison.InvariantCultureIgnoreCase) || - fullPath.Equals("/", StringComparison.InvariantCultureIgnoreCase) || - fullPath.IndexOf('/') < 0) - return fullPath; - - var pathParts = fullPath.Split(new char[] { '/' }); - - var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1); - - if (string.IsNullOrEmpty(partialFullPath)) - partialFullPath = "/"; - - realPathFiles = this.RequestRealPath(partialFullPath, true); - - if (realPathFiles != null) - { - canonizedPath = realPathFiles.First().Key; - } - - if (string.IsNullOrEmpty(canonizedPath)) - { - return fullPath; - } - else - { - var slash = string.Empty; - if (canonizedPath[canonizedPath.Length - 1] != '/') - slash = "/"; - return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Length - 1]); - } - } - - internal bool FileExistsCommand(string path, Flags flags) - { - var handle = this.RequestOpen(path, flags, true); - if (handle == null) - { - return false; - } - else - { - this.RequestClose(handle); - - return true; - } - } - - protected override void OnChannelOpen() - { - this.SendMessage(new SftpInitRequest(3)); - - this.WaitHandle(this._sftpVersionConfirmed, this._operationTimeout); - - this.ProtocolVersion = 3; - - // Resolve current directory - this.WorkingDirectory = this.RequestRealPath(".").First().Key; - } - - protected override void OnDataReceived(uint dataTypeCode, byte[] data) - { - // Add channel data to internal data holder - this._data.AddRange(data); - - while (this._data.Count > 4 + 1) - { - // Extract packet length - var packetLength = (this._data[0] << 24 | this._data[1] << 16 | this._data[2] << 8 | this._data[3]); - - // Check if complete packet data is available - if (this._data.Count < packetLength + 4) - { - // Wait for complete message to arrive first - break; - } - this._data.RemoveRange(0, 4); - - // Create buffer to hold packet data - var packetData = new byte[packetLength]; - - // Cope packet data to array - this._data.CopyTo(0, packetData, 0, packetLength); - - // Remove loaded data from _data holder - this._data.RemoveRange(0, packetLength); - - // Load SFTP Message and handle it - var response = SftpMessage.Load(packetData); - - try - { - var versionResponse = response as SftpVersionResponse; - if (versionResponse != null) - { - if (versionResponse.Version == 3) - { - this._sftpVersionConfirmed.Set(); - } - else - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", versionResponse.Version)); - } - } - else - { - this.HandleResponse(response as SftpResponse); - } - } - catch (Exception exp) - { - this.RaiseError(exp); - break; - } - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - if (this._sftpVersionConfirmed != null) - { - this._sftpVersionConfirmed.Dispose(); - this._sftpVersionConfirmed = null; - } - } - } - - private void SendRequest(SftpRequest request) - { - lock (this._requests) - { - this._requests.Add(request.RequestId, request); - } - - this.SendMessage(request); - //this.SendData(new SftpDataMessage(this.ChannelNumber, request)); - - //var messageData = request.GetBytes(); - - //var data = new byte[4 + messageData.Length]; - - //((uint)messageData.Length).GetBytes().CopyTo(data, 0); - //messageData.CopyTo(data, 4); - - //this.SendData(data); - - } - - #region SFTP API functions - - public void RequestPosixRename(string oldPath, string newPath) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new PosixRenameRequest(this.NextRequestId, oldPath, newPath, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - public SftpFilesytemInformation RequestStatVfs(string path, bool nullOnError = false) - { - SftpFilesytemInformation information = null; - using (var wait = new AutoResetEvent(false)) - { - var request = new StatVfsRequest(this.NextRequestId, path, - (response) => - { - information = response.OfType().Information; - wait.Set(); - }, - (response) => - { - if (nullOnError) - { - wait.Set(); - } - else - { - ThrowSftpException(response); - } - - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return information; - } - - - - - - - - - - - - //#define SSH_FXP_INIT 1 - //#define SSH_FXP_VERSION 2 - - /// - /// Performs SSH_FXP_OPEN request - /// - /// The path. - /// The flags. - /// if set to true returns null instead of throwing an exception. - /// - internal byte[] RequestOpen(string path, Flags flags, bool nullOnError = false) - { - byte[] handle = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpOpenRequest(this.NextRequestId, path, flags, - (response) => - { - handle = response.Handle; - wait.Set(); - }, - (response) => - { - if (nullOnError) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return handle; - } - - /// - /// Performs SSH_FXP_CLOSE request. - /// - /// The handle. - internal void RequestClose(byte[] handle) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpCloseRequest(this.NextRequestId, handle, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - /// - /// Performs SSH_FXP_READ request. - /// - /// The handle. - /// The offset. - /// The length. - /// data array; null if EOF - internal byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length) - { - byte[] data = new byte[0]; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadRequest(this.NextRequestId, handle, offset, length, - (response) => - { - data = response.Data; - wait.Set(); - }, - (response) => - { - if (response.StatusCode == StatusCodes.Eof) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return data; - } - - /// - /// Performs SSH_FXP_WRITE request. - /// - /// The handle. - /// The offset. - /// The data to send. - /// The wait event handle if needed. - internal void RequestWrite(byte[] handle, UInt64 offset, byte[] data, EventWaitHandle wait) - { - var maximumDataSize = 1024 * 32 - 38; - - if (data.Length < maximumDataSize + 1) - { - var request = new SftpWriteRequest(this.NextRequestId, handle, offset, data, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - if (wait != null) - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - if (wait != null) - this.WaitHandle(wait, this._operationTimeout); - } - else - { - var block = data.Length / maximumDataSize + 1; - - for (int i = 0; i < block; i++) - { - var blockBufferSize = Math.Min(data.Length - maximumDataSize * i, maximumDataSize); - var blockBuffer = new byte[blockBufferSize]; - - Buffer.BlockCopy(data, i * maximumDataSize, blockBuffer, 0, blockBufferSize); - - var request = new SftpWriteRequest(this.NextRequestId, handle, offset + (ulong)(i * maximumDataSize), blockBuffer, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - if (wait != null) - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - if (wait != null) - this.WaitHandle(wait, this._operationTimeout); - } - } - } - - /// - /// Performs SSH_FXP_LSTAT request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - internal SftpFileAttributes RequestLStat(string path, bool nullOnError = false) - { - SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpLStatRequest(this.NextRequestId, path, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - this.ThrowSftpException(response); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return attributes; - } - - /// - /// Performs SSH_FXP_FSTAT request. - /// - /// The handle. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - internal SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false) - { - SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpFStatRequest(this.NextRequestId, handle, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - this.ThrowSftpException(response); - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return attributes; - } - - /// - /// Performs SSH_FXP_SETSTAT request. - /// - /// The path. - /// The attributes. - internal void RequestSetStat(string path, SftpFileAttributes attributes) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpSetStatRequest(this.NextRequestId, path, attributes, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - /// - /// Performs SSH_FXP_FSETSTAT request. - /// - /// The handle. - /// The attributes. - internal void RequestFSetStat(byte[] handle, SftpFileAttributes attributes) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpFSetStatRequest(this.NextRequestId, handle, attributes, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - /// - /// Performs SSH_FXP_OPENDIR request - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - internal byte[] RequestOpenDir(string path, bool nullOnError = false) - { - byte[] handle = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpOpenDirRequest(this.NextRequestId, path, - (response) => - { - handle = response.Handle; - wait.Set(); - }, - (response) => - { - if (nullOnError) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return handle; - } - - /// - /// Performs SSH_FXP_READDIR request - /// - /// The handle. - /// - internal KeyValuePair[] RequestReadDir(byte[] handle) - { - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadDirRequest(this.NextRequestId, handle, - (response) => - { - result = response.Files; - wait.Set(); - }, - (response) => - { - if (response.StatusCode == StatusCodes.Eof) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return result; - } - - /// - /// Performs SSH_FXP_REMOVE request. - /// - /// The path. - internal void RequestRemove(string path) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRemoveRequest(this.NextRequestId, path, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - /// - /// Performs SSH_FXP_MKDIR request. - /// - /// The path. - internal void RequestMkDir(string path) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpMkDirRequest(this.NextRequestId, path, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - /// - /// Performs SSH_FXP_RMDIR request. - /// - /// The path. - internal void RequestRmDir(string path) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRmDirRequest(this.NextRequestId, path, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - /// - /// Performs SSH_FXP_REALPATH request - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - internal KeyValuePair[] RequestRealPath(string path, bool nullOnError = false) - { - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRealPathRequest(this.NextRequestId, path, - (response) => - { - result = response.Files; - - wait.Set(); - }, - (response) => - { - if (nullOnError) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return result; - } - - /// - /// Performs SSH_FXP_STAT request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - internal SftpFileAttributes RequestStat(string path, bool nullOnError = false) - { - SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpStatRequest(this.NextRequestId, path, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - if (nullOnError) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return attributes; - } - - /// - /// Performs SSH_FXP_RENAME request. - /// - /// The old path. - /// The new path. - internal void RequestRename(string oldPath, string newPath) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRenameRequest(this.NextRequestId, oldPath, newPath, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - /// - /// Performs SSH_FXP_READLINK request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - internal KeyValuePair[] RequestReadLink(string path, bool nullOnError = false) - { - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadLinkRequest(this.NextRequestId, path, - (response) => - { - result = response.Files; - - wait.Set(); - }, - (response) => - { - if (nullOnError) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - - return result; - } - - /// - /// Performs SSH_FXP_SYMLINK request. - /// - /// The linkpath. - /// The targetpath. - internal void RequestSymLink(string linkpath, string targetpath) - { - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpSymLinkRequest(this.NextRequestId, linkpath, targetpath, - (response) => - { - if (response.StatusCode == StatusCodes.Ok) - { - wait.Set(); - } - else - { - this.ThrowSftpException(response); - } - }); - - this.SendRequest(request); - - this.WaitHandle(wait, this._operationTimeout); - } - } - - #endregion - - private void ThrowSftpException(SftpStatusResponse response) - { - if (response.StatusCode == StatusCodes.PermissionDenied) - { - throw new SftpPermissionDeniedException(response.ErrorMessage); - } - else if (response.StatusCode == StatusCodes.NoSuchFile) - { - throw new SftpPathNotFoundException(response.ErrorMessage); - } - else - { - throw new SshException(response.ErrorMessage); - } - } - - private void HandleResponse(SftpResponse response) - { - SftpRequest request = null; - lock (this._requests) - { - this._requests.TryGetValue(response.ResponseId, out request); - if (request != null) - { - this._requests.Remove(response.ResponseId); - } - } - - if (request == null) - throw new InvalidOperationException("Invalid response."); - - request.Complete(response); - } - } -} From 8eb20b34ec27c430cf3d235fb588ed4aa71a937f Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 21 Jul 2014 19:34:53 +0200 Subject: [PATCH 016/134] Renci Pageant patch --- Renci.SshNet/AgentAuthenticationMethod.cs | 220 ++++++++++++++++++++++ Renci.SshNet/AgentConnectionInfo.cs | 193 +++++++++++++++++++ Renci.SshNet/IAgentProtocol.cs | 11 ++ Renci.SshNet/IdentityReference.cs | 17 ++ 4 files changed, 441 insertions(+) create mode 100644 Renci.SshNet/AgentAuthenticationMethod.cs create mode 100644 Renci.SshNet/AgentConnectionInfo.cs create mode 100644 Renci.SshNet/IAgentProtocol.cs create mode 100644 Renci.SshNet/IdentityReference.cs diff --git a/Renci.SshNet/AgentAuthenticationMethod.cs b/Renci.SshNet/AgentAuthenticationMethod.cs new file mode 100644 index 0000000..95c6853 --- /dev/null +++ b/Renci.SshNet/AgentAuthenticationMethod.cs @@ -0,0 +1,220 @@ +using System; +using System.Linq; +using Renci.SshNet.Messages.Authentication; +using Renci.SshNet.Messages; +using Renci.SshNet.Common; +using System.Threading; + +namespace Renci.SshNet +{ + /// + /// Provides functionality to perform private key authentication. + /// + public class AgentAuthenticationMethod : AuthenticationMethod, IDisposable + { + private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; + + private EventWaitHandle _authenticationCompleted = new ManualResetEvent(false); + + private bool _isSignatureRequired; + + /// + /// Gets authentication method name + /// + public override string Name + { + get { return "publickey"; } + } + + + public IAgentProtocol Protocol { get; private set; } + + + /// + /// Initializes a new instance of the class. + /// + /// The username. + /// The key files. + /// is whitespace or null. + public AgentAuthenticationMethod(string username, IAgentProtocol protocol) + : base(username) + { + this.Protocol = protocol; + } + + /// + /// Authenticates the specified session. + /// + /// The session to authenticate. + /// + public override AuthenticationResult Authenticate(Session session) + { + if (this.Protocol == null) + return AuthenticationResult.Failure; + + session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; + session.MessageReceived += Session_MessageReceived; + + session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK"); + + foreach (var identity in this.Protocol.GetIdentities()) + { + this._authenticationCompleted.Reset(); + this._isSignatureRequired = false; + + var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, identity.Type, identity.Blob); + + + // Send public key authentication request + session.SendMessage(message); + + session.WaitHandle(this._authenticationCompleted); + + if (this._isSignatureRequired) + { + this._authenticationCompleted.Reset(); + + var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, identity.Type, identity.Blob); + + var signatureData = new SignatureData(message, session.SessionId).GetBytes(); + + signatureMessage.Signature = this.Protocol.SignData(identity, signatureData); + + // Send public key authentication request with signature + session.SendMessage(signatureMessage); + } + + session.WaitHandle(this._authenticationCompleted); + + if (this._authenticationResult == AuthenticationResult.Success) + { + break; + } + } + + session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; + session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; + session.MessageReceived -= Session_MessageReceived; + + session.UnRegisterMessage("SSH_MSG_USERAUTH_PK_OK"); + + return this._authenticationResult; + } + + private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) + { + this._authenticationResult = AuthenticationResult.Success; + + this._authenticationCompleted.Set(); + } + + private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) + { + if (e.Message.PartialSuccess) + this._authenticationResult = AuthenticationResult.PartialSuccess; + else + this._authenticationResult = AuthenticationResult.Failure; + + // Copy allowed authentication methods + this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); + + this._authenticationCompleted.Set(); + } + + private void Session_MessageReceived(object sender, MessageEventArgs e) + { + var publicKeyMessage = e.Message as PublicKeyMessage; + if (publicKeyMessage != null) + { + this._isSignatureRequired = true; + this._authenticationCompleted.Set(); + } + } + + #region IDisposable Members + + private bool isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this.isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + if (this._authenticationCompleted != null) + { + this._authenticationCompleted.Dispose(); + this._authenticationCompleted = null; + } + } + + // Note disposing has been done. + isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~AgentAuthenticationMethod() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + + private class SignatureData : SshData + { + private RequestMessagePublicKey _message; + + private byte[] _sessionId; + + public SignatureData(RequestMessagePublicKey message, byte[] sessionId) + { + this._message = message; + this._sessionId = sessionId; + } + + protected override void LoadData() + { + throw new System.NotImplementedException(); + } + + protected override void SaveData() + { + this.WriteBinaryString(this._sessionId); + this.Write((byte)50); + this.Write(this._message.Username); + this.WriteAscii("ssh-connection"); + this.WriteAscii("publickey"); + this.Write((byte)1); + this.WriteAscii(this._message.PublicKeyAlgorithmName); + this.WriteBinaryString(this._message.PublicKeyData); + } + } + + } +} diff --git a/Renci.SshNet/AgentConnectionInfo.cs b/Renci.SshNet/AgentConnectionInfo.cs new file mode 100644 index 0000000..5d5bb52 --- /dev/null +++ b/Renci.SshNet/AgentConnectionInfo.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections.ObjectModel; + +namespace Renci.SshNet +{ + /// + /// Provides connection information when private key authentication method is used + /// + public class AgentConnectionInfo : ConnectionInfo, IDisposable + { + /// + /// Gets the key files used for authentication. + /// + public IAgentProtocol Protocol { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Connection key files. + public AgentConnectionInfo(string host, string username, IAgentProtocol protocol) + : this(host, 22, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, protocol) + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Connection username. + /// Connection key files. + public AgentConnectionInfo(string host, int port, string username, IAgentProtocol protocol) + : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, protocol) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// The port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The key files. + public AgentConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, IAgentProtocol protocol) + : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, protocol) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// The port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The key files. + public AgentConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, IAgentProtocol protocol) + : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, protocol) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The key files. + public AgentConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, IAgentProtocol protocol) + : this(host, 22, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, protocol) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The key files. + public AgentConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, IAgentProtocol protocol) + : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, protocol) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + /// The key files. + public AgentConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, IAgentProtocol protocol) + : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, protocol) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// The port. + /// Connection username. + /// Type of the proxy. + /// The proxy host. + /// The proxy port. + /// The proxy username. + /// The proxy password. + /// The key files. + public AgentConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, IAgentProtocol protocol) + : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new AgentAuthenticationMethod(username,protocol)) + { + this.Protocol = protocol; + } + + #region IDisposable Members + + private bool isDisposed = false; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this.isDisposed) + { + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + if (this.AuthenticationMethods != null) + { + foreach (var authenticationMethods in this.AuthenticationMethods.OfType()) + { + authenticationMethods.Dispose(); + } + } + } + + // Note disposing has been done. + isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~AgentConnectionInfo() + { + // Do not re-create Dispose clean-up code here. + // Calling Dispose(false) is optimal in terms of + // readability and maintainability. + Dispose(false); + } + + #endregion + } +} diff --git a/Renci.SshNet/IAgentProtocol.cs b/Renci.SshNet/IAgentProtocol.cs new file mode 100644 index 0000000..bdbce20 --- /dev/null +++ b/Renci.SshNet/IAgentProtocol.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Renci.SshNet +{ + public interface IAgentProtocol + { + IEnumerable GetIdentities(); + + byte[] SignData(IdentityReference identity, byte[] data); + } +} \ No newline at end of file diff --git a/Renci.SshNet/IdentityReference.cs b/Renci.SshNet/IdentityReference.cs new file mode 100644 index 0000000..bf6a4f6 --- /dev/null +++ b/Renci.SshNet/IdentityReference.cs @@ -0,0 +1,17 @@ +namespace Renci.SshNet +{ + public class IdentityReference + { + public string Type { get; private set; } + public byte[] Blob { get; private set; } + public string Comment { get; private set; } + + public IdentityReference(string type,byte[] blob,string comment ) + { + this.Type = type; + this.Blob = blob; + this.Comment = comment; + } + + } +} \ No newline at end of file From 3482167ad3cc55da35e9d4fc86232729220da307 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 21 Jul 2014 20:13:13 +0200 Subject: [PATCH 017/134] missing files from blind update --- Renci.SshNet/Channels/ClientChannel.cs | 101 ++++++++++++++++++ Renci.SshNet/Channels/ServerChannel.cs | 60 +++++++++++ .../Common/ChannelOpenConfirmedEventArgs.cs | 37 +++++++ Renci.SshNet/Common/Extensions.NET40.cs | 36 +++++++ Renci.SshNet/Properties/CommonAssemblyInfo.cs | 27 +++++ 5 files changed, 261 insertions(+) create mode 100644 Renci.SshNet/Channels/ClientChannel.cs create mode 100644 Renci.SshNet/Channels/ServerChannel.cs create mode 100644 Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs create mode 100644 Renci.SshNet/Common/Extensions.NET40.cs create mode 100644 Renci.SshNet/Properties/CommonAssemblyInfo.cs diff --git a/Renci.SshNet/Channels/ClientChannel.cs b/Renci.SshNet/Channels/ClientChannel.cs new file mode 100644 index 0000000..3fe61a2 --- /dev/null +++ b/Renci.SshNet/Channels/ClientChannel.cs @@ -0,0 +1,101 @@ +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Connection; + +namespace Renci.SshNet.Channels +{ + internal abstract class ClientChannel : Channel + { + /// + /// Occurs when message is received. + /// + public event EventHandler OpenConfirmed; + + /// + /// Occurs when message received + /// + public event EventHandler OpenFailed; + + /// + /// Initializes the channel. + /// + /// The session. + /// Size of the window. + /// Size of the packet. + internal override void Initialize(Session session, uint localWindowSize, uint localPacketSize) + { + base.Initialize(session, localWindowSize, localPacketSize); + Session.ChannelOpenConfirmationReceived += OnChannelOpenConfirmation; + Session.ChannelOpenFailureReceived += OnChannelOpenFailure; + } + + /// + /// Called when channel is opened by the server. + /// + /// The remote channel number. + /// Initial size of the window. + /// Maximum size of the packet. + protected virtual void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize) + { + InitializeRemoteInfo(remoteChannelNumber, initialWindowSize, maximumPacketSize); + + // Channel is consider to be open when confirmation message was received + this.IsOpen = true; + + var openConfirmed = OpenConfirmed; + if (openConfirmed != null) + openConfirmed(this, new ChannelOpenConfirmedEventArgs(remoteChannelNumber, initialWindowSize, maximumPacketSize)); + } + + /// + /// Send message to open a channel. + /// + /// Message to send + protected void SendMessage(ChannelOpenMessage message) + { + Session.SendMessage(message); + } + + /// + /// Called when channel failed to open. + /// + /// The reason code. + /// The description. + /// The language. + protected virtual void OnOpenFailure(uint reasonCode, string description, string language) + { + var openFailed = OpenFailed; + if (openFailed != null) + openFailed(this, new ChannelOpenFailedEventArgs(LocalChannelNumber, reasonCode, description, language)); + } + + private void OnChannelOpenConfirmation(object sender, MessageEventArgs e) + { + if (e.Message.LocalChannelNumber == this.LocalChannelNumber) + { + this.OnOpenConfirmation(e.Message.RemoteChannelNumber, e.Message.InitialWindowSize, e.Message.MaximumPacketSize); + } + } + + private void OnChannelOpenFailure(object sender, MessageEventArgs e) + { + if (e.Message.LocalChannelNumber == this.LocalChannelNumber) + { + this.OnOpenFailure(e.Message.ReasonCode, e.Message.Description, e.Message.Language); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + var session = Session; + if (session != null) + { + session.ChannelOpenConfirmationReceived -= OnChannelOpenConfirmation; + session.ChannelOpenFailureReceived -= OnChannelOpenFailure; + } + } + } + } +} diff --git a/Renci.SshNet/Channels/ServerChannel.cs b/Renci.SshNet/Channels/ServerChannel.cs new file mode 100644 index 0000000..3724cc3 --- /dev/null +++ b/Renci.SshNet/Channels/ServerChannel.cs @@ -0,0 +1,60 @@ +using System; +using Renci.SshNet.Messages.Connection; + +namespace Renci.SshNet.Channels +{ + internal abstract class ServerChannel : Channel + { + internal void Initialize(Session session, uint localWindowSize, uint localPacketSize, uint remoteChannelNumber, uint remoteWindowSize, uint remotePacketSize) + { + Initialize(session, localWindowSize, localPacketSize); + InitializeRemoteInfo(remoteChannelNumber, remoteWindowSize, remotePacketSize); + //Session.ChannelOpenReceived += OnChannelOpen; + } + + //private void OnChannelOpen(object sender, MessageEventArgs e) + //{ + // var channelOpenMessage = e.Message; + + // if (channelOpenMessage.LocalChannelNumber == LocalChannelNumber) + // { + // _remoteChannelNumber = channelOpenMessage.LocalChannelNumber; + // RemoteWindowSize = channelOpenMessage.InitialWindowSize; + // _remotePacketSize = channelOpenMessage.MaximumPacketSize; + // OnOpen(e.Message.Info); + // } + //} + + protected void SendMessage(ChannelOpenConfirmationMessage message) + { + // No need to check whether channel is open when trying to open a channel + Session.SendMessage(message); + + // When we act as server, consider the channel open when we've sent the + // confirmation message to the peer + IsOpen = true; + } + + ///// + ///// Called when channel need to be open on the client. + ///// + ///// Channel open information. + //protected virtual void OnOpen(ChannelOpenInfo info) + //{ + //} + + protected override void Dispose(bool disposing) + { + if (disposing) + { + var session = Session; + if (session != null) + { + //session.ChannelOpenReceived -= OnChannelOpen; + } + } + + base.Dispose(disposing); + } + } +} diff --git a/Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs b/Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs new file mode 100644 index 0000000..676e8e3 --- /dev/null +++ b/Renci.SshNet/Common/ChannelOpenConfirmedEventArgs.cs @@ -0,0 +1,37 @@ +namespace Renci.SshNet.Common +{ + /// + /// Provides data for event. + /// + internal class ChannelOpenConfirmedEventArgs : ChannelEventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The remote channel number. + /// The initial window size. + /// The maximum packet size. + public ChannelOpenConfirmedEventArgs(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize) + : base(remoteChannelNumber) + { + InitialWindowSize = initialWindowSize; + MaximumPacketSize = maximumPacketSize; + } + + /// + /// Gets the initial size of the window. + /// + /// + /// The initial size of the window. + /// + public uint InitialWindowSize { get; private set; } + + /// + /// Gets the maximum size of the packet. + /// + /// + /// The maximum size of the packet. + /// + public uint MaximumPacketSize { get; private set; } + } +} diff --git a/Renci.SshNet/Common/Extensions.NET40.cs b/Renci.SshNet/Common/Extensions.NET40.cs new file mode 100644 index 0000000..f90a358 --- /dev/null +++ b/Renci.SshNet/Common/Extensions.NET40.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; + +namespace Renci.SshNet +{ + /// + /// Collection of different extension method specific for .NET 4.0 + /// + public static partial class Extensions + { + /// + /// Indicates whether a specified string is null, empty, or consists only of white-space characters. + /// + /// The string to test. + /// + /// true if the value parameter is null or System.String.Empty, or if value consists exclusively of white-space characters; otherwise, false. + /// + internal static bool IsNullOrWhiteSpace(this string value) + { + return string.IsNullOrWhiteSpace(value); + } + + internal static bool CanRead(this Socket socket) + { + return socket.Connected && socket.Poll(-1, SelectMode.SelectRead) && socket.Available > 0; + } + + internal static bool CanWrite(this Socket socket) + { + return socket.Connected && socket.Poll(-1, SelectMode.SelectWrite); + } + } +} diff --git a/Renci.SshNet/Properties/CommonAssemblyInfo.cs b/Renci.SshNet/Properties/CommonAssemblyInfo.cs new file mode 100644 index 0000000..b2593d9 --- /dev/null +++ b/Renci.SshNet/Properties/CommonAssemblyInfo.cs @@ -0,0 +1,27 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyDescription("SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.")] +[assembly: AssemblyCompany("Renci")] +[assembly: AssemblyProduct("SSH.NET")] +[assembly: AssemblyCopyright("Copyright Renci 2010-2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: AssemblyVersion("2014.4.6")] +[assembly: AssemblyFileVersion("2014.4.6")] +[assembly: AssemblyInformationalVersion("2014.4.6-beta1")] +[assembly: CLSCompliant(false)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + + +#if DEBUG +[assembly: AssemblyConfiguration("Debug")] +#else +[assembly: AssemblyConfiguration("Release")] +#endif \ No newline at end of file From a32860e6c1ab3a0402f3d6a18b59ac67fe3a584a Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 21 Jul 2014 20:32:36 +0200 Subject: [PATCH 018/134] Final fixies of new Renci Looks working now. --- Renci.SshNet/AgentAuthenticationMethod.cs | 4 +- .../Documentation/Renci.SshNet.content | 32 + Renci.SshNet/Documentation/SshNet.shfbproj | 63 + Renci.SshNet/Renci.SshNet.csproj | 13 +- Renci.SshNet/Sftp/SftpFileAttributes.cs | 3 + Renci.SshNet/Sftp/SftpSession.cs | 5 +- Sshfs/Sshfs/MainForm.cs | 2 +- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 6 +- Sshfs/Sshfs/SftpContextStream.cs | 880 +++---- Sshfs/Sshfs/SftpFilesystem.cs | 2083 +++++++++-------- Sshfs/Sshfs/Sshfs.csproj | 458 ++-- 11 files changed, 1829 insertions(+), 1720 deletions(-) create mode 100644 Renci.SshNet/Documentation/Renci.SshNet.content create mode 100644 Renci.SshNet/Documentation/SshNet.shfbproj diff --git a/Renci.SshNet/AgentAuthenticationMethod.cs b/Renci.SshNet/AgentAuthenticationMethod.cs index 95c6853..06f23e5 100644 --- a/Renci.SshNet/AgentAuthenticationMethod.cs +++ b/Renci.SshNet/AgentAuthenticationMethod.cs @@ -69,7 +69,7 @@ public override AuthenticationResult Authenticate(Session session) // Send public key authentication request session.SendMessage(message); - session.WaitHandle(this._authenticationCompleted); + session.WaitOnHandle(this._authenticationCompleted); if (this._isSignatureRequired) { @@ -85,7 +85,7 @@ public override AuthenticationResult Authenticate(Session session) session.SendMessage(signatureMessage); } - session.WaitHandle(this._authenticationCompleted); + session.WaitOnHandle(this._authenticationCompleted); if (this._authenticationResult == AuthenticationResult.Success) { diff --git a/Renci.SshNet/Documentation/Renci.SshNet.content b/Renci.SshNet/Documentation/Renci.SshNet.content new file mode 100644 index 0000000..a40dcd9 --- /dev/null +++ b/Renci.SshNet/Documentation/Renci.SshNet.content @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Renci.SshNet/Documentation/SshNet.shfbproj b/Renci.SshNet/Documentation/SshNet.shfbproj new file mode 100644 index 0000000..87334df --- /dev/null +++ b/Renci.SshNet/Documentation/SshNet.shfbproj @@ -0,0 +1,63 @@ + + + + + Debug + AnyCPU + 2.0 + {f4b5a704-a7e7-4719-a343-ede32568762b} + 1.9.5.0 + + Documentation + Documentation + Documentation + + .\Help\ + SshNet.Help + en-US + Standard + Blank + False + VS2010 + False + Guid + SSH .NET Client Library Documentation + AboveNamespaces + + + + OnlyWarningsAndErrors + HtmlHelp1 + False + .NET Framework 4.0 + True + False + False + True + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Renci.SshNet/Renci.SshNet.csproj b/Renci.SshNet/Renci.SshNet.csproj index 6d8da3f..b453698 100644 --- a/Renci.SshNet/Renci.SshNet.csproj +++ b/Renci.SshNet/Renci.SshNet.csproj @@ -41,7 +41,7 @@ true
- ..\Renci.SshNet.snk + sshfs_4every1.pfx @@ -50,6 +50,8 @@ + + Code @@ -147,8 +149,13 @@ Code + + + + + Code @@ -466,10 +473,8 @@ - - Renci.SshNet.snk - + diff --git a/Renci.SshNet/Sftp/SftpFileAttributes.cs b/Renci.SshNet/Sftp/SftpFileAttributes.cs index 1d40ab0..525820e 100644 --- a/Renci.SshNet/Sftp/SftpFileAttributes.cs +++ b/Renci.SshNet/Sftp/SftpFileAttributes.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Globalization; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("WinSshFS, PublicKey=0024000004800000940000000602000000240000525341310004000001000100bb1df492d3d63bb4aedb73672b0c0694ad7838ea17c6d49685ef1a03301ae6de5a4b4b1795844de0ddab49254d05bd533b5a02c24a580346274b204b8975536ffe36b4e7bb8c82e419264c335682ad44861e99eef16e95ee978742064c9335283ecb6e2fd325ea97942a51a3fc1a7d9948dd919b0d4ad25148d5490cc37f85b4")] namespace Renci.SshNet.Sftp { diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs index 60c7e55..40b0f3f 100644 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ b/Renci.SshNet/Sftp/SftpSession.cs @@ -8,6 +8,9 @@ using System.Globalization; using Renci.SshNet.Sftp.Responses; using Renci.SshNet.Sftp.Requests; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("WinSshFS, PublicKey=0024000004800000940000000602000000240000525341310004000001000100bb1df492d3d63bb4aedb73672b0c0694ad7838ea17c6d49685ef1a03301ae6de5a4b4b1795844de0ddab49254d05bd533b5a02c24a580346274b204b8975536ffe36b4e7bb8c82e419264c335682ad44861e99eef16e95ee978742064c9335283ecb6e2fd325ea97942a51a3fc1a7d9948dd919b0d4ad25148d5490cc37f85b4")] namespace Renci.SshNet.Sftp { @@ -23,7 +26,7 @@ internal class SftpSession : SubsystemSession private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); - private IDictionary _supportedExtensions; + internal IDictionary _supportedExtensions; /// /// Gets remote working directory. diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index dafd24a..0309d19 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -438,7 +438,7 @@ private void MainForm_Shown(object sender, EventArgs e) private void openFileDialog_FileOk(object sender, CancelEventArgs e) { //don't check larger files - if (new FileInfo(openFileDialog.FileName).Length>4*4*1024||!PrivateKeyFile.IsValid(openFileDialog.FileName)) + if (new FileInfo(openFileDialog.FileName).Length>4*4*1024/*||!PrivateKeyFile.IsValid(openFileDialog.FileName) not supported in current version, solved on open*/) { MessageBox.Show(this, diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 4a2b4d7..1cf922e 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.5.6")] -[assembly: AssemblyFileVersion("0.1.5.6")] +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.5.8")] +[assembly: AssemblyFileVersion("0.1.5.8")] diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs index 88fef64..8024177 100644 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -1,441 +1,441 @@ -// Copyright (c) 2012 Dragan Mladjenovic -// 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. - -using System; -using System.Diagnostics; -using System.IO; -using System.Threading; -using Renci.SshNet.Sftp; - -namespace Sshfs -{ - internal sealed class SftpContextStream : Stream - { - private const int WRITE_BUFFER_SIZE = 28*1024;// (1024*32 - 38)*4; - private const int READ_BUFFER_SIZE = 128*1024; - private readonly byte[] _writeBuffer; - private byte[] _readBuffer = new byte[0]; - - - private readonly SftpSession _session; - private SftpFileAttributes _attributes; - private byte[] _handle; - - private bool _writeMode; - private int _writeBufferPosition; - private int _readBufferPosition; - private long _position; - - internal SftpContextStream(SftpSession session, string path, FileMode mode, FileAccess access, - SftpFileAttributes attributes) - { - Flags flags = Flags.None; - - switch (access) - { - case FileAccess.Read: - flags = Flags.Read; - break; - case FileAccess.Write: - flags = Flags.Write; - break; - case FileAccess.ReadWrite: - flags = Flags.Read | Flags.Write; - break; - } - - switch (mode) - { - case FileMode.Append: - flags |= Flags.Append; - break; - case FileMode.Create: - if (attributes == null) - { - flags |= Flags.CreateNew; - } - else - { - flags |= Flags.Truncate; - } - break; - case FileMode.CreateNew: - flags |= Flags.CreateNew; - break; - case FileMode.Open: - break; - case FileMode.OpenOrCreate: - flags |= Flags.CreateNewOrOpen; - break; - case FileMode.Truncate: - flags |= Flags.Truncate; - break; - } - - _session = session; - - _handle = _session.RequestOpen(path, flags); - - _attributes = attributes ?? _session.RequestFStat(_handle); - - - if (access.HasFlag(FileAccess.Write)) - { - _writeBuffer = new byte[WRITE_BUFFER_SIZE]; - _writeMode = true; - } - - _position = mode != FileMode.Append ? 0 : _attributes.Size; - } - - - public SftpFileAttributes Attributes - { - get - { - lock (this) - { - if (_writeMode) - { - - - - - //FlushWriteBuffer(); - SetupRead(); - _attributes = _session.RequestFStat(_handle); - - } - } - return _attributes; - } - } - - - public override bool CanRead - { - get { throw new NotImplementedException(); } - } - - public override bool CanSeek - { - get { throw new NotImplementedException(); } - } - - public override bool CanWrite - { - get { throw new NotImplementedException(); } - } - - public override long Length - { - get { throw new NotImplementedException(); } - } - - - public override long Position - { - get { return _position; } - set - { - if (!_writeMode) - { - long newPosn = _position - _readBufferPosition; - if (value >= newPosn && value < - (newPosn + _readBuffer.Length)) - { - _readBufferPosition = (int) (value - newPosn); - } - else - { - _readBufferPosition = 0; - _readBuffer = new byte[0]; - } - } - else - { - // Console.WriteLine("Position:{0}=?{1}",value,_position); - if (_position != value) - { - FlushWriteBuffer(); - } - } - _position = value; - } - } - - - public override void Close() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - - public override void Flush() - { - lock (this) - { - if (_writeMode) - { - FlushWriteBuffer(); - } - else - { - /* if (_bufferPosn < _bufferLen) - { - _position -= _bufferPosn; - }*/ - _readBufferPosition = 0; - _readBuffer = new byte[0]; - } - } - } - - - public override int Read(byte[] buffer, int offset, int count) - { - int readLen = 0; - - - // Lock down the file stream while we do this. - - // Set up for the read operation. - SetupRead(); - - // Read data into the caller's buffer. - while (count > 0) - { - // How much data do we have available in the buffer? - int tempLen = _readBuffer.Length - _readBufferPosition; - if (tempLen <= 0) - { - _readBufferPosition = 0; - - _readBuffer = _session.RequestRead(_handle, (ulong) _position, READ_BUFFER_SIZE); - - - if (_readBuffer.Length > 0) - { - tempLen = _readBuffer.Length; - } - else - { - break; - } - } - - - // Don't read more than the caller wants. - if (tempLen > count) - { - tempLen = count; - } - - // Copy stream data to the caller's buffer. - Debug.WriteLine("Copy:{0},{1},{2},{3},{4}",_readBuffer,_readBufferPosition,buffer,offset,tempLen); - Buffer.BlockCopy(_readBuffer, _readBufferPosition, buffer, offset, tempLen); - - // Advance to the next buffer positions. - readLen += tempLen; - offset += tempLen; - count -= tempLen; - _readBufferPosition += tempLen; - _position += tempLen; - } - - - // Return the number of bytes that were read to the caller. - return readLen; - } - - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override void SetLength(long value) - { - lock (this) - { - // Lock down the file stream while we do this. - - // Setup this object for writing. - SetupWrite(); - - _attributes.Size = value; - - _session.RequestFSetStat(_handle, _attributes); - } - } - - - public override void Write(byte[] buffer, int offset, int count) - { - // Lock down the file stream while we do this. - - // Setup this object for writing. - SetupWrite(); - - // Write data to the file stream. - // while (count > 0) - // { - // Determine how many bytes we can write to the buffer. - int tempLen = WRITE_BUFFER_SIZE - _writeBufferPosition; - - /* if (tempLen <= 0) - { - - _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer); - - _writeBufferPosition = 0; - tempLen = WRITE_BUFFER_SIZE; - }*/ - - - if (tempLen >= count) - { - // No: copy the data to the write buffer first. - Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); - _writeBufferPosition += count; - } - else - { - FlushWriteBuffer(); - - - if (count >= WRITE_BUFFER_SIZE) - { - _session.RequestWrite(_handle, (ulong) _position, buffer, null,null); - } - else - { - Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); - _writeBufferPosition += count; - } - } - // Advance the buffer and stream positions. - _position += count; - // offset += tempLen; - // count -= tempLen; - // } - - // If the buffer is full, then do a speculative flush now, - // rather than waiting for the next call to this method. - if (_writeBufferPosition == WRITE_BUFFER_SIZE) - { - - _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, null,null); - - - _writeBufferPosition = 0; - } - } - - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - - if (_handle != null) - { - if (_writeMode) - { - FlushWriteBuffer(); - } - - _session.RequestClose(_handle); - - _handle = null; - } - } - - - private void FlushWriteBuffer() - { - // Console.WriteLine("FLUSHHHH the water"); - if (_writeBufferPosition > 0) - { - // Console.WriteLine("Written:{0}",_writeBufferPosition); - var data = new byte[_writeBufferPosition]; - Buffer.BlockCopy(_writeBuffer, 0, data, 0, _writeBufferPosition); - - - - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, null,null); - - - _writeBufferPosition = 0; - } - } - -/* - private void FlushWriteBufferNoPipelining() - { - const int maximumDataSize = 1024 * 32 - 38; - Console.WriteLine("FLUSHHHH the water no pipe"); - if (_writeBufferPosition > 0) - { - Console.WriteLine("Written:{0}", _writeBufferPosition); - - int block = ((_writeBufferPosition - 1) / maximumDataSize) + 1; - for (int i = 0; i < block; i++) - { - var blockBufferSize = Math.Min(_writeBufferPosition - maximumDataSize * i, maximumDataSize); - var blockBuffer = new byte[blockBufferSize]; - - Buffer.BlockCopy(_writeBuffer, i*maximumDataSize, blockBuffer, 0, blockBufferSize); - - using (var wait = new AutoResetEvent(false)) - { - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition+i*maximumDataSize), blockBuffer, wait); - } - } - - _writeBufferPosition = 0; - } - } -*/ - - - private void SetupRead() - { - if (_writeMode) - { - FlushWriteBuffer(); - _writeMode = false; - } - } - - - private void SetupWrite() - { - if (_writeMode) return; - - /* if (_bufferPosn < _bufferLen) - { - _position -= _bufferPosn; - }*/ - _readBufferPosition = 0; - _writeBufferPosition = 0; - _readBuffer = new byte[0]; - _writeMode = true; - } - } +// Copyright (c) 2012 Dragan Mladjenovic +// 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. + +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using Renci.SshNet.Sftp; + +namespace Sshfs +{ + internal sealed class SftpContextStream : Stream + { + private const int WRITE_BUFFER_SIZE = 28*1024;// (1024*32 - 38)*4; + private const int READ_BUFFER_SIZE = 128*1024; + private readonly byte[] _writeBuffer; + private byte[] _readBuffer = new byte[0]; + + + private readonly SftpSession _session; + private SftpFileAttributes _attributes; + private byte[] _handle; + + private bool _writeMode; + private int _writeBufferPosition; + private int _readBufferPosition; + private long _position; + + internal SftpContextStream(SftpSession session, string path, FileMode mode, FileAccess access, + SftpFileAttributes attributes) + { + Flags flags = Flags.None; + + switch (access) + { + case FileAccess.Read: + flags = Flags.Read; + break; + case FileAccess.Write: + flags = Flags.Write; + break; + case FileAccess.ReadWrite: + flags = Flags.Read | Flags.Write; + break; + } + + switch (mode) + { + case FileMode.Append: + flags |= Flags.Append; + break; + case FileMode.Create: + if (attributes == null) + { + flags |= Flags.CreateNew; + } + else + { + flags |= Flags.Truncate; + } + break; + case FileMode.CreateNew: + flags |= Flags.CreateNew; + break; + case FileMode.Open: + break; + case FileMode.OpenOrCreate: + flags |= Flags.CreateNewOrOpen; + break; + case FileMode.Truncate: + flags |= Flags.Truncate; + break; + } + + _session = session; + + _handle = _session.RequestOpen(path, flags); + + _attributes = attributes ?? _session.RequestFStat(_handle); + + + if (access.HasFlag(FileAccess.Write)) + { + _writeBuffer = new byte[WRITE_BUFFER_SIZE]; + _writeMode = true; + } + + _position = mode != FileMode.Append ? 0 : _attributes.Size; + } + + + public SftpFileAttributes Attributes + { + get + { + lock (this) + { + if (_writeMode) + { + + + + + //FlushWriteBuffer(); + SetupRead(); + _attributes = _session.RequestFStat(_handle); + + } + } + return _attributes; + } + } + + + public override bool CanRead + { + get { throw new NotImplementedException(); } + } + + public override bool CanSeek + { + get { throw new NotImplementedException(); } + } + + public override bool CanWrite + { + get { throw new NotImplementedException(); } + } + + public override long Length + { + get { throw new NotImplementedException(); } + } + + + public override long Position + { + get { return _position; } + set + { + if (!_writeMode) + { + long newPosn = _position - _readBufferPosition; + if (value >= newPosn && value < + (newPosn + _readBuffer.Length)) + { + _readBufferPosition = (int) (value - newPosn); + } + else + { + _readBufferPosition = 0; + _readBuffer = new byte[0]; + } + } + else + { + // Console.WriteLine("Position:{0}=?{1}",value,_position); + if (_position != value) + { + FlushWriteBuffer(); + } + } + _position = value; + } + } + + + public override void Close() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + + public override void Flush() + { + lock (this) + { + if (_writeMode) + { + FlushWriteBuffer(); + } + else + { + /* if (_bufferPosn < _bufferLen) + { + _position -= _bufferPosn; + }*/ + _readBufferPosition = 0; + _readBuffer = new byte[0]; + } + } + } + + + public override int Read(byte[] buffer, int offset, int count) + { + int readLen = 0; + + + // Lock down the file stream while we do this. + + // Set up for the read operation. + SetupRead(); + + // Read data into the caller's buffer. + while (count > 0) + { + // How much data do we have available in the buffer? + int tempLen = _readBuffer.Length - _readBufferPosition; + if (tempLen <= 0) + { + _readBufferPosition = 0; + + _readBuffer = _session.RequestRead(_handle, (ulong) _position, READ_BUFFER_SIZE); + + + if (_readBuffer.Length > 0) + { + tempLen = _readBuffer.Length; + } + else + { + break; + } + } + + + // Don't read more than the caller wants. + if (tempLen > count) + { + tempLen = count; + } + + // Copy stream data to the caller's buffer. + Debug.WriteLine("Copy:{0},{1},{2},{3},{4}",_readBuffer,_readBufferPosition,buffer,offset,tempLen); + Buffer.BlockCopy(_readBuffer, _readBufferPosition, buffer, offset, tempLen); + + // Advance to the next buffer positions. + readLen += tempLen; + offset += tempLen; + count -= tempLen; + _readBufferPosition += tempLen; + _position += tempLen; + } + + + // Return the number of bytes that were read to the caller. + return readLen; + } + + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + lock (this) + { + // Lock down the file stream while we do this. + + // Setup this object for writing. + SetupWrite(); + + _attributes.Size = value; + + _session.RequestFSetStat(_handle, _attributes); + } + } + + + public override void Write(byte[] buffer, int offset, int count) + { + // Lock down the file stream while we do this. + + // Setup this object for writing. + SetupWrite(); + + // Write data to the file stream. + // while (count > 0) + // { + // Determine how many bytes we can write to the buffer. + int tempLen = WRITE_BUFFER_SIZE - _writeBufferPosition; + + /* if (tempLen <= 0) + { + + _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer); + + _writeBufferPosition = 0; + tempLen = WRITE_BUFFER_SIZE; + }*/ + + + if (tempLen >= count) + { + // No: copy the data to the write buffer first. + Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); + _writeBufferPosition += count; + } + else + { + FlushWriteBuffer(); + + + if (count >= WRITE_BUFFER_SIZE) + { + _session.RequestWrite(_handle, (ulong) _position, buffer, null,null); + } + else + { + Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); + _writeBufferPosition += count; + } + } + // Advance the buffer and stream positions. + _position += count; + // offset += tempLen; + // count -= tempLen; + // } + + // If the buffer is full, then do a speculative flush now, + // rather than waiting for the next call to this method. + if (_writeBufferPosition == WRITE_BUFFER_SIZE) + { + + _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, null,null); + + + _writeBufferPosition = 0; + } + } + + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + + if (_handle != null) + { + if (_writeMode) + { + FlushWriteBuffer(); + } + + _session.RequestClose(_handle); + + _handle = null; + } + } + + + private void FlushWriteBuffer() + { + // Console.WriteLine("FLUSHHHH the water"); + if (_writeBufferPosition > 0) + { + // Console.WriteLine("Written:{0}",_writeBufferPosition); + var data = new byte[_writeBufferPosition]; + Buffer.BlockCopy(_writeBuffer, 0, data, 0, _writeBufferPosition); + + + + _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, null,null); + + + _writeBufferPosition = 0; + } + } + +/* + private void FlushWriteBufferNoPipelining() + { + const int maximumDataSize = 1024 * 32 - 38; + Console.WriteLine("FLUSHHHH the water no pipe"); + if (_writeBufferPosition > 0) + { + Console.WriteLine("Written:{0}", _writeBufferPosition); + + int block = ((_writeBufferPosition - 1) / maximumDataSize) + 1; + for (int i = 0; i < block; i++) + { + var blockBufferSize = Math.Min(_writeBufferPosition - maximumDataSize * i, maximumDataSize); + var blockBuffer = new byte[blockBufferSize]; + + Buffer.BlockCopy(_writeBuffer, i*maximumDataSize, blockBuffer, 0, blockBufferSize); + + using (var wait = new AutoResetEvent(false)) + { + _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition+i*maximumDataSize), blockBuffer, wait); + } + } + + _writeBufferPosition = 0; + } + } +*/ + + + private void SetupRead() + { + if (_writeMode) + { + FlushWriteBuffer(); + _writeMode = false; + } + } + + + private void SetupWrite() + { + if (_writeMode) return; + + /* if (_bufferPosn < _bufferLen) + { + _position -= _bufferPosn; + }*/ + _readBufferPosition = 0; + _writeBufferPosition = 0; + _readBuffer = new byte[0]; + _writeMode = true; + } + } } \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index e50a072..c3d450b 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -1,1041 +1,1044 @@ -// Copyright (c) 2012 Dragan Mladjenovic -// 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. - - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.Caching; -using System.Security.AccessControl; -using System.Security.Principal; -using System.Text; -using DokanNet; -using Renci.SshNet; -using Renci.SshNet.Common; -using Renci.SshNet.Sftp; -using FileAccess = DokanNet.FileAccess; - -namespace Sshfs -{ - internal sealed class SftpFilesystem : BaseClient, IDokanOperations - { - - #region Constants - - // ReSharper disable InconsistentNaming - // private static readonly string[] _filter = { - // "desktop.ini", "Desktop.ini", "autorun.inf", - // "AutoRun.inf", //"Thumbs.db", - // }; - - // private static readonly Regex _dfregex = new Regex(@"^[a-z0-9/]+\s+(?[0-9]+)K\s+(?[0-9]+)K" - // , RegexOptions.Compiled); - - // ReSharper restore InconsistentNaming - - #endregion - - #region Fields - - private readonly MemoryCache _cache = MemoryCache.Default; - - private SftpSession _sftpSession; - private readonly TimeSpan _operationTimeout = TimeSpan.FromSeconds(30);//new TimeSpan(0, 0, 0, 0, -1); - private string _rootpath; - - private readonly bool _useOfflineAttribute; - private readonly bool _debugMode; - - - private int _userId; - private HashSet _userGroups; - - private readonly int _attributeCacheTimeout; - private readonly int _directoryCacheTimeout; - - private bool _supportsPosixRename; - private bool _supportsStatVfs; - - private readonly string _volumeLabel; - - #endregion - - #region Constructors - - public SftpFilesystem(ConnectionInfo connectionInfo, string rootpath, string label = null, - bool useOfflineAttribute = false, - bool debugMode = false, int attributeCacheTimeout = 5, int directoryCacheTimeout = 60) - : base(connectionInfo) - { - _rootpath = rootpath; - _directoryCacheTimeout = directoryCacheTimeout; - _attributeCacheTimeout = attributeCacheTimeout; - _useOfflineAttribute = useOfflineAttribute; - _debugMode = debugMode; - _volumeLabel = label ?? String.Format("{0} on '{1}'", ConnectionInfo.Username, ConnectionInfo.Host); - } - - #endregion - - #region Method overrides - - protected override void OnConnected() - { - base.OnConnected(); - - _sftpSession = new SftpSession(Session, _operationTimeout, Encoding.UTF8); - - - _sftpSession.Connect(); - - - _userId = GetUserId(); - if (_userId != -1) - _userGroups = new HashSet(GetUserGroupsIds()); - - - if (String.IsNullOrWhiteSpace(_rootpath)) - { - _rootpath = _sftpSession.RequestRealPath(".").First().Key; - } - - _supportsPosixRename = - _sftpSession._supportedExtensions.Contains(new KeyValuePair("posix-rename@openssh.com", "1")); - _supportsStatVfs = - _sftpSession._supportedExtensions.Contains(new KeyValuePair("statvfs@openssh.com", "2")); - // KeepAliveInterval=TimeSpan.FromSeconds(5); - - // Session.Disconnected+= (sender, args) => Debugger.Break(); - } - - - protected override void Dispose(bool disposing) - { - if (_sftpSession != null) - { - _sftpSession.Dispose(); - _sftpSession = null; - } - base.Dispose(disposing); - } - - #endregion - - #region Methods - - private string GetUnixPath(string path) - { - // return String.Concat(_rootpath, path.Replace('\\', '/')); - return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/')); - } - - - [Conditional("DEBUG")] - private void Log(string format, params object[] arg) - { - if (_debugMode) - { - Console.WriteLine(format, arg); - } - Debug.Write(DateTime.Now.ToLongTimeString() + " "); - Debug.WriteLine(format, arg); - } - - private IEnumerable GetUserGroupsIds() - { - using (var cmd = new SshCommand(Session, "id -G ")) - { - cmd.Execute(); - return cmd.Result.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries).Select(Int32.Parse); - } - } - - private int GetUserId() - { - using (var cmd = new SshCommand(Session, "id -u ")) - // Thease commands seems to be POSIX so the only problem would be Windows enviroment - { - cmd.Execute(); - return cmd.ExitStatus == 0 ? Int32.Parse(cmd.Result) : -1; - } - } - - private bool UserCanRead(SftpFileAttributes attributes) - { - return _userId == -1 || (attributes.OwnerCanRead && attributes.UserId == _userId || - (attributes.GroupCanRead && _userGroups.Contains(attributes.GroupId) || - attributes.OthersCanRead)); - } - - private bool UserCanWrite(SftpFileAttributes attributes) - { - return _userId == -1 || (attributes.OwnerCanWrite && attributes.UserId == _userId || - (attributes.GroupCanWrite && _userGroups.Contains(attributes.GroupId) || - attributes.OthersCanWrite)); - } - - private bool UserCanExecute(SftpFileAttributes attributes) - { - return _userId == -1 || (attributes.OwnerCanExecute && attributes.UserId == _userId || - (attributes.GroupCanExecute && _userGroups.Contains(attributes.GroupId) || - attributes.OthersCanExecute)); - } - - private SftpFileAttributes GetAttributes(string path) - { - var sftpLStatAttributes = _sftpSession.RequestLStat(path, true); - if (sftpLStatAttributes == null || !sftpLStatAttributes.IsSymbolicLink) - { - return sftpLStatAttributes; - } - var sftpStatAttributes = _sftpSession.RequestStat(path, true); - return sftpStatAttributes ?? sftpLStatAttributes; - } - - private void InvalidateParentCache(string fileName) - { - int index = fileName.LastIndexOf('\\'); - _cache.Remove(index != 0 ? fileName.Substring(0, index) : "\\"); - } - - #endregion - - #region DokanOperations - - DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, - FileMode mode, FileOptions options, - FileAttributes attributes, DokanFileInfo info) - { - if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || - fileName.EndsWith("autorun.inf", StringComparison.OrdinalIgnoreCase)) //.... - { - return DokanError.ErrorFileNotFound; - } - - string path = GetUnixPath(fileName); - // var sftpFileAttributes = GetAttributes(path); - var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; - - if (sftpFileAttributes == null) - { - Log("cache miss"); - - sftpFileAttributes = GetAttributes(path); - if (sftpFileAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); - } - - - - - Log("Open| Name:{0},\n Mode:{1},\n Share{2},\n Disp:{3},\n Flags{4},\n Attr:{5}\n", fileName, access, - share, mode, options, attributes); - - switch (mode) - { - case FileMode.Open: - if (sftpFileAttributes != null) - { - if (((uint) access & 0xe0000027) == 0 || sftpFileAttributes.IsDirectory) - //check if only wants to read attributes,security info or open directory - { - Log("JustInfo:{0},{1}", fileName, sftpFileAttributes.IsDirectory); - info.IsDirectory = sftpFileAttributes.IsDirectory; - info.Context = new SftpContext(sftpFileAttributes); - return DokanError.ErrorSuccess; - } - } - else return DokanError.ErrorFileNotFound; - break; - case FileMode.CreateNew: - if (sftpFileAttributes != null) - return DokanError.ErrorAlreadyExists; - - InvalidateParentCache(fileName); // cache invalidate - break; - case FileMode.Truncate: - if (sftpFileAttributes == null) - return DokanError.ErrorFileNotFound; - InvalidateParentCache(fileName); - _cache.Remove(path); - break; - default: - - InvalidateParentCache(fileName); - break; - } - Log("NotJustInfo:{0}-{1}", info.Context, mode); - try - { - info.Context = new SftpContext(_sftpSession, path, mode, - ((ulong) access & 0x40010006) == 0 - ? System.IO.FileAccess.Read - : System.IO.FileAccess.ReadWrite, sftpFileAttributes); - } - catch (SshException) // Don't have access rights or try to read broken symlink - { - var ownerpath = path.Substring(0, path.LastIndexOf('/')); - var sftpPathAttributes = _cache.Get(ownerpath) as SftpFileAttributes; - - if (sftpPathAttributes == null) - { - Log("cache miss"); - - sftpPathAttributes = GetAttributes(ownerpath); - if (sftpPathAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); - else - { - Log("Up directory must be created"); - return DokanError.ErrorPathNotFound; - } - } - return DokanError.ErrorAccessDenied; - } - - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) - { - Log("OpenDir:{0}", fileName); - - - - - string path = GetUnixPath(fileName); - // var sftpFileAttributes = GetAttributes(GetUnixPath(fileName)); - var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; - - if (sftpFileAttributes == null) - { - Log("cache miss"); - - sftpFileAttributes = GetAttributes(path); - if (sftpFileAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); - } - - - - - if (sftpFileAttributes != null && sftpFileAttributes.IsDirectory) - { - //??? - if (!UserCanExecute(sftpFileAttributes) || !UserCanRead(sftpFileAttributes)) - { - return DokanError.ErrorAccessDenied; - } - - - info.IsDirectory = true; - info.Context = new SftpContext(sftpFileAttributes); - - var dircahe = _cache.Get(fileName) as Tuple>; - if (dircahe != null && dircahe.Item1 != sftpFileAttributes.LastWriteTime) - { - _cache.Remove(fileName); - } - return DokanError.ErrorSuccess; - } - return DokanError.ErrorPathNotFound; - } - - DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) - { - Log("CreateDir:{0}", fileName); - - - try - { - _sftpSession.RequestMkDir(GetUnixPath(fileName)); - InvalidateParentCache(fileName); //invalidate dircahe of the parent - } - catch (SftpPermissionDeniedException) - { - return DokanError.ErrorAccessDenied; - } - catch (SshException) // operation should fail with generic error if file already exists - { - return DokanError.ErrorAlreadyExists; - } - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) - { - Log("Cleanup:{0},Delete:{1}", info.Context,info.DeleteOnClose); - - if (info.Context != null) - { - (info.Context as SftpContext).Release(); - - info.Context = null; - } - - if (info.DeleteOnClose) - { - string path = GetUnixPath(fileName); - if (info.IsDirectory) - { - try - { - _sftpSession.RequestRmDir(path); - } - catch (SftpPathNotFoundException) //in case we are dealing with simbolic link - { - _sftpSession.RequestRemove(path); - } - } - else - { - _sftpSession.RequestRemove(path); - } - InvalidateParentCache(fileName); - _cache.Remove(path); - } - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) - { - Log("Close:{0}", info.Context); - - return DokanError.ErrorSuccess; - } - - - DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, - DokanFileInfo info) - { - Log("ReadFile:{0}:{1}|lenght:[{2}]|offset:[{3}]", fileName, - info.Context , buffer.Length, offset); - - if (info.Context == null) - { - //called when file is read as memory memory mapeded file usualy notepad and stuff - var handle = _sftpSession.RequestOpen(GetUnixPath(fileName), Flags.Read); - var data = _sftpSession.RequestRead(handle, (ulong) offset, (uint) buffer.Length); - _sftpSession.RequestClose(handle); - Buffer.BlockCopy(data, 0, buffer, 0, data.Length); - bytesRead = data.Length; - } - else - { - // var watch = Stopwatch.StartNew(); - var stream = (info.Context as SftpContext).Stream; - lock (stream) - { - stream.Position = offset; - bytesRead = stream.Read(buffer, 0, buffer.Length); - } - // watch.Stop(); - // Log("{0}",watch.ElapsedMilliseconds); - } - Log("END READ:{0},{1}",offset,info.Context); - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, - DokanFileInfo info) - { - - - Log("WriteFile:{0}:{1}|lenght:[{2}]|offset:[{3}]", fileName, - info.Context, buffer.Length, offset); - - - if (info.Context == null) // who would guess - { - var handle = _sftpSession.RequestOpen(GetUnixPath(fileName), Flags.Write); - // using (var wait = new AutoResetEvent(false)) - { - _sftpSession.RequestWrite(handle, (ulong) offset, buffer, null,null/*, wait*/); - } - _sftpSession.RequestClose(handle); - bytesWritten = buffer.Length; - } - else - { - var stream = (info.Context as SftpContext).Stream; - int written = 0; - int chunk = 16384;//maximum is payload 32768-X, optimum? 16384=1.5, 8192=1.9, 4092=2.3, 2048=2.3(5.8), 1024=2.35 - lock (stream) - { - while (written < buffer.Length) - { - int writecount = buffer.Length - written; - if (writecount > chunk) writecount = chunk; - stream.Position = offset + written; - stream.Write(buffer, written, writecount); - written += writecount; - } - - //stream.Position = offset; - //stream.Write(buffer, 0, buffer.Length); - - - } - // stream.Flush(); - bytesWritten = buffer.Length; - // TODO there are still some apps that don't check disk free space before write - } - - // Log("END WRITE:{0},{1},{2}", offset,info.Context,watch.ElapsedMilliseconds); - return DokanError.ErrorSuccess; - } - - - DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) - { - Log("FLUSH:{0}", fileName); - - (info.Context as SftpContext).Stream.Flush(); //I newer saw it get called ,but .. - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, - DokanFileInfo info) - { - Log("GetInfo:{0}:{1}", fileName,info.Context); - - var context = info.Context as SftpContext; - - SftpFileAttributes sftpFileAttributes; - if (context != null) - { - sftpFileAttributes = context.Attributes; - } - else - { - string path = GetUnixPath(fileName); - sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; - - if (sftpFileAttributes == null) - { - sftpFileAttributes = GetAttributes(path); - if (sftpFileAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); - } - } - - - fileInfo = new FileInformation - { - Attributes = - FileAttributes.NotContentIndexed, - FileName = String.Empty, - // GetInfo info doesn't use it maybe for sorting . - CreationTime = sftpFileAttributes.LastWriteTime, - LastAccessTime = sftpFileAttributes.LastAccessTime, - LastWriteTime = sftpFileAttributes.LastWriteTime, - Length = sftpFileAttributes.Size - }; - if (sftpFileAttributes.IsDirectory) - { - fileInfo.Attributes |= FileAttributes.Directory; - fileInfo.Length = 0; // Windows directories use length of 0 - } - else - { - fileInfo.Attributes |= FileAttributes.Normal; - } - if (fileName.Length != 1 && fileName[fileName.LastIndexOf('\\') + 1] == '.') - //aditional check if filename isn't \\ - { - fileInfo.Attributes |= FileAttributes.Hidden; - } - - if (!UserCanWrite(sftpFileAttributes)) - { - fileInfo.Attributes |= FileAttributes.ReadOnly; - } - if (_useOfflineAttribute) - { - fileInfo.Attributes |= FileAttributes.Offline; - } - // Console.WriteLine(sftpattributes.UserId + "|" + sftpattributes.GroupId + "L" + - // sftpattributes.OthersCanExecute + "K" + sftpattributes.OwnerCanExecute); - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) - { - Log("FindFiles:{0}", fileName); - - var dircache = _cache.Get(fileName) as Tuple>; - if (dircache != null) - { - files = (dircache).Item2; - Log("CacheHit:{0}", fileName); - return DokanError.ErrorSuccess; - } - - - byte[] handle; - try - { - handle = _sftpSession.RequestOpenDir(GetUnixPath(fileName)); - } - catch (SftpPermissionDeniedException) - { - files = null; - return DokanError.ErrorAccessDenied; - } - - - files = new List(); - for (var sftpFiles = _sftpSession.RequestReadDir(handle); - sftpFiles != null; - sftpFiles = _sftpSession.RequestReadDir(handle)) - { - - - - - (files as List).AddRange(sftpFiles.Select( - file => - { - var sftpFileAttributes = file.Value; - if (sftpFileAttributes.IsSymbolicLink) - { - sftpFileAttributes = _sftpSession.RequestStat( - GetUnixPath(String.Format("{0}{1}", fileName, file.Key)), true) ?? - file.Value; - } - - - var fileInformation = new FileInformation - { - Attributes = - FileAttributes.NotContentIndexed, - CreationTime - = - sftpFileAttributes - . - LastWriteTime, - FileName - = - file.Key - , - LastAccessTime - = - sftpFileAttributes - . - LastAccessTime, - LastWriteTime - = - sftpFileAttributes - . - LastWriteTime, - Length - = - sftpFileAttributes - . - Size - }; - if (sftpFileAttributes.IsDirectory) - { - fileInformation.Attributes - |= - FileAttributes. - Directory; - fileInformation.Length = 0; - } - else - { - fileInformation.Attributes |= FileAttributes.Normal; - } - if (file.Key[0] == '.') - { - fileInformation.Attributes - |= - FileAttributes. - Hidden; - } - - if ( - !UserCanWrite( - sftpFileAttributes)) - { - fileInformation.Attributes - |= - FileAttributes. - ReadOnly; - } - if (_useOfflineAttribute) - { - fileInformation.Attributes - |= - FileAttributes. - Offline; - } - return fileInformation; - })); - - - - int timeout = Math.Max(_attributeCacheTimeout + 2, _attributeCacheTimeout + sftpFiles.Length / 10); - - foreach ( - var file in - sftpFiles.Where( - pair => !pair.Value.IsSymbolicLink)) - { - _cache.Set(GetUnixPath(String.Format("{0}{1}", fileName, file.Key)), file.Value, - DateTimeOffset.UtcNow.AddSeconds(timeout)); - } - } - - - _sftpSession.RequestClose(handle); - - - _cache.Add(fileName, new Tuple>( - (info.Context as SftpContext).Attributes.LastWriteTime, - files), - DateTimeOffset.UtcNow.AddSeconds(Math.Max(_attributeCacheTimeout, - Math.Min(files.Count, _directoryCacheTimeout)))); - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.SetFileAttributes(string fileName, FileAttributes attributes, DokanFileInfo info) - { - Log("TrySetAttributes:{0}\n{1};", fileName, attributes); - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.SetFileTime(string filename, DateTime? creationTime, DateTime? lastAccessTime, - DateTime? lastWriteTime, DokanFileInfo info) - { - Log("TrySetFileTime:{0}\n|c:{1}\n|a:{2}\n|w:{3}", filename, creationTime, lastAccessTime, - lastWriteTime); - var sftpattributes = (info.Context as SftpContext).Attributes; - - var mtime = lastWriteTime ?? (creationTime ?? sftpattributes.LastWriteTime); - - var atime = lastAccessTime ?? sftpattributes.LastAccessTime; - - _sftpSession.RequestSetStat(GetUnixPath(filename), new SftpFileAttributes(atime, mtime, -1, -1, -1, 0, null)); - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) - { - Log("DeleteFile:{0}", fileName); - - string parentPath = GetUnixPath(fileName.Substring(0, fileName.LastIndexOf('\\'))); - - var sftpFileAttributes = _cache.Get(parentPath) as SftpFileAttributes; - - if (sftpFileAttributes == null) - { - sftpFileAttributes = GetAttributes(parentPath); - if (sftpFileAttributes != null) - _cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); - } - - - return - UserCanWrite( - sftpFileAttributes) - ? DokanError.ErrorSuccess - : DokanError.ErrorAccessDenied; - } - - DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) - { - Log("DeleteDirectory:{0}", fileName); - - - string parentPath = GetUnixPath(fileName.Substring(0, fileName.LastIndexOf('\\'))); - - var sftpFileAttributes = _cache.Get(parentPath) as SftpFileAttributes; - - if (sftpFileAttributes == null) - { - sftpFileAttributes = GetAttributes(parentPath); - if (sftpFileAttributes != null) - _cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); - } - - - if ( - !UserCanWrite( - sftpFileAttributes)) - { - return DokanError.ErrorAccessDenied; - } - var dircache = _cache.Get(fileName) as Tuple>; - if (dircache != null) - { - Log("DelateCacheHit:{0}", fileName); - - - return dircache.Item2.Count == 0 || dircache.Item2.All(i => i.FileName == "." || i.FileName == "..") - ? DokanError.ErrorSuccess - : DokanError.ErrorDirNotEmpty; - } - - var handle = _sftpSession.RequestOpenDir(GetUnixPath(fileName), true); - - if (handle == null) - return DokanError.ErrorAccessDenied; - - var dir = _sftpSession.RequestReadDir(handle); - _sftpSession.RequestClose(handle); - // usualy there are two entries . and .. - - return dir.Length == 0 || dir.All(i => i.Key == "." || i.Key == "..") - ? DokanError.ErrorSuccess - : DokanError.ErrorDirNotEmpty; - } - - DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replace, DokanFileInfo info) - { - Log("MoveFile |Name:{0} ,NewName:{3},Reaplace{4},IsDirectory:{1} ,Context:{2}", - oldName, info.IsDirectory, - info.Context, newName, replace); - string oldpath = GetUnixPath(oldName); - /* if (_generalSftpSession.RequestLStat(oldpath, true) == null) - return DokanError.ErrorPathNotFound; - if (oldName.Equals(newName)) - return DokanError.ErrorSuccess;*/ - string newpath = GetUnixPath(newName); - - if (_sftpSession.RequestLStat(newpath, true) == null) - { - (info.Context as SftpContext).Release(); - - info.Context = null; - try - { - _sftpSession.RequestRename(oldpath, newpath); - InvalidateParentCache(oldName); - InvalidateParentCache(newName); - _cache.Remove(oldpath); - } - catch (SftpPermissionDeniedException) - { - return DokanError.ErrorAccessDenied; - } - - return DokanError.ErrorSuccess; - } - else if (replace) - { - (info.Context as SftpContext).Release(); - - info.Context = null; - - - try - { - if (_supportsPosixRename) - { - _sftpSession.RequestPosixRename(oldpath, newpath); - } - else - { - if (!info.IsDirectory) - _sftpSession.RequestRemove(newpath); - _sftpSession.RequestRename(oldpath, newpath); - } - - - InvalidateParentCache(oldName); - InvalidateParentCache(newName); - _cache.Remove(oldpath); - } - catch (SftpPermissionDeniedException) - { - return DokanError.ErrorAccessDenied; - } // not tested on sftp3 - return DokanError.ErrorSuccess; - } - return DokanError.ErrorAlreadyExists; - } - - DokanError IDokanOperations.SetEndOfFile(string fileName, long length, DokanFileInfo info) - { - Log("SetEnd"); - (info.Context as SftpContext).Stream.SetLength(length); - InvalidateParentCache(fileName); - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.SetAllocationSize(string fileName, long length, DokanFileInfo info) - { - Log("SetSize"); - (info.Context as SftpContext).Stream.SetLength(length); - InvalidateParentCache(fileName); - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.LockFile(string fileName, long offset, long length, DokanFileInfo info) - { - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.UnlockFile(string fileName, long offset, long length, DokanFileInfo info) - { - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, - out long used, DokanFileInfo info) - { - - - Log("GetDiskFreeSpace"); - - var diskSpaceInfo = _cache.Get(_volumeLabel) as Tuple; - - if (diskSpaceInfo != null) - { - free = diskSpaceInfo.Item1; - total = diskSpaceInfo.Item2; - used = diskSpaceInfo.Item3; - } - else - { - if (_supportsStatVfs) - { - var information = _sftpSession.RequestStatVfs(_rootpath, true); - total = (long) (information.TotalBlocks*information.BlockSize); - free = (long) (information.FreeBlocks*information.BlockSize); - used = (long) (information.AvailableBlocks*information.BlockSize); - } - else - using (var cmd = new SshCommand(Session, String.Format(" df -Pk {0}", _rootpath))) - // POSIX standard df - { - cmd.Execute(); - if (cmd.ExitStatus == 0) - { - var values = cmd.Result.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); - - total = Int64.Parse(values[values.Length - 5]) << 10; - used = Int64.Parse(values[values.Length - 4]) << 10; - free = Int64.Parse(values[values.Length - 3]) << 10; //<======maybe to cache all this - } - else - { - total = 0x1900000000; //100 GiB - used = 0xc80000000; // 50 Gib - free = 0xc80000000; - } - } - - _cache.Add(_volumeLabel, new Tuple(free, total, used), - DateTimeOffset.UtcNow.AddMinutes(3)); - } - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features, - out string filesystemName, DokanFileInfo info) - { - Log("GetVolumeInformation"); - - volumeLabel = _volumeLabel; - - filesystemName = "SSHFS"; - - features = FileSystemFeatures.CasePreservedNames | FileSystemFeatures.CaseSensitiveSearch | - FileSystemFeatures.SupportsRemoteStorage | FileSystemFeatures.UnicodeOnDisk; - //FileSystemFeatures.PersistentAcls - - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.GetFileSecurity(string filename, out FileSystemSecurity security, - AccessControlSections sections, DokanFileInfo info) - { - Log("GetSecurrityInfo:{0}:{1}", filename, sections); - - - var sftpattributes = (info.Context as SftpContext).Attributes; - var rights = FileSystemRights.ReadPermissions | FileSystemRights.ReadExtendedAttributes | - FileSystemRights.ReadAttributes | FileSystemRights.Synchronize; - - - if (UserCanRead(sftpattributes)) - { - rights |= FileSystemRights.ReadData; - } - if (UserCanWrite(sftpattributes)) - { - rights |= FileSystemRights.Write; - } - if (UserCanExecute(sftpattributes) && info.IsDirectory) - { - rights |= FileSystemRights.Traverse; - } - security = info.IsDirectory ? new DirectorySecurity() as FileSystemSecurity : new FileSecurity(); - // if(sections.HasFlag(AccessControlSections.Access)) - security.AddAccessRule(new FileSystemAccessRule("Everyone", rights, AccessControlType.Allow)); - security.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl ^ rights, - AccessControlType.Deny)); - //not sure this works at all, needs testing - // if (sections.HasFlag(AccessControlSections.Owner)) - security.SetOwner(new NTAccount("None")); - // if (sections.HasFlag(AccessControlSections.Group)) - security.SetGroup(new NTAccount("None")); - - return DokanError.ErrorSuccess; - } - - DokanError IDokanOperations.SetFileSecurity(string filename, FileSystemSecurity security, - AccessControlSections sections, DokanFileInfo info) - { - Log("TrySetSecurity:{0}", filename); - - return DokanError.ErrorAccessDenied; - } - - DokanError IDokanOperations.Unmount(DokanFileInfo info) - { - Log("UNMOUNT"); - - // Disconnect(); - return DokanError.ErrorSuccess; - } - - #endregion - - #region Events - - public event EventHandler Disconnected - { - add { Session.Disconnected += value; } - remove { Session.Disconnected -= value; } - } - - #endregion - } +// Copyright (c) 2012 Dragan Mladjenovic +// 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. + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.Caching; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Text; +using DokanNet; +using Renci.SshNet; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; +using FileAccess = DokanNet.FileAccess; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("WinSshFS, PublicKey=0024000004800000940000000602000000240000525341310004000001000100bb1df492d3d63bb4aedb73672b0c0694ad7838ea17c6d49685ef1a03301ae6de5a4b4b1795844de0ddab49254d05bd533b5a02c24a580346274b204b8975536ffe36b4e7bb8c82e419264c335682ad44861e99eef16e95ee978742064c9335283ecb6e2fd325ea97942a51a3fc1a7d9948dd919b0d4ad25148d5490cc37f85b4")] + +namespace Sshfs +{ + internal sealed class SftpFilesystem : BaseClient, IDokanOperations + { + + #region Constants + + // ReSharper disable InconsistentNaming + // private static readonly string[] _filter = { + // "desktop.ini", "Desktop.ini", "autorun.inf", + // "AutoRun.inf", //"Thumbs.db", + // }; + + // private static readonly Regex _dfregex = new Regex(@"^[a-z0-9/]+\s+(?[0-9]+)K\s+(?[0-9]+)K" + // , RegexOptions.Compiled); + + // ReSharper restore InconsistentNaming + + #endregion + + #region Fields + + private readonly MemoryCache _cache = MemoryCache.Default; + + private SftpSession _sftpSession; + private readonly TimeSpan _operationTimeout = TimeSpan.FromSeconds(30);//new TimeSpan(0, 0, 0, 0, -1); + private string _rootpath; + + private readonly bool _useOfflineAttribute; + private readonly bool _debugMode; + + + private int _userId; + private HashSet _userGroups; + + private readonly int _attributeCacheTimeout; + private readonly int _directoryCacheTimeout; + + private bool _supportsPosixRename; + private bool _supportsStatVfs; + + private readonly string _volumeLabel; + + #endregion + + #region Constructors + + public SftpFilesystem(ConnectionInfo connectionInfo, string rootpath, string label = null, + bool useOfflineAttribute = false, + bool debugMode = false, int attributeCacheTimeout = 5, int directoryCacheTimeout = 60) + : base(connectionInfo, true) + { + _rootpath = rootpath; + _directoryCacheTimeout = directoryCacheTimeout; + _attributeCacheTimeout = attributeCacheTimeout; + _useOfflineAttribute = useOfflineAttribute; + _debugMode = debugMode; + _volumeLabel = label ?? String.Format("{0} on '{1}'", ConnectionInfo.Username, ConnectionInfo.Host); + } + + #endregion + + #region Method overrides + + protected override void OnConnected() + { + base.OnConnected(); + + _sftpSession = new SftpSession(Session, _operationTimeout, Encoding.UTF8); + + + _sftpSession.Connect(); + + + _userId = GetUserId(); + if (_userId != -1) + _userGroups = new HashSet(GetUserGroupsIds()); + + + if (String.IsNullOrWhiteSpace(_rootpath)) + { + _rootpath = _sftpSession.RequestRealPath(".").First().Key; + } + + _supportsPosixRename = + _sftpSession._supportedExtensions.Contains(new KeyValuePair("posix-rename@openssh.com", "1")); + _supportsStatVfs = + _sftpSession._supportedExtensions.Contains(new KeyValuePair("statvfs@openssh.com", "2")); + // KeepAliveInterval=TimeSpan.FromSeconds(5); + + // Session.Disconnected+= (sender, args) => Debugger.Break(); + } + + + protected override void Dispose(bool disposing) + { + if (_sftpSession != null) + { + _sftpSession.Dispose(); + _sftpSession = null; + } + base.Dispose(disposing); + } + + #endregion + + #region Methods + + private string GetUnixPath(string path) + { + // return String.Concat(_rootpath, path.Replace('\\', '/')); + return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/')); + } + + + [Conditional("DEBUG")] + private void Log(string format, params object[] arg) + { + if (_debugMode) + { + Console.WriteLine(format, arg); + } + Debug.Write(DateTime.Now.ToLongTimeString() + " "); + Debug.WriteLine(format, arg); + } + + private IEnumerable GetUserGroupsIds() + { + using (var cmd = new SshCommand(Session, "id -G ")) + { + cmd.Execute(); + return cmd.Result.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries).Select(Int32.Parse); + } + } + + private int GetUserId() + { + using (var cmd = new SshCommand(Session, "id -u ")) + // Thease commands seems to be POSIX so the only problem would be Windows enviroment + { + cmd.Execute(); + return cmd.ExitStatus == 0 ? Int32.Parse(cmd.Result) : -1; + } + } + + private bool UserCanRead(SftpFileAttributes attributes) + { + return _userId == -1 || (attributes.OwnerCanRead && attributes.UserId == _userId || + (attributes.GroupCanRead && _userGroups.Contains(attributes.GroupId) || + attributes.OthersCanRead)); + } + + private bool UserCanWrite(SftpFileAttributes attributes) + { + return _userId == -1 || (attributes.OwnerCanWrite && attributes.UserId == _userId || + (attributes.GroupCanWrite && _userGroups.Contains(attributes.GroupId) || + attributes.OthersCanWrite)); + } + + private bool UserCanExecute(SftpFileAttributes attributes) + { + return _userId == -1 || (attributes.OwnerCanExecute && attributes.UserId == _userId || + (attributes.GroupCanExecute && _userGroups.Contains(attributes.GroupId) || + attributes.OthersCanExecute)); + } + + private SftpFileAttributes GetAttributes(string path) + { + var sftpLStatAttributes = _sftpSession.RequestLStat(path, true); + if (sftpLStatAttributes == null || !sftpLStatAttributes.IsSymbolicLink) + { + return sftpLStatAttributes; + } + var sftpStatAttributes = _sftpSession.RequestStat(path, true); + return sftpStatAttributes ?? sftpLStatAttributes; + } + + private void InvalidateParentCache(string fileName) + { + int index = fileName.LastIndexOf('\\'); + _cache.Remove(index != 0 ? fileName.Substring(0, index) : "\\"); + } + + #endregion + + #region DokanOperations + + DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, + FileMode mode, FileOptions options, + FileAttributes attributes, DokanFileInfo info) + { + if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || + fileName.EndsWith("autorun.inf", StringComparison.OrdinalIgnoreCase)) //.... + { + return DokanError.ErrorFileNotFound; + } + + string path = GetUnixPath(fileName); + // var sftpFileAttributes = GetAttributes(path); + var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + + if (sftpFileAttributes == null) + { + Log("cache miss"); + + sftpFileAttributes = GetAttributes(path); + if (sftpFileAttributes != null) + _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + } + + + + + Log("Open| Name:{0},\n Mode:{1},\n Share{2},\n Disp:{3},\n Flags{4},\n Attr:{5}\n", fileName, access, + share, mode, options, attributes); + + switch (mode) + { + case FileMode.Open: + if (sftpFileAttributes != null) + { + if (((uint) access & 0xe0000027) == 0 || sftpFileAttributes.IsDirectory) + //check if only wants to read attributes,security info or open directory + { + Log("JustInfo:{0},{1}", fileName, sftpFileAttributes.IsDirectory); + info.IsDirectory = sftpFileAttributes.IsDirectory; + info.Context = new SftpContext(sftpFileAttributes); + return DokanError.ErrorSuccess; + } + } + else return DokanError.ErrorFileNotFound; + break; + case FileMode.CreateNew: + if (sftpFileAttributes != null) + return DokanError.ErrorAlreadyExists; + + InvalidateParentCache(fileName); // cache invalidate + break; + case FileMode.Truncate: + if (sftpFileAttributes == null) + return DokanError.ErrorFileNotFound; + InvalidateParentCache(fileName); + _cache.Remove(path); + break; + default: + + InvalidateParentCache(fileName); + break; + } + Log("NotJustInfo:{0}-{1}", info.Context, mode); + try + { + info.Context = new SftpContext(_sftpSession, path, mode, + ((ulong) access & 0x40010006) == 0 + ? System.IO.FileAccess.Read + : System.IO.FileAccess.ReadWrite, sftpFileAttributes); + } + catch (SshException) // Don't have access rights or try to read broken symlink + { + var ownerpath = path.Substring(0, path.LastIndexOf('/')); + var sftpPathAttributes = _cache.Get(ownerpath) as SftpFileAttributes; + + if (sftpPathAttributes == null) + { + Log("cache miss"); + + sftpPathAttributes = GetAttributes(ownerpath); + if (sftpPathAttributes != null) + _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + else + { + Log("Up directory must be created"); + return DokanError.ErrorPathNotFound; + } + } + return DokanError.ErrorAccessDenied; + } + + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) + { + Log("OpenDir:{0}", fileName); + + + + + string path = GetUnixPath(fileName); + // var sftpFileAttributes = GetAttributes(GetUnixPath(fileName)); + var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + + if (sftpFileAttributes == null) + { + Log("cache miss"); + + sftpFileAttributes = GetAttributes(path); + if (sftpFileAttributes != null) + _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + } + + + + + if (sftpFileAttributes != null && sftpFileAttributes.IsDirectory) + { + //??? + if (!UserCanExecute(sftpFileAttributes) || !UserCanRead(sftpFileAttributes)) + { + return DokanError.ErrorAccessDenied; + } + + + info.IsDirectory = true; + info.Context = new SftpContext(sftpFileAttributes); + + var dircahe = _cache.Get(fileName) as Tuple>; + if (dircahe != null && dircahe.Item1 != sftpFileAttributes.LastWriteTime) + { + _cache.Remove(fileName); + } + return DokanError.ErrorSuccess; + } + return DokanError.ErrorPathNotFound; + } + + DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) + { + Log("CreateDir:{0}", fileName); + + + try + { + _sftpSession.RequestMkDir(GetUnixPath(fileName)); + InvalidateParentCache(fileName); //invalidate dircahe of the parent + } + catch (SftpPermissionDeniedException) + { + return DokanError.ErrorAccessDenied; + } + catch (SshException) // operation should fail with generic error if file already exists + { + return DokanError.ErrorAlreadyExists; + } + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) + { + Log("Cleanup:{0},Delete:{1}", info.Context,info.DeleteOnClose); + + if (info.Context != null) + { + (info.Context as SftpContext).Release(); + + info.Context = null; + } + + if (info.DeleteOnClose) + { + string path = GetUnixPath(fileName); + if (info.IsDirectory) + { + try + { + _sftpSession.RequestRmDir(path); + } + catch (SftpPathNotFoundException) //in case we are dealing with simbolic link + { + _sftpSession.RequestRemove(path); + } + } + else + { + _sftpSession.RequestRemove(path); + } + InvalidateParentCache(fileName); + _cache.Remove(path); + } + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) + { + Log("Close:{0}", info.Context); + + return DokanError.ErrorSuccess; + } + + + DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, + DokanFileInfo info) + { + Log("ReadFile:{0}:{1}|lenght:[{2}]|offset:[{3}]", fileName, + info.Context , buffer.Length, offset); + + if (info.Context == null) + { + //called when file is read as memory memory mapeded file usualy notepad and stuff + var handle = _sftpSession.RequestOpen(GetUnixPath(fileName), Flags.Read); + var data = _sftpSession.RequestRead(handle, (ulong) offset, (uint) buffer.Length); + _sftpSession.RequestClose(handle); + Buffer.BlockCopy(data, 0, buffer, 0, data.Length); + bytesRead = data.Length; + } + else + { + // var watch = Stopwatch.StartNew(); + var stream = (info.Context as SftpContext).Stream; + lock (stream) + { + stream.Position = offset; + bytesRead = stream.Read(buffer, 0, buffer.Length); + } + // watch.Stop(); + // Log("{0}",watch.ElapsedMilliseconds); + } + Log("END READ:{0},{1}",offset,info.Context); + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, + DokanFileInfo info) + { + + + Log("WriteFile:{0}:{1}|lenght:[{2}]|offset:[{3}]", fileName, + info.Context, buffer.Length, offset); + + + if (info.Context == null) // who would guess + { + var handle = _sftpSession.RequestOpen(GetUnixPath(fileName), Flags.Write); + // using (var wait = new AutoResetEvent(false)) + { + _sftpSession.RequestWrite(handle, (ulong) offset, buffer, null,null/*, wait*/); + } + _sftpSession.RequestClose(handle); + bytesWritten = buffer.Length; + } + else + { + var stream = (info.Context as SftpContext).Stream; + int written = 0; + int chunk = 16384;//maximum is payload 32768-X, optimum? 16384=1.5, 8192=1.9, 4092=2.3, 2048=2.3(5.8), 1024=2.35 + lock (stream) + { + while (written < buffer.Length) + { + int writecount = buffer.Length - written; + if (writecount > chunk) writecount = chunk; + stream.Position = offset + written; + stream.Write(buffer, written, writecount); + written += writecount; + } + + //stream.Position = offset; + //stream.Write(buffer, 0, buffer.Length); + + + } + // stream.Flush(); + bytesWritten = buffer.Length; + // TODO there are still some apps that don't check disk free space before write + } + + // Log("END WRITE:{0},{1},{2}", offset,info.Context,watch.ElapsedMilliseconds); + return DokanError.ErrorSuccess; + } + + + DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) + { + Log("FLUSH:{0}", fileName); + + (info.Context as SftpContext).Stream.Flush(); //I newer saw it get called ,but .. + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, + DokanFileInfo info) + { + Log("GetInfo:{0}:{1}", fileName,info.Context); + + var context = info.Context as SftpContext; + + SftpFileAttributes sftpFileAttributes; + if (context != null) + { + sftpFileAttributes = context.Attributes; + } + else + { + string path = GetUnixPath(fileName); + sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + + if (sftpFileAttributes == null) + { + sftpFileAttributes = GetAttributes(path); + if (sftpFileAttributes != null) + _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + } + } + + + fileInfo = new FileInformation + { + Attributes = + FileAttributes.NotContentIndexed, + FileName = String.Empty, + // GetInfo info doesn't use it maybe for sorting . + CreationTime = sftpFileAttributes.LastWriteTime, + LastAccessTime = sftpFileAttributes.LastAccessTime, + LastWriteTime = sftpFileAttributes.LastWriteTime, + Length = sftpFileAttributes.Size + }; + if (sftpFileAttributes.IsDirectory) + { + fileInfo.Attributes |= FileAttributes.Directory; + fileInfo.Length = 0; // Windows directories use length of 0 + } + else + { + fileInfo.Attributes |= FileAttributes.Normal; + } + if (fileName.Length != 1 && fileName[fileName.LastIndexOf('\\') + 1] == '.') + //aditional check if filename isn't \\ + { + fileInfo.Attributes |= FileAttributes.Hidden; + } + + if (!UserCanWrite(sftpFileAttributes)) + { + fileInfo.Attributes |= FileAttributes.ReadOnly; + } + if (_useOfflineAttribute) + { + fileInfo.Attributes |= FileAttributes.Offline; + } + // Console.WriteLine(sftpattributes.UserId + "|" + sftpattributes.GroupId + "L" + + // sftpattributes.OthersCanExecute + "K" + sftpattributes.OwnerCanExecute); + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) + { + Log("FindFiles:{0}", fileName); + + var dircache = _cache.Get(fileName) as Tuple>; + if (dircache != null) + { + files = (dircache).Item2; + Log("CacheHit:{0}", fileName); + return DokanError.ErrorSuccess; + } + + + byte[] handle; + try + { + handle = _sftpSession.RequestOpenDir(GetUnixPath(fileName)); + } + catch (SftpPermissionDeniedException) + { + files = null; + return DokanError.ErrorAccessDenied; + } + + + files = new List(); + for (var sftpFiles = _sftpSession.RequestReadDir(handle); + sftpFiles != null; + sftpFiles = _sftpSession.RequestReadDir(handle)) + { + + + + + (files as List).AddRange(sftpFiles.Select( + file => + { + var sftpFileAttributes = file.Value; + if (sftpFileAttributes.IsSymbolicLink) + { + sftpFileAttributes = _sftpSession.RequestStat( + GetUnixPath(String.Format("{0}{1}", fileName, file.Key)), true) ?? + file.Value; + } + + + var fileInformation = new FileInformation + { + Attributes = + FileAttributes.NotContentIndexed, + CreationTime + = + sftpFileAttributes + . + LastWriteTime, + FileName + = + file.Key + , + LastAccessTime + = + sftpFileAttributes + . + LastAccessTime, + LastWriteTime + = + sftpFileAttributes + . + LastWriteTime, + Length + = + sftpFileAttributes + . + Size + }; + if (sftpFileAttributes.IsDirectory) + { + fileInformation.Attributes + |= + FileAttributes. + Directory; + fileInformation.Length = 0; + } + else + { + fileInformation.Attributes |= FileAttributes.Normal; + } + if (file.Key[0] == '.') + { + fileInformation.Attributes + |= + FileAttributes. + Hidden; + } + + if ( + !UserCanWrite( + sftpFileAttributes)) + { + fileInformation.Attributes + |= + FileAttributes. + ReadOnly; + } + if (_useOfflineAttribute) + { + fileInformation.Attributes + |= + FileAttributes. + Offline; + } + return fileInformation; + })); + + + + int timeout = Math.Max(_attributeCacheTimeout + 2, _attributeCacheTimeout + sftpFiles.Length / 10); + + foreach ( + var file in + sftpFiles.Where( + pair => !pair.Value.IsSymbolicLink)) + { + _cache.Set(GetUnixPath(String.Format("{0}{1}", fileName, file.Key)), file.Value, + DateTimeOffset.UtcNow.AddSeconds(timeout)); + } + } + + + _sftpSession.RequestClose(handle); + + + _cache.Add(fileName, new Tuple>( + (info.Context as SftpContext).Attributes.LastWriteTime, + files), + DateTimeOffset.UtcNow.AddSeconds(Math.Max(_attributeCacheTimeout, + Math.Min(files.Count, _directoryCacheTimeout)))); + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.SetFileAttributes(string fileName, FileAttributes attributes, DokanFileInfo info) + { + Log("TrySetAttributes:{0}\n{1};", fileName, attributes); + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.SetFileTime(string filename, DateTime? creationTime, DateTime? lastAccessTime, + DateTime? lastWriteTime, DokanFileInfo info) + { + Log("TrySetFileTime:{0}\n|c:{1}\n|a:{2}\n|w:{3}", filename, creationTime, lastAccessTime, + lastWriteTime); + var sftpattributes = (info.Context as SftpContext).Attributes; + + var mtime = lastWriteTime ?? (creationTime ?? sftpattributes.LastWriteTime); + + var atime = lastAccessTime ?? sftpattributes.LastAccessTime; + + _sftpSession.RequestSetStat(GetUnixPath(filename), new SftpFileAttributes(atime, mtime, -1, -1, -1, 0, null)); + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) + { + Log("DeleteFile:{0}", fileName); + + string parentPath = GetUnixPath(fileName.Substring(0, fileName.LastIndexOf('\\'))); + + var sftpFileAttributes = _cache.Get(parentPath) as SftpFileAttributes; + + if (sftpFileAttributes == null) + { + sftpFileAttributes = GetAttributes(parentPath); + if (sftpFileAttributes != null) + _cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + } + + + return + UserCanWrite( + sftpFileAttributes) + ? DokanError.ErrorSuccess + : DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) + { + Log("DeleteDirectory:{0}", fileName); + + + string parentPath = GetUnixPath(fileName.Substring(0, fileName.LastIndexOf('\\'))); + + var sftpFileAttributes = _cache.Get(parentPath) as SftpFileAttributes; + + if (sftpFileAttributes == null) + { + sftpFileAttributes = GetAttributes(parentPath); + if (sftpFileAttributes != null) + _cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + } + + + if ( + !UserCanWrite( + sftpFileAttributes)) + { + return DokanError.ErrorAccessDenied; + } + var dircache = _cache.Get(fileName) as Tuple>; + if (dircache != null) + { + Log("DelateCacheHit:{0}", fileName); + + + return dircache.Item2.Count == 0 || dircache.Item2.All(i => i.FileName == "." || i.FileName == "..") + ? DokanError.ErrorSuccess + : DokanError.ErrorDirNotEmpty; + } + + var handle = _sftpSession.RequestOpenDir(GetUnixPath(fileName), true); + + if (handle == null) + return DokanError.ErrorAccessDenied; + + var dir = _sftpSession.RequestReadDir(handle); + _sftpSession.RequestClose(handle); + // usualy there are two entries . and .. + + return dir.Length == 0 || dir.All(i => i.Key == "." || i.Key == "..") + ? DokanError.ErrorSuccess + : DokanError.ErrorDirNotEmpty; + } + + DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replace, DokanFileInfo info) + { + Log("MoveFile |Name:{0} ,NewName:{3},Reaplace{4},IsDirectory:{1} ,Context:{2}", + oldName, info.IsDirectory, + info.Context, newName, replace); + string oldpath = GetUnixPath(oldName); + /* if (_generalSftpSession.RequestLStat(oldpath, true) == null) + return DokanError.ErrorPathNotFound; + if (oldName.Equals(newName)) + return DokanError.ErrorSuccess;*/ + string newpath = GetUnixPath(newName); + + if (_sftpSession.RequestLStat(newpath, true) == null) + { + (info.Context as SftpContext).Release(); + + info.Context = null; + try + { + _sftpSession.RequestRename(oldpath, newpath); + InvalidateParentCache(oldName); + InvalidateParentCache(newName); + _cache.Remove(oldpath); + } + catch (SftpPermissionDeniedException) + { + return DokanError.ErrorAccessDenied; + } + + return DokanError.ErrorSuccess; + } + else if (replace) + { + (info.Context as SftpContext).Release(); + + info.Context = null; + + + try + { + if (_supportsPosixRename) + { + _sftpSession.RequestPosixRename(oldpath, newpath); + } + else + { + if (!info.IsDirectory) + _sftpSession.RequestRemove(newpath); + _sftpSession.RequestRename(oldpath, newpath); + } + + + InvalidateParentCache(oldName); + InvalidateParentCache(newName); + _cache.Remove(oldpath); + } + catch (SftpPermissionDeniedException) + { + return DokanError.ErrorAccessDenied; + } // not tested on sftp3 + return DokanError.ErrorSuccess; + } + return DokanError.ErrorAlreadyExists; + } + + DokanError IDokanOperations.SetEndOfFile(string fileName, long length, DokanFileInfo info) + { + Log("SetEnd"); + (info.Context as SftpContext).Stream.SetLength(length); + InvalidateParentCache(fileName); + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.SetAllocationSize(string fileName, long length, DokanFileInfo info) + { + Log("SetSize"); + (info.Context as SftpContext).Stream.SetLength(length); + InvalidateParentCache(fileName); + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.LockFile(string fileName, long offset, long length, DokanFileInfo info) + { + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.UnlockFile(string fileName, long offset, long length, DokanFileInfo info) + { + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, + out long used, DokanFileInfo info) + { + + + Log("GetDiskFreeSpace"); + + var diskSpaceInfo = _cache.Get(_volumeLabel) as Tuple; + + if (diskSpaceInfo != null) + { + free = diskSpaceInfo.Item1; + total = diskSpaceInfo.Item2; + used = diskSpaceInfo.Item3; + } + else + { + if (_supportsStatVfs) + { + var information = _sftpSession.RequestStatVfs(_rootpath, true); + total = (long) (information.TotalBlocks*information.BlockSize); + free = (long) (information.FreeBlocks*information.BlockSize); + used = (long) (information.AvailableBlocks*information.BlockSize); + } + else + using (var cmd = new SshCommand(Session, String.Format(" df -Pk {0}", _rootpath))) + // POSIX standard df + { + cmd.Execute(); + if (cmd.ExitStatus == 0) + { + var values = cmd.Result.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); + + total = Int64.Parse(values[values.Length - 5]) << 10; + used = Int64.Parse(values[values.Length - 4]) << 10; + free = Int64.Parse(values[values.Length - 3]) << 10; //<======maybe to cache all this + } + else + { + total = 0x1900000000; //100 GiB + used = 0xc80000000; // 50 Gib + free = 0xc80000000; + } + } + + _cache.Add(_volumeLabel, new Tuple(free, total, used), + DateTimeOffset.UtcNow.AddMinutes(3)); + } + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features, + out string filesystemName, DokanFileInfo info) + { + Log("GetVolumeInformation"); + + volumeLabel = _volumeLabel; + + filesystemName = "SSHFS"; + + features = FileSystemFeatures.CasePreservedNames | FileSystemFeatures.CaseSensitiveSearch | + FileSystemFeatures.SupportsRemoteStorage | FileSystemFeatures.UnicodeOnDisk; + //FileSystemFeatures.PersistentAcls + + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.GetFileSecurity(string filename, out FileSystemSecurity security, + AccessControlSections sections, DokanFileInfo info) + { + Log("GetSecurrityInfo:{0}:{1}", filename, sections); + + + var sftpattributes = (info.Context as SftpContext).Attributes; + var rights = FileSystemRights.ReadPermissions | FileSystemRights.ReadExtendedAttributes | + FileSystemRights.ReadAttributes | FileSystemRights.Synchronize; + + + if (UserCanRead(sftpattributes)) + { + rights |= FileSystemRights.ReadData; + } + if (UserCanWrite(sftpattributes)) + { + rights |= FileSystemRights.Write; + } + if (UserCanExecute(sftpattributes) && info.IsDirectory) + { + rights |= FileSystemRights.Traverse; + } + security = info.IsDirectory ? new DirectorySecurity() as FileSystemSecurity : new FileSecurity(); + // if(sections.HasFlag(AccessControlSections.Access)) + security.AddAccessRule(new FileSystemAccessRule("Everyone", rights, AccessControlType.Allow)); + security.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl ^ rights, + AccessControlType.Deny)); + //not sure this works at all, needs testing + // if (sections.HasFlag(AccessControlSections.Owner)) + security.SetOwner(new NTAccount("None")); + // if (sections.HasFlag(AccessControlSections.Group)) + security.SetGroup(new NTAccount("None")); + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.SetFileSecurity(string filename, FileSystemSecurity security, + AccessControlSections sections, DokanFileInfo info) + { + Log("TrySetSecurity:{0}", filename); + + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.Unmount(DokanFileInfo info) + { + Log("UNMOUNT"); + + // Disconnect(); + return DokanError.ErrorSuccess; + } + + #endregion + + #region Events + + public event EventHandler Disconnected + { + add { Session.Disconnected += value; } + remove { Session.Disconnected -= value; } + } + + #endregion + } } \ No newline at end of file diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index be95620..cdb71ad 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -1,236 +1,236 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {FF4FC8BB-91A3-45FF-8449-650647610394} - WinExe - Properties - Sshfs - WinSshFS - v4.5 - - - 512 - true - X:\public\winsshfs\ - true - Web - true - Foreground - 7 - Days - false - false - true - http://dev.4e1.cz/winsshfs/ - http://dev.4e1.cz/winsshfs/ - cs - WinSshFS 4every1 edition - 4every1 s.r.o. - WinSshFS - true - index.html - false - 6 - 0.1.5.6 - true - true - true - true - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - x86 - pdbonly - true - bin\Release\ - - - prompt - 4 - false - - - app.ico - - - Sshfs.Program - - - BDCAC9E8E97F1DD52CA96613318E8D477A12F966 - - - sshfs_4every1.pfx - - - true - - - true - - - app.manifest - - - true - - - sshfs_4every1.pfx - - - LocalIntranet - - - - - - - - - - - - - - - Form - - - AboutForm.cs - - - - - - - Form - - - MainForm.cs - - - - - - - - - - AboutForm.cs - - - MainForm.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - - PublicSettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C} - DokanNet - - - {2F5F8C90-0BD1-424F-997C-7BC6280919D1} - Renci.SshNet - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - False - Windows Installer 4.5 - true - - - + + + + Debug + x86 + 8.0.30703 + 2.0 + {FF4FC8BB-91A3-45FF-8449-650647610394} + WinExe + Properties + Sshfs + WinSshFS + v4.5 + + + 512 + true + X:\public\winsshfs\ + true + Web + true + Foreground + 7 + Days + false + false + true + http://dev.4e1.cz/winsshfs/ + http://dev.4e1.cz/winsshfs/ + cs + WinSshFS 4every1 edition + 4every1 s.r.o. + WinSshFS + true + index.html + false + 8 + 0.1.5.8 + true + true + true + true + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\Release\ + + + prompt + 4 + false + + + app.ico + + + Sshfs.Program + + + BDCAC9E8E97F1DD52CA96613318E8D477A12F966 + + + sshfs_4every1.pfx + + + true + + + true + + + app.manifest + + + true + + + sshfs_4every1.pfx + + + LocalIntranet + + + + + + + + + + + + + + + Form + + + AboutForm.cs + + + + + + + Form + + + MainForm.cs + + + + + + + + + + AboutForm.cs + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + + PublicSettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C} + DokanNet + + + {2F5F8C90-0BD1-424F-997C-7BC6280919D1} + Renci.SshNet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + --> \ No newline at end of file From 16a4b42a34c2a04375f8c911734a46256af903c5 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 22 Jul 2014 21:26:44 +0200 Subject: [PATCH 019/134] cache time 15/3 0/0 make it slow --- Sshfs/Sshfs/Properties/Settings.Designer.cs | 124 ++++++++++---------- Sshfs/Sshfs/Properties/Settings.settings | 34 +++--- Sshfs/Sshfs/app.config | 26 ++-- 3 files changed, 92 insertions(+), 92 deletions(-) diff --git a/Sshfs/Sshfs/Properties/Settings.Designer.cs b/Sshfs/Sshfs/Properties/Settings.Designer.cs index e50c687..6331881 100644 --- a/Sshfs/Sshfs/Properties/Settings.Designer.cs +++ b/Sshfs/Sshfs/Properties/Settings.Designer.cs @@ -1,62 +1,62 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34014 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Sshfs.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public uint AttributeCacheTimeout { - get { - return ((uint)(this["AttributeCacheTimeout"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public uint DirContentCacheTimeout { - get { - return ((uint)(this["DirContentCacheTimeout"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool UseOfflineAttribute { - get { - return ((bool)(this["UseOfflineAttribute"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool UseNetworkDrive { - get { - return ((bool)(this["UseNetworkDrive"])); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34014 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Sshfs.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("15")] + public uint AttributeCacheTimeout { + get { + return ((uint)(this["AttributeCacheTimeout"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("3")] + public uint DirContentCacheTimeout { + get { + return ((uint)(this["DirContentCacheTimeout"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseOfflineAttribute { + get { + return ((bool)(this["UseOfflineAttribute"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseNetworkDrive { + get { + return ((bool)(this["UseNetworkDrive"])); + } + } + } +} diff --git a/Sshfs/Sshfs/Properties/Settings.settings b/Sshfs/Sshfs/Properties/Settings.settings index 9a229f0..22993af 100644 --- a/Sshfs/Sshfs/Properties/Settings.settings +++ b/Sshfs/Sshfs/Properties/Settings.settings @@ -1,18 +1,18 @@ - - - - - - 0 - - - 0 - - - False - - - False - - + + + + + + 15 + + + 3 + + + False + + + False + + \ No newline at end of file diff --git a/Sshfs/Sshfs/app.config b/Sshfs/Sshfs/app.config index 6c7a709..3b70716 100644 --- a/Sshfs/Sshfs/app.config +++ b/Sshfs/Sshfs/app.config @@ -7,19 +7,19 @@ - - - 0 - - - 0 - - - False - - - False - + + + 15 + + + 3 + + + False + + + False + From b0b7ddd102306a507aa2197cfb2cad69d2eab0f7 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 22 Jul 2014 21:51:29 +0200 Subject: [PATCH 020/134] bug 2 more missing nullonerror --- Renci.SshNet/Sftp/SftpSession.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs index 40b0f3f..6a6da17 100644 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ b/Renci.SshNet/Sftp/SftpSession.cs @@ -430,7 +430,7 @@ internal SftpFileAttributes RequestLStat(string path, bool nullOnError = false) this.WaitOnHandle(wait, this._operationTimeout); } - if (exception != null) + if (!nullOnError && exception != null) { throw exception; } @@ -470,7 +470,7 @@ internal SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false this.WaitOnHandle(wait, this._operationTimeout); } - if (exception != null) + if (!nullOnError && exception != null) { throw exception; } From d412d5d3739c14c0164c48ba0cdf5d8c2073dffb Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 23 Jul 2014 17:09:25 +0200 Subject: [PATCH 021/134] linux compatibility root access, getinfo filename patch --- Renci.SshNet/Renci.SshNet.csproj | 6 +++--- Sshfs/Sshfs/SftpFilesystem.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Renci.SshNet/Renci.SshNet.csproj b/Renci.SshNet/Renci.SshNet.csproj index b453698..f4adc5e 100644 --- a/Renci.SshNet/Renci.SshNet.csproj +++ b/Renci.SshNet/Renci.SshNet.csproj @@ -153,9 +153,9 @@ - - - + + + Code diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index c3d450b..98e508d 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -181,21 +181,21 @@ private int GetUserId() private bool UserCanRead(SftpFileAttributes attributes) { - return _userId == -1 || (attributes.OwnerCanRead && attributes.UserId == _userId || + return _userId <= 0 || (attributes.OwnerCanRead && attributes.UserId == _userId || (attributes.GroupCanRead && _userGroups.Contains(attributes.GroupId) || attributes.OthersCanRead)); } private bool UserCanWrite(SftpFileAttributes attributes) { - return _userId == -1 || (attributes.OwnerCanWrite && attributes.UserId == _userId || + return _userId <= 0 || (attributes.OwnerCanWrite && attributes.UserId == _userId || (attributes.GroupCanWrite && _userGroups.Contains(attributes.GroupId) || attributes.OthersCanWrite)); } private bool UserCanExecute(SftpFileAttributes attributes) { - return _userId == -1 || (attributes.OwnerCanExecute && attributes.UserId == _userId || + return _userId <= 0 || (attributes.OwnerCanExecute && attributes.UserId == _userId || (attributes.GroupCanExecute && _userGroups.Contains(attributes.GroupId) || attributes.OthersCanExecute)); } @@ -547,7 +547,7 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat { Attributes = FileAttributes.NotContentIndexed, - FileName = String.Empty, + FileName = Path.GetFileName(fileName), //String.Empty, // GetInfo info doesn't use it maybe for sorting . CreationTime = sftpFileAttributes.LastWriteTime, LastAccessTime = sftpFileAttributes.LastAccessTime, From 7728cbf7d9038ed6a890638415bed26885c5dd17 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 22 Jul 2014 18:33:18 +0200 Subject: [PATCH 022/134] logigin improve, but unstable, cannot copy --- DokanNet/DokanNet.csproj | 4 +- DokanNet/DokanOperationProxy.cs | 131 ++++++++++++++++++++------------ DokanNet/FileAccess.cs | 32 ++++---- Sshfs/Sshfs/app.config | 24 +++--- 4 files changed, 112 insertions(+), 79 deletions(-) diff --git a/DokanNet/DokanNet.csproj b/DokanNet/DokanNet.csproj index 5952eda..edbaab0 100644 --- a/DokanNet/DokanNet.csproj +++ b/DokanNet/DokanNet.csproj @@ -73,7 +73,7 @@ true bin\x86\Debug\ - DEBUG;TRACE + TRACE;DEBUG;DEBUGDOKAN full x86 bin\Debug\DokanNet.dll.CodeAnalysisLog.xml @@ -156,7 +156,7 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - - - AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w - LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 - ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACu - LAAAAk1TRnQBSQFMAgEBBAEAAQwBAAEMAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg - AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A - /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ - AwcBCgMHAQoDBwEKAwgBCwMaASUCVgFXAbECQAG3Af0CgQG4Af8CgQG4Af8CgQG4Af8CgQG4Af8CgQG4 - Af8CgQG4Af8CXwFyAfMDSAGEBwABAQMDAQQDBQEHAwYBCAMGAQgDBwEJAwcBCQMHAQkDBwEKAwcBCgMH - AQoDCAELAwcBCgMHAQkDBQEHAwEBAiAAAw8BFAMPARQDEgEYAx4BKwMmATkDKAE8AygBPAMmATgDIgEx - AxgBIgMQARYDEAEVAxEBFwNIAYMCLAGkAf8CLAGkAf8CLAGkAf8CLAGkAf8CLAGkAf8CLAGkAf8CLAGk - Af8DRgF+CAADDwEUAw8BFAMSARgDHgErAyYBOQMoATwDKAE8AyYBOAMiATEDGAEiAxABFgMQARUDEQEX - AwcBCQMCAQMDCwEPAxYBHgMWAR8DCwEPAwEBAhAAA00BjwNWAa8DVwGxA1cBswNXAbMDVwGzA1cBswNX - AbMDWAG0A1YBtQNWAbUDVgG1AlsBXgHIA2oB+QKBAdMB/wKBAdMB/wKBAdMB/wKBAdMB/wKBAdMB/wKB - AdMB/wKBAdMB/wKBAdQB/wJkAXQB8QQAA00BjwNWAa8DVwGxA1cBswNXAbMDVwGzA1cBswNXAbMDWAG0 - A1YBtQNWAbUDVgG1A1YBtQNYAbQDPQFoAw4BEwMDAQQcAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AiwBpAH/AiwBzAH/AiwBzAH/AiwBzAH/AiwBzAH/AiwBzAH/AiwBzAH/ - AiwBzAH/AiwBpAH/CAABgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMZ - ASMDFAEbAzUBVgNiAdoBewJ8AfoDXgHIAx4BKxAAAagBqwGqAf8B5ALlAf8B4QHkAeMB/wHfAuEB/wHf - AeEB4AH/Ad8C4QH/Ad8C4QH/Ad8C4QH/Ad8C4QH/AeEB4gHhAf8B3gHhAd8B/wHhAeIB4QH/AZgBmgHP - Af8CgQHMAf8CagHMAf8CgQHRAf8CgQHQAf8CagHMAf8CagHMAf8CgQHUAf8CbgHNAf8CgQHTAf8CgQG4 - Af8EAAGoAasBqgH/AeQC5QH/AeEB5AHjAf8B3wLhAf8B3wHhAeAB/wHfAuEB/wHfAuEB/wHfAuEB/wHf - AuEB/wHhAeIB4QH/Ad4B4QHfAf8B4QHiAeEB/wHjAeUB5AH/AeEB4wHiAf8DfwH+AxgBIgMGAQgcAAGB - AYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGEAYIB/wIs - AaQB/wIsAcwJ/wIsAcwJ/wIsAcwB/wIsAaQB/wgAAYEBhAGCAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ - Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/A4EB/wOBAf8DgQH/AZwCnQH/A7YB/wGsAq0B/wNa - AcAQAANAAW4DZgHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKrAf8BqgKrAf8DuAH/ - Aa4CrwH/AcgCyQH/AoEBoQH/AoEBzAH/AmoBzAH/AoEB3wH/AsEB8wH/AoEB1gH/AoEB3wH/As0B9QH/ - AoEB0wH/AoEB0wH/AoEBuAH/BAADQAFuA2YB6gG6ArsB/wG2ArcB/wGtAq4B/wGqAasBqgH/AaoCqwH/ - AaoCqwH/AaoCqwH/A7gB/wGuAq8B/wHIAskB/wGdAZ8BngH/A2YB7wNJAYgDIAEuAwQBBRwAAYEBhAGC - Af8D8wH/A/AB/wPRAf8D7AH/A+sB/wPrAf8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/AiwBpAH/ - AiwBzBX/AiwBzAH/AiwBpAH/CAABgQGEAYIB/wPzAf8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/ - A8UB/wPFAf8D6AH/AYEBhAGCAf8DkAH/A7MB/wOBAf8DtgH/A+sB/wPVAf8DagH5EAADCAELAzgBXQN/ - Af4BlgGaAZgB/wGVAZkBmAH/AZIBlQGUAf8BkgGVAZQB/wGSAZUBlAH/AZIBlQGUAf8BkQGUAZMB/wGX - AZoBmQH/AZgBnAGbAf8CgQGUAf8CgQHMAf8CagHMAf8CegHPAf8CiAHnAf8C2gH4Af8CzQH1Af8CgQHf - Af8CagHMAf8CgQHTAf8CgQG4Af8EAAMIAQsDOAFdA38B/gGWAZoBmAH/AZUBmQGYAf8BkgGVAZQB/wGS - AZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGRAZQBkwH/AZcBmgGZAf8BmAGcAZsB/wNjAdUDPwFtA00BkgNH - AYADAAEBHAADTwGXAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ - AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNGAX0CLAGkAf8CLAHMAf8CLAHM - Df8CLAHMAf8CLAHMAf8CLAGkAf8IAANPAZcBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0YBfQOB - Af8DgQH/A4EB/wGjAqQB/wPVAf8D1wH/A1sBvxQAAwEBAgMEAQUBlQGZAZgB/wHLAc8BzgH/AdsB3QHc - Af8B2AHaAdkB/wHLAc8BzgH/AcABxAHDAf8BtwG7AboB/wNdAc0BgQGEAYIB/wJUAVYBqwKBAcwB/wJq - AcwB/wJqAcwB/wKBAd8F/wLaAfgB/wKBAdYB/wJqAcwB/wKBAdMB/wKBAbgB/wgAAwEBAgMEAQUBlQGZ - AZgB/wHLAc8BzgH/AdsB3QHcAf8B2AHaAdkB/wHLAc8BzgH/AcABxAHDAf8BtwG7AboB/wNdAc0BgQGE - AYIB/wNKAYkDWwHEA18BzgMZASMgAAGBAYQBggH/A9YB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt - Af8D7QH/A+0B/wPUAf8BgQGEAYIB/wIsAaQB/wIsAcwV/wIsAcwB/wIsAaQB/wgAAYEBhAGCAf8D1gH/ - AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHx - AesB/wH3AfEB6wH/A9QB/wGBAYQBggH/BAADBQEHAyYBOAOBAf8DgQH/A4EB/wMaASUQAAMfASwDYwHV - AaEBpgGkAf8BoQGlAaMB/wGhAaUBowH/AaEBpQGjAf8BoQGlAaMB/wGkAagBpwH/AaUBqgGoAf8BpQGq - AagB/wGlAakBpwH/AaUBqQGnAf8CgQGmAf8CgQHMAf8CagHMAf8CgQHbAf8CzQH1Af8CgQHfAf8ChwHn - Af8CwQHzAf8CfAHPAf8CgQHTAf8CgQG4Af8EAAMfASwDYwHVAaEBpgGkAf8BoQGlAaMB/wGhAaUBowH/ - AaEBpQGjAf8BoQGlAaMB/wGkAagBpwH/AaUBqgGoAf8BpQGqAagB/wGlAakBpwH/AaUBqQGnAf8BoAGl - AaMB/wNdAcMDBwEJAwcBCgMHAQoDBwEKAwgBCwMHAQoDBwEJAwQBBgMBAQIEAAGBAYQBggH/A/MB/wGB - AXQBYgH/AYEBdAFiAf8BgQF0AWIB/wGBAXQBYgH/AYEBdAFiAf8BgQF0AWIB/wGBAXQBYgH/AYEBdAFi - Af8BgQF0AWIB/wPtAf8BgQGEAYIB/wIsAaQB/wIsAcwJ/wIsAcwJ/wIsAcwB/wIsAaQB/wgAAYEBhAGC - Af8B+gH2AfIB/wGkAYEBaAH/AagBgQFuAf8BqAGBAW4B/wGoAYEBbgH/AagBgQFuAf8BqAGBAW4B/wGo - AYEBbgH/AagBgQFuAf8BpAGBAWgB/wH3AfEB6wH/AYEBhAGCAf8DCwEPAwwBEAMMARADgQH/A9MB/wOB - Af8UAAM1AVUBvwHDAcEB/wHXAdwB2QH/AeIB5gHkAf8B4gHmAeQB/wHgAeQB4gH/AcYBywHIAf8BxQHK - AcgB/wHdAeIB3wH/Ad8B5AHhAf8B3wHjAeEB/wHfAeMB4QH/AY8BkgHGAf8CgQHMAf8CagHMAf8CgQHX - Af8CgQHbAf8CagHMAf8CegHPAf8CgQHhAf8CfgHQAf8CgQHTAf8CgQG4Af8EAAM1AVUBvwHDAcEB/wHX - AdwB2QH/AeIB5gHkAf8B4gHmAeQB/wHgAeQB4gH/AcYBywHIAf8BxQHKAcgB/wHdAeIB3wH/Ad8B5AHh - Af8B3wHjAeEB/wHfAeMB4QH/AdYB2gHYAf8BnAGhAZ8B/wNYAbQDWAG0A1YBtQNWAbUDVgG1A1YBtQNX - AbMDMQFOAwsBDwQAAYEBhAGCAf8D7gH/AYEBdAFiAf8BkwGBAXgB/wGTAYEBeAH/AZQBgQF4Af8BkwGB - AXgB/wGTAYEBeAH/AZMBgQF5Af8BlAGBAXgB/wGBAXQBYgH/A+4B/wGBAYQBggH/AiwBpAH/AiwBzAH/ - AiwBzAH/AiwBzAH/AiwBzAH/AiwBzAH/AiwBzAH/AiwBzAH/AiwBpAH/CAABgQGEAYIB/wH4AfIB7AH/ - AagBgQFuAf8BugGBAXIB/wG6AYEBcgH/AbsBgQFyAf8BugGBAXIB/wG6AYEBcgH/AboBgQFzAf8BuwGB - AXIB/wGoAYEBbgH/AfgB8gHsAf8BgQGEAYIB/wM4AV4DOAFeAzYBWAOBAf8BxwLIAf8DgQH/AwMBBAMF - AQcMAAM1AVUBwgHFAcQB/wFTAlIBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/ - AZMBlgHKAf8CgQHMAf8CbwHNAf8CagHMAf8CagHMAf8CagHMAf8CagHMAf8CagHMAf8CagHMAf8CgQHT - Af8CQAG3Af0EAAM1AVUBwgHFAcQB/wFTAlIBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/ - AZ0CgQH/AdsB4AHeAf8BngGjAaEB/wHfAuEB/wHgAeIB4QH/Ad8B4gHgAf8B3gHhAd8B/wHiAeMB4gH/ - AeMB5QHjAf8B4QHjAeIB/wNZAbgDFQEdBAABgQGEAYIB/wPuAf8BgQF0AWIB/wGrAYoBgQH/AZ0CgQH/ - AY8BgQF1Af8BkAGBAXUB/wGQAYEBdQH/AZABgQF1Af8BjwGBAXUB/wGBAXQBYgH/A+4B/wGBAYQBggH/ - AWwBbgGSAf8CLAGkAf8CLAGkAf8CLAGkAf8CLAGkAf8CLAGkAf8CLAGkAf8CLAGkAf8DRgF+CAABgQGE - AYIB/wH4AfIB7AH/AagBgQFuAf8B0AGeAYEB/wHEAYsBgQH/AbYBgQFwAf8BtwGBAXAB/wG3AYEBcAH/ - AbcBgQFwAf8BtgGBAXAB/wGoAYEBbgH/AfgB8gHsAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AwEBAgwAAzUBVQO+Af0DgQH/AaACgQH/ - AZ8CgQH/AZoCgQH/AZgCgQH/AZcCgQH/AZgCgQH/AZgCgQH/AZgCgQH/A4EB/wG2AbkB1gH/AoEBuwH/ - AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/A18B+wJWAVcBswQAAzUBVQO+ - Af0DgQH/AaACgQH/AZ8CgQH/AZoCgQH/AZgCgQH/AZcCgQH/AZgCgQH/AZgCgQH/AZgCgQH/A4EB/wHf - AeIB4QH/AZ4BogGgAf8BqgKrAf8BsQKyAf8BrgKvAf8BsgKzAf8BugK7Af8BnAGeAZ0B/wNpAecDQAFu - AxYBHwQAAYEBhAGCAf8D7gH/AYEBdAFiAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGB - AYAB/wGLAYEBcgH/AYwBgQFyAf8BgQF0AWIB/wPvAf8BgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/ - A9kB/wGBAYQBggH/AwEBAgwAAYEBhAGCAf8B+AHyAewB/wGoAYEBbgH/Ac4BmwGBAf8BzgGbAYEB/wHO - AZsBgQH/AcIBhgGBAf8BugGBAXkB/wGyAYEBbQH/AbQBgQFtAf8BqAGBAW4B/wH4AfMB7QH/AYEBhAGC - Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGEAYIB/wMBAQIMAAM1AVUDXwH7A4EB/wGsAYsBgQH/ - AasBiQGBAf8BpQKBAf8BmQKBAf8BlQKBAf8BkwKBAf8BkwKBAf8BlAKBAf8DgQH/AdoC3gH/AYIBhgGl - Af8CgQGSAf8CgQGRAf8CgQGRAf8CgQGRAf8CgQGRAf8CVAFWAasDRQF8AlcBWQG6AzUBVgQAAzUBVQNf - AfsDgQH/AawBiwGBAf8BqwGJAYEB/wGlAoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOB - Af8B3QHhAd8B/wGcAaABnwH/AZIBlQGUAf8BkgGVAZQB/wGQAZMBkgH/AZsBngGdAf8BlAGXAZYB/wNW - AasDOwFkA1IBoQMrAUIEAAGBAYQBggH/A/AB/wGBAXQBYgH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/ - AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF9Af8BgQF0AWIB/wPwAf8BgQGEAYIB/wPrAf8D6wH/ - A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFuAf8BzAGXAYEB/wHL - AZgBgQH/AcwBmAGBAf8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXcB/wGoAYEBbgH/AfgB9AHu - Af8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA34B+AOBAf8BqwGJ - AYEB/wGpAYcBgQH/AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOBAf8BvQHB - AcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1cBswNjAdUDTQGRA2AB2QNRAZsDDQER - BAADNQFVA34B+AOBAf8BqwGJAYEB/wGpAYcBgQH/AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQ - AoEB/wGQAoEB/wOBAf8BvQHBAcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1cBswNj - AdUDTQGRA2AB2QNRAZsDDQERBAABgQGEAYIB/wPzAf8BgQF0AWIB/wGjAoEB/wGjAoEB/wGjAoEB/wGj - AoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAXQBYgH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0YBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBbgH/ - AckBlQGBAf8ByQGUAYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGB - AW4B/wH4AfQB7gH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ - AYEBhAGCAf8DRgF9EAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGBAf8BpwGEAYEB/wGm - AYIBgQH/AZkCgQH/AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGqAagB/wGlAaoBqAH/ - AaUBqgGnAf8BpQGpAacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAADNQFVA2IB9gOBAf8BqgGIAYEB/wGo - AYcBgQH/AacBhgGBAf8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/ - AZkBngGcAf8BpQGqAagB/wGlAaoBqAH/AaUBqgGnAf8BpQGpAacB/wGlAakBpwH/AZwBoAGeAf8DVAGm - DAABgQGEAYIB/wPtAf8BgQF0AWIB/wGBAXQBYgH/AYEBdAFiAf8BgQF0AWIB/wGBAXQBYgH/AYEBdAFi - Af8BgQF0AWIB/wGBAXQBYgH/AYEBdAFiAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPU - Af8BgQGEAYIB/xAAAYEBhAGCAf8B9wHxAesB/wGkAYEBaAH/AagBgQFuAf8BqAGBAW4B/wGoAYEBbgH/ - AagBgQFuAf8BqAGBAW4B/wGoAYEBbgH/AagBgQFuAf8BpAGBAWgB/wH4AfQB7gH/AYEBhAGCAf8B9wHx - AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/A9QB/wGBAYQBggH/EAADNQFVA2cB9AOB - Af8BqAGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGjAoEB/wGdAoEB/wGVAoEB/wOB - Af8B0wHWAdUB/wGZAZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/Ad8B4wHhAf8B3AHgAd4B/wHW - AdkB2AH/AYEBhAGCAf8MAAM1AVUDZwH0A4EB/wGoAYYBgQH/AacBhQGBAf8BpgGEAYEB/wGlAYIBgQH/ - AaQCgQH/AaMCgQH/AZ0CgQH/AZUCgQH/A4EB/wHTAdYB1QH/AZkBngGcAf8BxQHKAcgB/wHeAeIB4AH/ - Ad8B4wHhAf8B3wHjAeEB/wHcAeAB3gH/AdYB2QHYAf8BgQGEAYIB/wwAAYEBhAGCAf8BxwLIAf8D7QH/ - A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAXQBYgH/AYEBdAFi - Af8BgQF0AWIB/wGBAXQBYgH/AYEBdAFiAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHr - Af8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3 - AfEB6wH/AdkC2gH/AYEBhAGCAf8BqAGBAW4B/wGoAYEBbgH/AagBgQFuAf8BqAGBAW4B/wGkAYEBaAH/ - AfcB8QHrAf8BgQGEAYIB/xAAAzUBVQNsAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/ - AaICgQH/AaECgQH/AaACgQH/AZ8CgQH/A4EB/wHAAcMBwQH/AZYBmQGXAf8DgQH/A4EB/wOBAf8DgQH/ - A1sBwgHcAd8B3gH/AYEBhAGCAf8MAAM1AVUDbAHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGk - AoEB/wGiAoEB/wGhAoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/ - A4EB/wNbAcIB3AHfAd4B/wGBAYQBggH/DAADVgGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGH - AoEB/wGTAYEBeAH/AZMBgQF4Af8BkwGBAXkB/wGUAYEBeAH/AYEBdAFiAf8D7gH/AYEBhAGCAf8QAANW - AawBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFyAf8BugGBAXIB/wG6AYEBcwH/ - AbsBgQFyAf8BqAGBAW4B/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZwH0A1YBrAGWAoEB/wGWAoEB/wGW - AoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGdAoEB/wHUAdYB1AH/AZsBnQGbAf8BlwKB - Af8BmAKBAf8BlwKBAf8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/DAADNQFVA2cB9ANWAawBlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGb - AZ0BmwH/AZcCgQH/AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGC - Af8D7gH/AYEBdAFiAf8BqwGKAYEB/wGdAoEB/wGPAYEBdQH/AZABgQF1Af8BkAGBAXUB/wGQAYEBdQH/ - AY8BgQF1Af8BgQF0AWIB/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBbgH/AdABngGB - Af8BxAGLAYEB/wG2AYEBcAH/AbcBgQFwAf8BtwGBAXAB/wG3AYEBcAH/AbYBgQFwAf8BqAGBAW4B/wH4 - AfIB7AH/AYEBhAGCAf8QAAMhATADYAHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe - AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmwGfAZ0B/wGJAYcBgQH/ - AZQCgQH/AZMCgQH/AZQCgQH/AZQCgQH/AZsCgQH/A74B/QGBAYQBggH/DAADIQEwA2AB2QGaAZ4BnQH/ - AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe - AZ0B/wGaAZ4BnQH/AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO+ - Af0BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBdAFiAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKB - Af8BkwGBAYAB/wGLAYEBcgH/AYwBgQFyAf8BgQF0AWIB/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHy - AewB/wGoAYEBbgH/Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXkB/wGyAYEBbQH/ - AbQBgQFtAf8BqAGBAW4B/wH4AfMB7QH/AYEBhAGCAf8sAANVAakDcAH1AYkCgQH/AaoBiAGBAf8BqQGH - AYEB/wGpAYYBgQH/AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DfgH8AYEBhAGC - Af8oAANVAakDcAH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/AagBhQGBAf8BkwKBAf8BkAKB - Af8BkAKBAf8BkAKBAf8BjgKBAf8DfgH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAXQBYgH/AaYBgwGB - Af8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF9Af8BgQF0AWIB/wPw - Af8BgQGEAYIB/ywAAYEBhAGCAf8B+AH0Ae4B/wGoAYEBbgH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/ - AcsBmAGBAf8BzAGYAYEB/wHMAZgBgQH/AbcBgQF3Af8BqAGBAW4B/wH4AfQB7gH/AYEBhAGCAf8sAANV - AakDdAHxAYkCgQH/AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGO - AoEB/wGLAoEB/wGXAoEB/wNqAfkBgQGEAYIB/ygAA1UBqQN0AfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/ - AacBhgGBAf8BpgGEAYEB/wGmAYIBgQH/AZYCgQH/AY4CgQH/AYsCgQH/AZcCgQH/A2oB+QGBAYQBggH/ - KAABgQGEAYIB/wPzAf8BgQF0AWIB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGj - AoEB/wGBAXQBYgH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFuAf8ByQGVAYEB/wHJ - AZQBgQH/AckBlAGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBbgH/AfgB9AHu - Af8BgQGEAYIB/ywAA1UBqQNuAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGk - AoEB/wGiAoEB/wGbAoEB/wGSAoEB/wGXAoEB/wNuAfcBgQGEAYIB/ygAA1UBqQNuAe4BiAKBAf8BpwGG - AYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGbAoEB/wGSAoEB/wGXAoEB/wNu - AfcBgQGEAYIB/ygAAYEBhAGCAf8D7QH/AYEBdAFiAf8BgQF0AWIB/wGBAXQBYgH/AYEBdAFiAf8BgQF0 - AWIB/wGBAXQBYgH/AYEBdAFiAf8BgQF0AWIB/wGBAXQBYgH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3 - AfEB6wH/AaQBgQFoAf8BqAGBAW4B/wGoAYEBbgH/AagBgQFuAf8BqAGBAW4B/wGoAYEBbgH/AagBgQFu - Af8BqAGBAW4B/wGkAYEBaAH/AfgB9AHuAf8BgQGEAYIB/ywAA1UBqQNpAekBiAKBAf8BpgGEAYEB/wGl - AYQBgQH/AaQBggGBAf8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKBAf8DagH5AYEBhAGC - Af8oAANVAakDaQHpAYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGkAYIBgQH/AaMCgQH/AaICgQH/AaECgQH/ - AaACgQH/AZ4CgQH/AZACgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wHHAsgB/wPtAf8D7QH/A+0B/wPt - Af8D7QH/A+0B/wPtAf8D7QH/A+0B/wHZAtoB/wGBAYQBggH/LAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/ - AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHx - AesB/wHZAtoB/wGBAYQBggH/LAADVQGpA24B7gNeAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8DYgHWA3QB8QGBAYQBggH/KAADVQGpA24B7gNeAcgBlgKBAf8BlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYgHWA3QB8QGBAYQBggH/KAADVgGs - AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNPAZcsAANWAawBgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/A08BlywAAzgBXgN/Af4BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ - AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wNfAfsDXgHIKAADOAFeA38B/gGa - AZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGd - Af8BmgGeAZ0B/wGaAZ4BnQH/A18B+wNeAcj/AIkAAUIBTQE+BwABPgMAASgDAAFgAwABMAMAAQEBAAEB - BQABQAECFgAD//8AIgAG/wGAAgABgAEAAX8BgAEAAQEBgAEAAQcBgAIAAYABAAE/AYABAAEBAYABAAEH - AYACAAGAAQABPwGAAQABAQGAAQABBwGAAgABgAEAAT8BgAEAAQEBgAEAAQcBgAIAAYABAAE/AYABAAEB - AYABAAEHAcACAAHAAQABfwGAAQABAQGAAQIBBwGAAgABgAIAAYABAAEBAYABAAEPAYACAAGAAgABgAEA - AQEBgAEAAQMBgAIAAYACAAGAAQABAQGAAQABAwGAAgABgAIAAYABAAEDAYABAAEDAYACAAGAAgABgAEA - AQcBgAEAAQcBgAIAAYACAAGAAQABBwGAAQABBwGAAQABAwGAAQABAwGAAQABBwGAAQABBwGAAQABAwGA - AQABAwGAAQABBwGAAQABBwGAAQABAwGAAQABAwGAAQABBwGAAQABBwGAAQABAwGAAQABAwH/AQABBwH/ - AQABBwGAAQABAwGAAQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwH/AQABBwH/AQABBwH/AQABAwH/ - AQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwH/AQABBwH/ - AQABBwH/AQABAwH/AQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwz/Cw== - - - - - - iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 - JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA3UAAAN1AHvkboVAAAAhElE - QVRIS2MYBaNgFAxXYJK65AwQ/ycRn4FqJwxAGg5cfvF/98Xn//cA8fZzz7DibWef/d96BkgDMUgPVDth - AFK85czT/1O33/4/Zdvt/5O23Po/YfOt/70bb/7vWn/zf9vam/+bV934X7/i+v/aZdf/z9/3gGQLaBtE - o2AUjIIhBRgYAJgulRaaJZA7AAAAAElFTkSuQmCC - - - - - iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 - JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA3UAAAN1AHvkboVAAAA+0lE - QVRIS+2UTwsBQRjGF1/JV3IlH0AOykEpiZs/FyQlseweSJQTS9gDy0XEcvIRHvMybUIh40D7q2dnetv3 - eWZ22pFsbCzcnpzGBC6Nl8VBxg1tizoTzXlZHP8TIIsIYAbXB2qJVi8PLgEP9PrBU0NH36M5MdHiauuH - s3mtv0VzbEId7aAMd2xXbGSiHt7+HHqZVptUl0goS8TrC8TkBaJVA5GKgXDZQKg0R7A4Q6AwQ7a9ejvg - 4SfKddfwZ6Z3da7P/g0yyV8F8LI4yLShmfClvxkwMOFNiQ9w0YNM9dURpd7mNsDBx89gpl+97GiVTiba - DYnmNj+BJJ0AUzYC0bmj8Z8AAAAASUVORK5CYII= - - - - 116, 17 - - - 246, 17 - - - 353, 17 - - - - AAABAAUAAAAAAAEAIAAoIAQAVgAAAICAAAABACAAKAgBAH4gBAAwMAAAAQAgAKglAACmKAUAICAAAAEA - IACoEAAATk4FABAQAAABACAAaAQAAPZeBQAoAAAAAAEAAAACAAABACAAAAAAAAAABAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDovAAg6H/AIOh/wCDof8Ag6H/AIOhwQCA - mQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbiwAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1sIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AgqK0AICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+TAFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBablgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8IAICeIgCG - oRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JKAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoaYAqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYKAFpv7wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm+OAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJaAIOh5gCD - of8Ag6H5AIOijgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfIACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AISglwD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtwYgBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvxQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6F/AIOh/gCD - of8Ag6H/AIOh/wCDof8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - bskAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/YAQIAEAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6GgAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOhsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiygCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEongAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABY - bTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpuMwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACImQ8AhKG8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oaAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW2+YAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Q9Pn/wRhdv8AWm//AFpv/wBab/8AWm//AFpv/wBa - cGkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoh4Ag6HTAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6JzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJ2AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAgqJaAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVWoMAFpv8wBab/8AWm//AFpv/wBab/8AWm//FYGV/0/p/P8Uf5P/AFpv/wBab/8AWm//AFpv/wBa - b/8AWm+fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoTEAg6HlAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIWgSwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCE - ok0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtuaABab/8AWm//AFpv/wBab/8AWm//AFpv/za7z/9P6fz/JZyx/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv1wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoUkAg6HzAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoiEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H1AIOfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABab88AWm//AFpv/wBab/8AWm//AFpv/whoff9O5vr/T+n8/za8z/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/0AVXcPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoWcAg6H8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoZ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAA//8BAIOh9ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDovAAhKI0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFpv/wBab/8mn7T/T+n8/0/p/P9G2ez/AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpxRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCDoYgAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKFXAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDocsAg6H/AIOh/wCDof8Ag6H/AIOh/wKFo/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh6gCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+eAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Rtnr/0/p/P9P6fz/T+n8/wlq - f/8AWm//AFpv/wBab/8AWm//AFpv/wBab3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDoagAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AICcEgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KhAIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/JrPN/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAYHAQAFpv9QBab/8AWm//AFpv/wBab/8AWm//F4WY/0/p/P9P6fz/T+n8/0/p - /P8Zh5z/AFpv/wBab/8AWm//AFpv/wBab/8AWW+xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICcEgCDoMIAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhyAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdwCDof8Ag6H/AIOh/wCDof8Ag6H/C5Kv/0/p - /P8hrcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhaYXAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFlubQBab/8AWm//AFxx/wBedf8AYXf/AGR7/yeuxP81x97/M8bc/y/A - 1/8vwNf/GJev/wBqg/8AaoP/AGqD/wBqg/8AaIH/AGmA7wCDoUQAhKE2AIShGwCAvwQAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgIwCDodgAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoYIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoUwAg6H/AIOh/wCDof8Ag6H/AIOh/wGE - ov9N5vn/T+n8/xynwv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKEbAIKiPwCDomMAgqGFAIOioQB5lfQAfJj/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIOh5gCD - ocwAgqCyAISglwCEon4Ag6BhAISiNACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiNwCDoekAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x6qxP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqA7AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/QNbs/0/p/P9N5/r/GKK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDn0AAg6FvAIShnQCD - ocwAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIKh1wCDoakAg6B5AIWgSwCAnRoAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShUQCDofYAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/BImm/zrN5P8mtM3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAKqqAwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCDofQAg6H/AIOh/wCD - of8Ag6H/AIOh/zPF3P9P6fz/T+n8/0zl+P8Unbj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - obsAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKgKwCCoWQAhKGdAIOh1gCDof4Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh3ACD - oZoAgqJYAICiFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCDofwAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/CY+r/0HX7P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKirAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAF10FgBbcGIAWm5dAGBwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6HMAIOh/wCD - of8Ag6H/AIOh/wCDof8mtM3/T+n8/0/p/P9P6fz/SuP2/xGZtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKhrgCqqgMAAAAAAICkHACComAAg6KkAIOh5wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof4Ag6LSAIOhkACFoEsAkpIHAAAAAAAAAAAAqqoDAIOhkACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/D5ax/0fe8v9P6fz/SeH1/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - omUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFttRgBab/UAWm//AFpv/wBab+8AW3BrAICAAgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhogCD - of8Ag6H/AIOh/wCDof8Ag6H/GaO9/0/p/P9P6fz/T+n8/0/p/P9I4fT/DpSw/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HQAISh0ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACDoqEAhKFRAIKhsACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/FqC7/0vk9/9P6fz/T+n8/zPF3P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AgKIeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBab+gAWm//AFpv/wBab/8AWm//AFpv/wBab8sAXHAyAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - ongAg6H/AIOh/wCDof8Ag6H/AIOh/wySr/9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8v8Lka7/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/H6vF/07n+/9P6fz/T+n8/0/p/P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCD - of8AgqHXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXbSMAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+wBa - cJAAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJNAIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/Tef5/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wiO - q/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6L/AImn/wCOrP8Ak7D/AJi0/wCZtf8AmbX/AJ26/wCeuv8Anrr/AJ66/wCeuv8Anbn/AJm1/wCV - sv8Akq//AI2q/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0/p/P9P6fz/T+n8/0/p/P9P6fz/CI6r/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFlv5ABbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICeIgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/0HX7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9B1+z/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCLqf8Al7T/AKK+/wCuyf8At9H/AL/a/wTJ - 4v8H0uv/C9fv/w3X7/8Q2fD/FNrw/xXZ8f8V2fH/Fdnx/xHY8P8Q2fD/ENnw/xDZ8P8L1/D/B9bu/wLV - 7f8A1O3/ANTt/wDU7f8A1O3/AM7m/wDG3/8AvNf/ALXQ/wCsyP8AoL3/AJKv/wCEo/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ChaT/M8Xc/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab6gAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFtvtABdbCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACAgAIAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8zxtz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/z7T6f8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhaP/AJWy/wCkwP8Ft9H/EMri/xvZ7/8m3vX/MOH2/zfj+P8+5fn/Ruf6/0zo - +/9P6fz/T+n8/0/p/P9O6Pv/RNru/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/0fn+v9B5vr/OOL4/zDh9v8n3vX/Hdzz/xTZ8f8J1u//ANTt/wDU7f8A0uv/AMXf/wC2 - 0f8AqMP/AJm1/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Fiqf/O8/m/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yu60v8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDofgAjqoJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWG0xAFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/2AFlveABVgAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOgzQCDof8Ag6H/AIOh/wCDof8Ag6H/JrTN/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/O8/l/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ai6j/AaC9/w261P8d0Oj/K9/0/zvk+P9J6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/SN/z/zDA1v8aorv/Boei/wGAnP9E2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8/5fn/MuH2/yXe - 9f8W2vH/BtXu/wDU7f8A1Oz/AMPd/wCuyf8AmbX/AIak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8KkK3/Q9nt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Vnrn/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb7cAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/UAFlvPAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaMAg6H/AIOh/wCDof8Ag6H/AIOh/xmjvv9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83yuD/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSP - rP8n1+7/O+T4/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DV - 6/8pts3/EZaw/wGAnP8Af5v/AH+b/wB/m/8Af5v/Iq3F/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9E5vn/M+L3/yLd9P8Q2PD/AdXt/wDR6f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8RmLT/SOD0/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/AoWk/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AISgdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW0/AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/4AWm+cAF5rEwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Mk6//T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLE2/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8ChaP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I4PP/KrjP/wuO - qf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWGof9L5Pf/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fi9/8Pnrr/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Yor3/TOX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OMvh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b8YAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b+sAW25fAAAAAQAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh2ACDof8Ag6H/AIOh/wCDof8Ag6H/AYWi/03n - +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Lb3V/wCDof8Ag6H/AIOh/wCD - of8Ag6H/D5ay/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+j8/zvP5P8bo73/AoKe/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/LbvS/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8Pl7L/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ir8j/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKv - yf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofEAkrYHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWm5PAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab8EAV3ApAAAAAACEojQAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of9B1+z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puM//AIOh/wCD - of8Ag6H/AoWj/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N8ne/w6SrP8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wuOqf9O6Pv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl+P8ChaP/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wGEov8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8Mk6//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAQBab9UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AGB1+gB6l9UAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/NMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7U - 6f8ltM3/J7XO/0Ta7v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P87zuT/Epew/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/D4eh/xOJov8Af5v/AH+b/wB/m/8Af5v/NcTa/03k - 9/9N5Pf/TeT3/07l+P9O5vn/Tuf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8yxNv/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wOGpP82yN//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9F3fH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOivwCCoTEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm9eAFpv/wBab/8AWm//AFpv/wBab/8AWm//IJSo/x2PpP8AW3D/AFpv/wBa - b/8AWm//AFpv/wBab/8AXHH/AG6I/wCBn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/ya0zv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8f8Xnrf/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wKAnP8kkan/Tqa6/2ezw/9Mpbn/AH+b/wB/m/8Af5v/AH+b/wGQ - qv8Fwtj/BcLY/wXC2P8Jwtn/DcTZ/xHF2f8Wxdr/Gsjd/x7K3/8nzuP/L9Po/znY7P9B3fD/S+L0/03k - 9/9P6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KLfP/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/weLqf8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/L8DX/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIShnQCF - phcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAECABABab+EAWm//AFpv/wBab/8AWm//AFpv/wVjeP9L4vX/O8XZ/wxx - hv8AWm//AFpv/wBab/8AZ3//AH+b/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ZpL7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLB2P8FhqH/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/CoSf/zSZr/94v9D/oNbl/7zp9/+Y0uH/Z7PD/w6GoP8Af5v/AH+b/wB/ - m/8Af5v/ALPM/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8Iwtn/FMXa/yDM4f8s0uf/ONns/0fh8/9O5/r/T+n8/0/p/P9P6fz/T+n8/znM4/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuSr/9E2+//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xmkvv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HtAIShXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW5tAFpv/wBab/8AWm//AFpv/wBab/8AWm//K6e7/0/p - /P9N5vn/KaS3/wNieP8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/DJOw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SN/z/xefuP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8MhaD/SKS5/5HO3v+55/X/yPD+/8jw/v/I8P7/wOv6/2u1xv83m7H/AH+b/wB/ - m/8Af5v/AH+b/wCYsv8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/CsPZ/xrJ3v8p0+f/O9vv/0vk9/9P6fz/IK3G/wCD - of8Ag6H/AIOh/xWeuf9J4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07n+/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhKG4AICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFVxCQBab+sAWm//AFpv/wBab/8AWm//AFpv/who - ff9N5vn/T+n8/zDC2f8EiKb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGFov9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/854/j/G8Xc/wGHov8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wiDnv9Pqr//pNrp/8Xu/f/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v+Ny9v/X6/A/wKA - nP8Af5v/AH+b/wB/m/8Agp7/ALvS/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8CwNf/Esfb/ybS - 5v8uyeD/Lr7W/zvQ5f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P880ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAg6FtAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//K6zA/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCF - o/8Prcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Qdfs/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9K5/v/M+L3/xrZ8P8EzeX/AKzF/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/zqetv+g2Of/x/D+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/t+X0/2ez - w/8kkan/AH+b/wB/m/8Af5v/AH+b/wChuf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wTB2P8Vy+D/Ldbr/0bk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/J7XO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wCIpv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq - qgMAgqCBAIOh5gCDoewAg6CnAIakKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBwEABab/MAWm//AFpv/wBa - b/8AWm//AGd//wGCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wql - wf823/T/Tuj7/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/zTH3f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/zPi9/8W2vH/AtDn/wDH3v8Av9f/AKC6/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/GY2n/4/P4f/F7/3/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw - /v9/wtL/Taa5/wB/m/8Af5v/AH+b/wB/m/8AhqH/AL7W/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/EMje/yjV6v9B4/b/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xijvv8s3fL/B6rG/wCE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh8QCEol0AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqKDAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6KhAIOiIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBa - b/8AXHL/AHSO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5Ku/yfT - 6v9M6Pv/T+n8/0/p/P8PlrL/AIOh/wCDof8Ag6H/AIOh/wCDof8ntc7/T+n8/0/p/P9P6fz/T+n8/0/p - /P895Pn/INzz/wXV7f8Ay+P/AMPZ/wDA1/8AwNf/AJq0/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/S6vB/7vp+P/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw - /v/I8P7/qt3s/2ezw/8Qh6H/AH+b/wB/m/8Af5v/AH+b/wCowf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/w7J3v8q2Oz/R+b5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8vwNf/T+n8/0vo - +/8n1+7/BJi1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOipACA - pA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOh5ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKGTAIWjGQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhsGgBa - b/kAYXf/AHyZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaT/GL/Y/0Tm - +f9P6fz/T+n8/0/p/P9P6fz/HKfB/wCDof8Ag6H/AIOh/wCDof8Ag6H/GqS//0/p/P9P6fz/TOj7/y7g - 9f8M2PD/ANLr/wDJ4f8Awdj/AMDX/wDA1/8AwNf/AKK7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8IhKD/hczg/8bw/v/G8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw - /v/I8P7/yPD+/8Xv/f90u8z/OZyx/wB/m/8Af5v/AH+b/wB/m/8AjKb/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHD - 2f8VzuT/Mt7z/03p+/9P6fz/T+n8/0/p/P9P6fz/DZSw/wCDof8Ag6H/AIOh/wCDof8ChaT/Rt3y/0/p - /P9P6fz/T+n8/0jn+v8hyOD/Aoim/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKLdAIKiNwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAP//AQCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgqGHAICfEAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAbYa/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Gm7j/Nd3z/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKvyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xGZtf9G5vr/Jd70/wXW - 7v8A0uv/AMnh/wDC2f8Awdj/AMHY/wDB2P8Awdj/AKnC/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Ui6b/otzu/8Xv/v/F8P7/xvD+/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8Hu/v+u6f7/oub+/8Du - /v/I8P7/yPD+/8Xw/v/D8P7/mtXl/1+vwP8DgZz/AH+b/wB/m/8Af5v/AH+b/wCwyP8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8Gxdz/JNfs/0Xm+v9P6fz/T+n8/zPF2/8Ag6H/AIOh/wCDof8Ag6H/KbjQ/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8XsMn/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDofsAg6F1AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6GpAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oe8Ag6B5AIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq - qgMAg6GQAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCGpP8Xwtv/SOf6/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puND/AIOh/wCDof8Ag6H/AIOh/wCDof8Lor7/AtTt/wDT - 7P8AyuL/AMPZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/ALXN/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8gk63/reP1/8Pv/v/E7/7/xe/+/8bw/v/G8P7/x/D+/8jw/v/I8P7/xO/+/7Dq/v+i5v7/oub+/6Lm - /v+x6v7/yPD+/8Xw/v/C8P7/wO/+/7ns+v9stsf/JZKq/wB/m/8Af5v/AH+b/wB/m/8Ala//AMHY/wDB - 2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Bw9n/F9Hp/zni9/9P6fz/N8rg/xihvP8Vn7n/Lr7W/07n - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfZ7v8KlLH/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoagAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIafKACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoecAg6FtAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA - nxAAhKG4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSZtf8w3fL/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/w+Xsv8Ag6H/AIOh/wCDof8Ag6H/ALrW/wDM - 5P8Aw9n/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMDX/wCKpf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8dkKv/suX3/8Lv/v/D7/7/xO/+/8Tv/v/F7/7/xvD+/8bw/v/H8P7/uOz+/6Tn/v+i5v7/oub+/6Lm - /v+i5v7/pOb+/8Lv/v/C8P7/v+/+/7zv/f+57v3/i87e/0+nuv8Af5v/AH+b/wB/m/8Af5v/AICc/wC5 - 0f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Awtn/Ds3j/zTg9f9O6Pz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/ye/2P8ChqT/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QCDoCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKGTAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEod8AhKFfAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - nyUAg6HYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/w+20P9E5vn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3vL/D6S//wCJp/8Aj63/ALTO/wDF - 3P8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wCdtv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8QiaT/qOL0/8Hu/v/B7v7/wu/+/8Pv/v/E7/7/xe/+/8Xw/v/C7/7/q+j+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oub+/53l/f+x6/3/v+/+/7zv/f+57v3/tu79/6nm9f9os8T/EYii/wB/m/8Af5v/AH+b/wB/ - m/8Anbf/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awtn/Cs3l/zHf - 9P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qt/z/xCZ - tP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HuAIOfSAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICmFACDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoNUAg6JSAP//AQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - ojcAg6HqAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIuo/x3P5/9M6Pv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P864/j/Ddfw/wDU7f8A0uv/AMnh/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wC40P8AgJz/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Gg5//mNjs/8Du/v/A7v7/we7+/8Lv/v/C7/7/w+/+/8Tv/v+97f7/pef+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oeX9/5zl/f+X5f3/m+b9/7zv/f+57v3/tu79/7Pu/f+x7v3/fcXV/zudsv8Af5v/AH+b/wB/ - m/8Af5v/AIOf/wC/1v8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awtn/DM3l/zfh9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9O6Pv/Kb3V/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAhKJ4AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6F1AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDosoAg6FEAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oUwAg6H0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Apq2/y/d8/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bn+v8a2/L/ANTt/wDU7f8Azub/AMXc/wDE - 2/8AxNv/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8AlbD/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/bcDY/77u/v+/7v7/wO7+/8Hu/v/B7v7/wu/+/8Pv/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oeX9/5zl/f+X5P3/k+T9/47k/P+u7P3/tu79/7Pu/f+w7v3/re39/5rf7v9hsMH/BIGd/wB/ - m/8Af5v/AH+b/wB/m/8Apr7/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Aw9n/EdDm/zzj+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9B3PH/D5ey/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oZoAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDod4Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ob0AhJ84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oGEAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/B7DK/z7l+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDS6/8AyeH/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8Ats//AICb/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/MZ64/7rs/v++7f7/vu7+/7/u/v/A7v7/we7+/8Hu/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43k/P+J4/z/l+f8/7Pu/f+w7v3/re39/6rt/f+n7fz/c77O/yeT - qv8Af5v/AH+b/wB/m/8Af5v/AImk/wDC2f8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8AxNv/GtTq/0fm+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o+/8isMr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIShuACJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqBWAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDobEAhaIsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oWoAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhqT/DMHa/0Xn+v9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOf6/xja8v8A1O3/ANTt/wDQ6P8Ax97/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AJOu/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/CoWh/6Lg9v+87f7/ve3+/77t/v+/7v7/wO7+/8Du/v+87f7/pOb+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43j/P+I4/z/g+P8/4Di/P+t7f3/re39/6rt/f+n7fz/pO38/4zX - 5/9Qp7r/AH+b/wB/m/8Af5v/AH+b/wB/m/8Arsb/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Ex97/Ktvw/03p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbL4f8Fiqf/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HQAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD - ocUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ru9L/Iq/J/wKFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCDoaMAgJ4iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - onMAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/Eszk/0no+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/NeL3/wbV7v8A1O3/ANTt/wDM5P8Axt3/AMbd/wDG - 3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/ALnR/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/1i3z/+67P7/vO3+/73t/v+97f7/vu7+/7/u/v++7v7/puf+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/n+X9/5rl/f+V5P3/keT9/4zj/P+I4/z/g+P8/37i+/954vv/lej8/6rt/f+n7fz/pO38/6Hs - /P+d6/v/bLnJ/xOJov8Af5v/AH+b/wB/m/8Af5v/AJOt/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8MzOT/O+L2/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rd7y/xGZtf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeMAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhJ84AIOh/gCDof8Ag6H/AIOh/wCDof8Ag6H/CY+r/0zl+P9G3fH/HqvE/wGFov8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDopYAhaMZAAAAAAAAAAAAAAAAAAAAAACD - oXUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak6//GNXt/0zo+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6fz/Id3z/wDU7f8A1O3/ANLr/wDJ4f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wCguf8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wyHov+o5fv/uu3+/7zt/v+87f7/ve3+/77t/v++7v7/run+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/n+X9/5rl/f+V5P3/kOT9/4zj/P+H4/z/guL8/33i+/954vv/dOH7/3rj+/+n7fz/pO38/6Hs - /P+e7Pz/m+v7/4HQ4P89nrP/AH+b/wB/m/8Af5v/AH+b/wCAm/8AutL/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDE - 2/8g1er/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/H6vF/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACEojwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoagAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8puM//T+n8/0/p/P9D2e3/GqW//wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9gCCoIkAgJwSAAAAAACD - oW0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/INrx/07o/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/Edjw/wDU7f8A1O3/ANHp/wDI4P8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDG3f8Ah6L/AH+b/wB/ - m/8Af5v/AH+b/wB/m/9Eq8X/tuv+/7rs/v+77f7/vO3+/7zt/v+97f7/uez+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/nuX9/5nl/f+V5P3/kOT8/4vj/P+H4/z/guL8/33i+/944fv/dOH7/2/h+/9q4Pr/ler8/6Hs - /P+e7Pz/m+v7/5jr+/+R5ff/YrHC/wWCnf8Af5v/AH+b/wB/m/8Af5v/AJu1/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wnK4P863/X/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8tvdX/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIOgRgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgJ8gAIOh9wCDof8Ag6H/AIOh/wCDof8Ag6H/BIil/0jf8/9P6fz/T+n8/0/p - /P8/1Or/FqC6/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wCE - oa4Ag6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AoLz/Jd3z/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P875Pj/B9bu/wDU7f8A1O3/AM/n/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Atc7/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/h9Pr/7ns/v+57P7/uu3+/7vt/v+87f7/ve3+/6vp/v+i5v7/oub+/6Lm - /v+i5v7/nuX9/5nl/f+U5P3/j+T8/4vj/P+G4/z/geL8/33i+/944fv/c+H7/2/h+/9q4Pr/ZeD6/3fk - +/+d7Pz/m+v7/5jr+/+V6/v/k+v7/3bH2P8plKv/AH+b/wB/m/8Af5v/AH+b/wCCnv8AwNj/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AcTb/yHU6f9N6Pr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/znN4/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKFRAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoIkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8fqsT/T+n8/0/p - /P9P6fz/T+n8/0/p/P880Ob/Epu2/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ApsH/Jd70/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u4PX/AdXt/wDU7f8A1O3/AM3l/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AKW+/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/GY+r/63p/v+47P7/uez+/7rs/v+67f7/vO3+/7rt/v+j5v7/oub+/6Lm - /v+i5v7/neX9/5jl/f+U5P3/j+T8/4rj/P+G4/z/geL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/2Df - +v9d3/n/lur7/5jr+/+V6/v/kuv7/4/q+/+F3/D/Uqi7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AKS9/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/Dcrg/0Lh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Qtru/wqQrf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACE - ol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQOAIOh6gCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0HX - 7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83y+H/D5ax/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqMT/Jd71/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A0+z/AMzk/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCU - rv8Af5v/AH+b/wB/m/8Af5v/AH+b/0Oqxf+y6v7/uOz+/7js/v+57P7/uuz+/7vt/v+y6v7/oub+/6Lm - /v+i5v7/neX9/5jl/f+T5P3/juT8/4rj/P+F4/z/gOL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/1/f - +v9a3/n/Vt75/3jl+v+U6/v/kuv7/4/q+/+M6vv/iur7/2/A0f8ViqP/AH+b/wB/m/8Af5v/AH+b/wCH - ov8Axd3/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Ex97/Mdnt/0/o+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9H4PT/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWoAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Vnrn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8zxtz/C5Ku/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqsX/Jt71/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8d3PL/ANTt/wDU7f8A0+z/AMzk/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AhJ//AH+b/wB/m/8Af5v/AH+b/wB/m/9rw9z/tuv+/7fs/v+47P7/uez+/7ns/v+67P7/q+j+/6Lm - /v+h5f3/nOX9/5fl/f+T5P3/juT8/4nj/P+F4/z/gOL8/3vi+/924fv/ceH7/23g+/9o4Pr/ZN/6/1/f - +v9a3/n/Vt75/1He+f9X3/n/kev7/47q+/+M6vv/ier7/4bq+v972On/Pp6z/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AK3H/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8c0eb/S+T3/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9/8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAIOh1gCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/zjM4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8vwNf/CY+r/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CJWy/znN4/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8V2fH/ANTt/wDU7f8A0+z/AMvj/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AxNz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/lNzz/7br/v+37P7/uOz+/7js/v+57P7/uuz+/6Xn - /v+h5f3/nOX9/5fk/f+S5P3/jeT8/4nj/P+E4/z/f+L8/3vi+/924fv/ceH7/23g+v9o4Pr/Y9/6/17f - +v9Z3vn/Vd75/1De+f9L3fn/R934/3vn+v+L6vv/ier7/4bq+v+D6fr/gOj6/2a4yf8Ggp3/AH+b/wB/ - m/8Af5v/AH+b/wCPqv8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/w7K - 4f9E3/L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/xegu/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDoWcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Nk7D/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zm - +f8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/FJ23/z3S - 6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fn+v8P2O//ANTt/wDU7f8A0uv/AMvj/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/ALzU/wB/m/8Af5v/AH+b/wB/m/8Af5v/CIWg/6fn/v+16/7/tuv+/7fs/v+47P7/uOz+/7js - /v+g5f3/m+X9/5bk/f+S5P3/jeP8/4jj/P+E4/z/f+L8/3ri+/914fv/cOH7/2zg+v9n4Pr/Y9/6/17f - +v9Z3vn/VN75/1De+f9L3fn/Rt34/0Hc+P9Y4fn/iOr7/4Xq+v+D6fr/gOn6/37p+v900uL/KpSr/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/ALjR/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//CMnh/zvb7v9O5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/GaO+/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOiYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0vj9/8nts//BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/GKG8/0Xc8P9P6fz/T+n8/0fn+v8M2PD/ANTt/wDU7f8A0uv/AMzk/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wC0zf8Af5v/AH+b/wB/m/8Af5v/AH+b/xuRrP+n5/7/tev+/7br/v+26/7/t+z+/7js - /v+z6v3/m+X9/5bk/f+R5P3/jOP8/4jj/P+D4/z/fuL8/3ri+/914fv/cOH7/2zg+v9n4Pr/Yt/6/13f - +v9Y3vn/VN75/0/d+f9L3fn/Rt34/0Hc+P883Pj/Otv3/3/o+v+C6fr/f+n6/37p+v9+6fr/feX3/1Ws - v/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCYs/8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8CyOD/MNbp/07l+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o - +v8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4Ag6BhAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF - oS4Ag6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Hi6n/S+P3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nh9f8jsMr/Aoak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Jjqz/Rdzw/0fn+v8M2PD/ANTt/wDU7f8A0+z/AMzk/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8As83/AH+b/wB/m/8Af5v/AH+b/wB/m/8wnrj/qOj+/7Tr/v+16/7/tuv+/7fs - /v+16/3/q+n9/5Xk/f+R5P3/jOP8/4fj/P+D4/z/fuL7/3ni+/904fv/cOH7/2vg+v9m4Pr/Yt/6/13f - +v9Y3vn/U975/0/d+f9K3fn/Rd34/0Dc+P883Pj/N9v3/zPb9/9b4vn/f+n6/37p+v9+6fr/fun6/37p - +v9xytv/F4uk/wB/m/8Af5v/AH+b/wB/m/8AgJ3/AMDZ/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8l0uj/TeP1/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Tef6/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACEoFsAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIShnQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8fq8X/AYWi/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xilv/8M1/D/ANTt/wDU7f8A0+z/AM3l/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/ALDK/wB/m/8Af5v/AH+b/wB/m/8Af5v/OaO+/6jo/v+06/7/tev+/7Xr - /v+z6/3/sev9/6fp/f+Q5P3/i+P8/4fj/P+C4vz/feL7/3ni+/904fv/b+H7/2rg+v9m4Pr/Yd/6/1zf - +v9Y3vn/U975/07d+f9K3fj/Rdz4/0Dc+P873Pj/Ntv3/zLb9/8t2vf/NNz3/37p+v9+6fr/fun6/37p - +v9+6fr/e+Hy/0Cgtf8Af5v/AH+b/wB/m/8Af5v/AH+b/wChu/8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/R5v9L4PL/T+j7/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9M5vr/FJ23/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIOgTgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACFoxkAg6HzAIOh/wCDof8Ag6H/AIOh/wCDof8ChqT/Rd3x/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pa7v8bpcD/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ckq//ANTt/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wCxy/8Af5v/AH+b/wB/m/8Af5v/AH+b/zmjvv+o6P7/s+v+/7Tr - /v+x6v3/r+v9/6zq/f+k6f3/i+P8/4bj/P+C4vz/feL7/3jh+/9z4fv/b+H7/2rg+v9l4Pr/Yd/6/1zf - +v9X3vn/Ut75/07d+f9J3fj/RNz4/z/c+P872/j/Ntv3/zLb9/8t2vf/KNr3/yfa9/9n5fn/fun6/37p - +v9+6fr/fun6/37p+v9qwtT/BoKd/wB/m/8Af5v/AH+b/wB/m/8AhJ//AMff/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/GtDm/0ne8P9O5vn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rl+P8RmbT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofUAhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISifgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xumwP9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DW - 6/8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKK+/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8Assv/AH+b/wB/m/8Af5v/AH+b/wB/m/8rmrb/p+f+/7Pr - /v+v6v3/rer9/6rq/f+n6v3/oOj9/4bj/P+B4vz/feL7/3jh+/9z4fv/buD7/2ng+v9l4Pr/YN/6/1vf - +v9X3vn/Ut75/03d+f9J3fj/RNz4/z/c+P862/j/Ndv3/zHb9/8s2vf/J9r3/yfa9/8n2vf/RN/4/37p - +v9+6fr/fun6/37p+v9+6fr/eNvt/yyVrP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCsxf8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8V0Ob/SN3v/03k - 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOP3/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zzR5/8NlLD/AIOh/wCDof8Ag6H/AI2q/wDM5v8A1O3/AM/n/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8AzeX/AM3l/wDN5f8AzeX/ALbP/wB/m/8Af5v/AH+b/wB/m/8Af5v/HZGt/6bn - /v+u6v3/q+r9/6np/f+l6f3/oun9/5/p/f+A4vz/fOL7/3fh+/9y4fv/buD7/2ng+v9k4Pr/YN/6/1vf - +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z/c+P862/j/Ndv3/zHa9/8s2vf/J9r3/yfa9/8n2vf/J9r3/yna - 9/925/r/fun6/37p+v9+6fr/fun6/37p+v9btsj/AH+b/wB/m/8Af5v/AH+b/wB/m/8AjKf/AMvk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xLP - 5f9I3e//TeP1/0/p/P9Q6fz/YOv8/3Ht/f967v3/eu79/3Ht/f9Y4vP/Bomn/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HpAICjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiYACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKb - tv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zjM4/8Lscz/ALTP/wDR6v8A1O3/ANDo/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wC81v8Af5v/AH+b/wB/m/8Af5v/AH+b/w2H - o/+e5v3/qen9/6bp/f+j6f3/oOj9/53p/f+b6P3/gOP7/3fh+/9y4fv/beD7/2jg+v9k4Pr/X9/6/1rf - +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/VeL5/37p+v9+6fr/fun6/37p+v9+6fr/ddXl/xiLpP8Af5v/AH+b/wB/m/8Af5v/AH+b/wC3 - 0P8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8By+P/ONfr/47q9v+28/r/z/n+/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/8Py+f+Cw9L/RKS6/weG - pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6DNAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8c2/L/ANTt/wDU7f8A1O3/ANLq/wDP5/8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Axd3/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/idr0/6To/f+h6f3/nuj9/5vo/f+Y6P3/luj8/4Hj+/9x4fv/beD6/2jg+v9k3/r/X9/6/1rf - +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/zLc9/996fr/fun6/37p+v9+6fr/fun6/37n+P9Epbr/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8AlbD/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/xLQ - 5v9c3u7/qO72/73y+P/C8/n/0Pf8/9H3/P/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5/v/R+f7/yvL4/73f - 5/+u2OH/YLLE/wyJpf8Ag6H/AIOh/wCDof8Ag6H/AIOhyACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuRrv9N5/n/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM3l/wCDn/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/2jH4f+b5/z/nOj8/5ro/f+X5/3/lOj8/5Hn/P+E5fz/bOD6/2fg+v9j3/r/Xt/6/1ne - +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z3c+P842/j/M9v3/y/a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/ZuX5/37p+v9+6fr/fun6/37p+v9+6fr/cMze/wiDnv8Af5v/AH+b/wB/ - m/8Af5v/AICb/wDA2v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/IdPn/3vl - 8f+58fj/vfL4/73y+P+98vj/vfL4/8Pz+f/Q9/z/0ff8/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5 - /v/H7vT/vd/n/73f5/+u2OH/Sqe8/wGDof8Ag6H/AIOh/wCDof8Ag6GxAKqqAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGwAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/K7vS/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8w4fb/ANTt/wDU7f8A1O3/ANPs/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Akqz/AH+b/wB/ - m/8Af5v/AH+b/wB/m/9Is8z/kuX8/5fo/P+U5/3/kef8/47n/P+L5/z/ieb7/3Tj+v9i3/r/Xt/6/1ne - +f9U3vn/T935/0vd+f9G3fj/Qdz4/z3c+P842/f/M9v3/y7a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Lf+P9+6fr/fun6/37p+v9+6fr/fun6/3zk9f8vmK//AH+b/wB/ - m/8Af5v/AH+b/wB/m/8An7n/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8e0+j/feby/7zy - +P+98vj/vfL4/73y+P+98vj/vfL4/73y+P+98vj/xfT6/9H3/P/R9/z/0fn9/9H5/v/R+f7/0fn+/9H5 - /v/R+f7/0fn+/8Lp8P+93+f/vd/n/73f5/+IxdP/E4yo/wCDof8Ag6H/AIOh/wCCopEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKEbAIShTwCBo0UAg6IhAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIahJgCD - ofoAg6H/AIOh/wCDof8Ag6H/AIOh/wWKp/9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5vn/AtTt/wDU7f8A1O3/ANTt/wDR6f8A0en/ANHp/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AKK8/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/IJay/4jk/P+S5vz/kOf8/43m/P+K5vz/h+b7/4Tm+/+C5vv/eOT7/2Dg - +f9U3vn/T935/0rd+f9G3fj/Qdz4/zzc+P832/f/M9v3/y7a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8p2vf/def6/37p+v9+6fr/fun6/37p+v9+6fr/YMDU/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AIKe/wDI4P8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/xPR6P905PH/vPL4/73y - +f+98vn/vfL5/73y+f+98vn/vfL4/7Dw9/+X6/T/g+fy/3Tj8P+G5PH/leby/5/p8/+t7/j/xPj+/9H5 - /v/R+f7/0fn+/9H5/v/Q+f7/v+Tr/73f5/+93+f/vd/n/6rW4P8smLH/AIOh/wCDof8Ag6H/AIOh7AAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGFAIOh+QCDof8Ag6H/AIOh/wCDofkAg6HYAIOhrQCDoHEAhKI0AKqqAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Iq/I/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdfv/wDU7f8A1O3/ANTt/wDS6v8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wC3 - 0P8Af5v/AH+b/wB/m/8Af5v/AH+b/wKBnP912/T/jOb8/4rm/P+I5vz/heX7/4Lm+/9/5fv/feX7/3vl - +v945fr/aOL6/07e+P9F3fj/QNz4/zzc+P832/f/Mtv3/y3a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/1Pi+f9+6fr/fun6/37p+v9+6fr/fun6/3re - 8P8ajKX/AH+b/wB/m/8Af5v/AH+b/wB/m/8AqcP/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/Cc/n/2Lg8P+28fj/vfL5/73y - +f+98vn/vfL5/5jr9f9o3+7/Osne/xayyv8AnLf/AI2p/wCFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wOE - of8YmbH/Ub3Q/4rd6f/C9fv/0fn+/833/f++4Oj/vd/n/73f5/+93+f/ud3l/0Skuv8Ag6H/AIOh/wCD - of8Ag6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKFfAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe8AhKKZAISiNAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIahEwCDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9D2e7/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/FNnw/wDU7f8A1O3/ANTt/wDS6/8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8AzeX/AIKd/wB/m/8Af5v/AH+b/wB/m/8Af5v/RrjT/4Hk+/+F5vz/guX7/3/l+/995fv/euX7/3jk - +v915fr/c+T6/3Dk+v9s5Pr/VuD5/z3c+P822/f/Mtv3/y3a9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8x3Pf/fen6/37p+v9+6fr/fun6/37p - +v9+6fr/SrLG/wB/m/8Af5v/AH+b/wB/m/8Af5v/AIik/wDO5f8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Bzub/Sdzt/6rv9/+98vn/vfL5/73y - +f+Y6/X/V9fo/xi0zP8AlrH/AIOf/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/A4Og/0ezx/+d5e//yPT6/73f5/+93+f/vd/n/73f5/+83ub/VazA/wCD - of8Ag6H/AIOh6QCEoRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HJAISiTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKN0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/GKK9/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LeD1/wDU7f8A1O3/ANTt/wDT7P8A0uv/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wCWsf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/9j0+3/fuT7/33l+/965Pv/eOX7/3Xk - +v9z5Pr/cOT6/27k+f9s4/n/aeT6/2jj+v9f4vn/Qd74/yza9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/2Tk+f9+6fr/fun6/37p - +v9+6fr/fun6/3XX6P8Ig57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Atc7/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8v2Ov/mez1/73z+f+98/n/rO/3/2Pd - 6/8atMz/AI+r/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zCiuf+M2eX/vd/n/73f5/+93+f/vd/n/73f - 5/9YrsH/AIOh/wCDof8Ag6HEAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCodkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhKC6AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKfcACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof87z+X/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/wHU7f8A1O3/ANTt/wDU7f8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8Atc7/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/y2oxP9n3PP/deT6/3Pj - +v9w5Pr/buP6/2vk+f9p4/n/Z+P5/2Tj+v9i4/n/YeP5/17j+f9P4fj/MNz3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9A3vj/fun6/37p - +v9+6fr/fun6/37p+v9+6Pn/NKC3/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJGs/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/xbU6v+A5/P/vfP5/73z+f+T6vT/RMnb/wWZ - tP8AgZ//AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zyovv+o1d//vd/n/73f - 5/+93+f/vd/n/1Gqv/8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAhaFJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIahEwCDoewAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5ey/07o - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/wzX8P8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/AM/p/wCFof8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BoWh/zW1 - 0P9k4Pj/auP6/2jj+f9m4vn/ZOP5/2Hi+f9f4vn/XeL5/1ri+f9a4/n/XOP5/1zj+f9I4Pj/K9v3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/KNr3/3Pn - +v9+6fr/fun6/37p+v9+6fr/fun6/2fM3/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Av9j/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/CdLp/2Li8f+28vj/vPP5/4Lk8P8pts3/AIml/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/Coej/33A - zv+93+f/vd/n/73f5/+23OT/FY2p/wCDof8Ag6H/AISiVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhmgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCopEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8Apb//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/C4yo/zvD3P9e4fn/YeL5/17i+f9c4vn/WuL5/1ji+f9W4vn/WOL5/1rj+f9c4/n/XuP5/1zj - +f9A3vj/KNr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/9R4fn/fun6/37p+v9+6fr/fun6/37p+v995ff/HZCq/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJu2/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8D0un/Sd7v/6rw+P+68/n/edzp/yCowP8AgqD/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCFov8AhqP/AIyo/wCIpv8AhKH/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/UKm9/7nd5f+93+f/vd/n/3m+zv8Ag6H/AIOh/wCDofIAg6MnAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAICjJAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoCMAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/CI6r/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/AMnj/wCLpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/EZay/0DO6P9X4fj/V+H5/1Xh+f9U4vn/VuL5/1fi+f9Z4/n/W+P5/1zj - +f9e4/n/YOT5/1ji+f843fj/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/L9v3/3vo+v9+6fr/fun6/37p+v9+6fr/fun6/1G+0/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCA - nf8AyOH/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wHS6v8/3e//oO73/7fx+P9v0uH/GJqz/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIKg/wCTrv8AoLv/AKzG/wC40v8Axd3/AM7m/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDP - 5/8AxN3/ALrS/wCqxf8Am7X/AIek/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8wmrD/s9rj/73f5/+83ub/KJaw/wCDof8Ag6H/AIOh1ACAmQoAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCi - vv8Aj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodAAgJkKAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ots7/T+n8/0/p/P9P6fz/T+n8/0/o/P8L1u//ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0uv/AK3I/wCEoP8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJz/F6S//0DX8P9R4fn/UuH5/1Ti+f9W4vn/V+L5/1nj - +f9c4/n/XeP5/1/k+f9h5Pn/YuT5/1Hh+f8v2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/9h5Pn/fen6/37p+v9+6fr/fun6/37p+v954fL/CoWg/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AKfB/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/zbc7v+Y7ff/s+31/2bH1/8Sjqn/AIGe/wCBnv8AgZ7/AISg/wCX - sv8AqsT/AL3X/wDQ6P8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDQ6P8Awdr/AKnD/wSPqv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ySTrP+r1uD/vd/n/5LK1/8Bg6H/AIOh/wCDof8Ag6KkAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShNgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCM - qf8A1O3/ANTt/wDI4f8Ap8P/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOghAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaIsAIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/A4el/0ff8/9P6fz/T+n8/0/p/P8s4PX/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Azeb/AKK+/wCA - nf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Cg6D/H7LN/0jc9f9T4vn/VeL5/1bi - +f9Y4vn/WuP5/1zj+f9e4/n/YOT5/2Hk+f9j5Pn/Y+T5/0ng+P8q2/f/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/Pt74/3zp+v9+6fr/fun6/37p+v9+6fr/fun6/zuvxv8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wCGov8A0On/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8C0uv/QN3w/5js9v+r5+//WrnM/wyHo/8AgZ7/AIGe/wCJpv8AoLz/ALnT/wDP - 6P8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/KMrh/x6owv8Cg6H/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/G46o/6zX4P+93+f/QKK5/wCDof8Ag6H/AIOh/wCC - oWoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGoSYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ah6b/ANTt/wDU7f8A1O3/ANTt/wDL5P8AmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofUAgKQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKirACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8eq8T/T+n8/0/p/P9I5/v/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8AxuD/AJey/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Fiqb/K7/a/1Hg - +f9W4vn/V+L5/1nj+f9b4/n/XOP5/17j+f9g5Pn/YuT5/2Tk+f9m5fn/YeT5/0De+P8o2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yja9/9w5vr/fen6/37p+v9+6fr/fun6/37p+v9s1+r/AYCb/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/ALLM/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wfU7f9N4PL/o+v0/57d5/9Jqb7/BYOg/wCBnv8AjKj/AKfC/wDC3P8A0+z/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wbT7P9O5/r/R93x/yay - y/8EhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8hkav/r9ji/6bU3v8GhqP/AIOh/wCD - of8Ag6H5AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wCeuv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKhiwAAAAAAAAAAAAAAAAAAAAAAAAAAAIakKgCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ak7D/A4uo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0DW7P9P6fz/ENjw/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8AvNb/AI2o/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8MlLD/OMzm/1Xi+f9X4vn/WeP5/1vj+f9d4/n/X+T5/2Hk+f9i5Pn/ZOT5/2bl+f9n5fn/W+P5/zbd - +P8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/TuH4/3vo+v996fr/fun6/37p+v9+6fr/fuj6/yKb - tP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCNqP8A0+3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/I9nv/23l8/+r6PD/esnX/zGZsf8BgZ7/AI6r/wCpxP8Aw93/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Etfu/0zn - +v9O5/r/Rt3w/yCrxP8Bg6D/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ymWrv+23OT/Wq/C/wCD - of8Ag6H/AIOh/wCDoeEAhqETAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAImdDQCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8A0On/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ev/AI2q/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoe8AjqoJAAAAAAAAAAAAAAAAAAAAAACDoqEAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/B8Ld/y2/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/NeL3/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AdTt/wHU7f8C1O3/AtTt/wLT6/8Bscr/AIai/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wCAm/8Xor3/Rdfv/1ji+f9a4/n/XOP5/13j+f9g5Pn/YeT5/2Pk+f9l5fn/Z+X5/2jl - +f9q5fn/U+L5/y7b9/8n2vf/J9r3/yfa9/8n2vf/J9r3/y/b9/966Pr/fOn6/33p+v9+6fr/fun6/37p - +v9Yyt//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/ALzW/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W - 7v9M4fL/lOXw/5DX4/9RrsH/DYej/wCSrf8ArMf/AMfg/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8e3PL/T+j7/0/o+/9P6Pv/QdXr/xGXsv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/PaC2/7Pa - 4/8SjKj/AIOh/wCDof8Ag6H/AIKitAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AMzm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC30v8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOiUgAAAAAAAAAAAAAAAACAoh4Ag6H7AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AJe0/y/h9v9O5/v/DZOw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wK91/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU - 7f8C1O3/AtTt/wPV7f8D1e3/BNXt/wTV7f8E1e3/BNXt/wTQ6P8CpsD/AIGd/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGDn/8isMv/UN71/1vj+f9c4/n/XuP5/2Dk+f9h5Pn/ZOT5/2bl - +f9n5fn/aeX5/2vm+v9p5fn/SuD4/yra9/8n2vf/J9r3/zLc9/9g5Pn/eOj6/3vo+v996fr/fun6/37p - +v9+6fr/fOb4/w6Lpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wCXsv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//VOLz/3Tf - 7v900N//QKm//w2MqP8AnLj/ALTP/wDM5v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yzg9f9P6fz/T+j7/0/o+/9O5/r/Lr3U/wOFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv9cr8L/dLvM/wCDof8Ag6H/AIOh/wCDof8Ag6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDN5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/AI2q/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoJ8AAAAAAAAAAAAAAAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wrD3v9O6Pz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak7D/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/A9Xt/wTV - 7f8E1e3/BNXt/wXV7f8F1e3/BtXt/wbV7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wbJ4v8Cm7X/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWIpP8wv9n/V+H4/1zj+f9f5Pn/YeT5/2Lk - +f9k5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9m5fn/Qd74/03h+P9y5/r/duf6/3fo+v956Pr/e+j6/33p - +v9+6fr/fun6/37p+v9CvNP/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJv/AMbg/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDR6v8Xwdn/IbPM/wme - uf8Aob3/ALTO/wDF3/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1e3/OeP4/0/p/P9P6fz/T+n8/0/p/P9C2ez/D5Sv/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AYGe/37Az/8kla7/AIOh/wCDof8Ag6H/AIOh+gCCoTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCs - yP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LSAAAAAAAAAAAAgKoMAIOh7wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCTsP8v4fb/T+n8/0/p/P9P6fz/FZ+5/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDL - 5f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wXV - 7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wjV7v8I1e7/Cdbu/wnW7v8J1u7/Cdbu/wnW7v8J1u7/Ctbu/wfA - 2f8CkKv/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wyTrv8/y+X/XOP5/1/k - +f9h5Pn/Y+T5/2Xl+f9n5fn/aOX5/2rl+f9s5vr/beb6/2/m+v9y5/r/c+f6/3Xn+v936Pr/eOj6/3ro - +v986fr/fen6/37p+v9+6fr/Wdzy/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCkvv8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT6/8AyeL/ANDp/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXW7v9E5vn/T+n8/0/p/P9P6fz/T+n8/03m+f8hrcb/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8MiKT/dbzM/wCDof8Ag6H/AIOh/wCDof8Ag6HgAICcEgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA - pBwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8AyOL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCSkgcAAAAAAIKiaACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Jv9n/Ten8/0/p/P9P6fz/T+n8/0HX7P8BhKL/AIOh/wCDof8Ag6H/AIOh/wCD - of8AzOb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV - 7f8I1e7/CNXu/wnW7v8J1u7/Cdbu/wnW7v8K1u7/Ctbu/wvW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/wzW - 7v8M1u7/DNXt/we1z/8BiKP/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/xae - uP9M1ez/YOT5/2Hk+f9j5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9u5vr/cOb6/3Ln+v9z5/r/def6/3fo - +v946Pr/euj6/33p+v9z5/r/Qd32/xWxzv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ag5//AM/o/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/zDA - 1/8Cg6D/AIGe/wCBnv8AgZ7/AIGe/yqXr/84nrb/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6MnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIem/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wCHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAACCoNUAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Aj6v/LuD1/0/p/P9P6fz/T+n8/0/p/P9P6fz/D5ey/wCDof8Ag6H/AIOh/wCD - of8AmbX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AdTt/wLU7f8C1O3/A9Xt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW - 7v8J1u7/Ctbu/wrW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/w3W7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W - 7v8O1u7/Dtbu/w7W7v8P1+7/DtPq/weqw/8Bg57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AYCd/yatyP9Y3fT/YuT5/2Tk+f9m5fn/Z+X5/2nl+f9r5vr/bOb6/27m+v9x5/r/cuf6/3Tn - +v925/r/d+j6/3fo+v9R4fn/IMHd/waMqP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGu - yP8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vL/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/PNDm/weJpv8AgZ7/AIGe/wCBnv8AgZ//PqG4/wSFov8Ag6H/AIOh/wCDof8Ag6H/AIKgZgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKiPwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCQrf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Ah6b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKfLQCEoD4Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/CLjR/03p+/9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8jsMr/AYak/wCE - ov8An7v/AM7m/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU - 7f8C1O3/AtTt/wPV7f8E1e3/BNXt/wXV7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8J1u7/Ctbu/wvW - 7v8M1u7/DNbu/wzW7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W7v8P1+7/D9fu/xDX7v8Q1+7/ENfu/xDX - 7v8Q1+7/ENfu/xDX7v8R1+7/Edfu/xHX7v8R1+7/D83k/waeuP8AgJv/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/BIah/zS71f9g4vj/ZeX5/2fl+f9o5fn/auX5/2zm+v9t5vr/b+b6/3Hn - +v9z5/r/def6/1/k+f8szun/C5m1/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8BlrD/BNXt/wPV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yrf9f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9D2e7/Co6q/wCBnv8AgZ7/AIGe/wKDof8smLH/AIOh/wCDof8Ag6H/AIOh/wCD - ofQAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACComgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AnLj/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A0uv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoRsAhKCfAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIek/ybb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/POT5/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU - 7f8D1e3/BNXt/wTV7f8F1e3/BtXt/wfV7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/wzW - 7v8N1u7/Dtbu/w7W7v8O1u7/D9fu/w/X7v8Q1+7/ENfu/xDX7v8Q1+7/Edfu/xHX7v8S1+7/Etfu/xPX - 7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/D8Tc/wSSrf8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpCr/0TJ4f9l5fn/Z+X5/2nl+f9r5vr/bOb6/27m - +v9w5vr/Z+X5/zvY8v8QpcL/AYCd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGE - oP8Ersj/B9Pr/wbV7f8E1e3/BNXt/wPV7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/O+T4/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fe8v8Nkq7/AIGe/wCBnv8AgZ7/FI2o/wqIpf8Ag6H/AIOh/wCD - of8Ag6H/AIOhywCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqGNAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKnF/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AMTe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgKQOAIOh8wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKlwf9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ - 8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV - 7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8J1u7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W - 7v8O1u7/D9fu/xDX7v8Q1+7/ENfu/xHX7v8R1+7/Etfu/xPX7v8T1+7/E9fu/xPX7v8U1+7/FNfu/xTX - 7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8W2O//Ftjv/xbY7/8W1+//DrnS/wKJ - pf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/G523/1TV7P9n5fn/aeX5/2vm - +v9q5fn/St/3/xmz0P8ChaD/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wSj - vv8Iz+f/Cdbu/wjV7v8H1e3/B9Xt/wbV7f8F1e3/BNXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW - 7/9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/w+VsP8AgZ7/AIGe/wCCn/8Oiqb/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhuQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC10P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCzzv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HDAIKgXgCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8d0en/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7l - +f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wTV - 7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8K1u7/C9bu/wzW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX - 7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xTX7v8V2O7/Fdju/xXY7v8V2O7/Ftjv/xbY - 7/8X2O//F9jv/xfY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY - 7/8X1Oz/DK3H/wGDn/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8BgJz/Kq3F/2De - 8/9W4vn/JsPe/waOqv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BJey/wvI - 4f8M1u7/DNbu/wvW7v8K1u7/Cdbu/wnW7v8I1e7/B9Xt/wfV7f8G1e3/BNXt/wTV7f8D1e3/AtTt/wHU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/E9nw/03o/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/DJOv/wCBnv8AgZ7/AYOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCCoy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAJmZBQCDoPMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Axd7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOijgCE - obwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aka//PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8b3PL/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wPV7f8E1e3/BdXt/wbV - 7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8O1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX - 7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8W2O//F9jv/xfY7/8Y2O//GNjv/xjY - 7/8Y2O//Gdjv/xnY7/8Z2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY - 7/8a2O//Gtjv/xrY7/8Xz+f/CqG7/wCAnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8CgJz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ci6f/C7vT/xDX - 7v8P1+7/Dtbu/w7W7v8N1u7/DNbu/wzW7v8L1u7/Ctbu/wnW7v8J1u7/B9Xt/wfV7f8G1e3/BdXt/wTV - 7f8D1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8m3vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jh9P8Ika3/AIGe/wCB - oP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HLAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAgqD/AIGf/wCBn/8AgqD/AIOh/wCDof8AiKX/ANTs/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJi0/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D7rU/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9G5/r/AdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wTV7f8F1e3/B9Xt/wfV - 7f8I1e7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W7v8P1+7/ENfu/xDX7v8R1+7/Etfu/xPX - 7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xnY7/8a2O//Gtjv/xrY - 7/8a2O//G9nv/xvZ7/8b2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ - 7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8Xx9//B5ex/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGFoP8Lr8n/E9Xs/xPX - 7v8S1+7/Edfu/xDX7v8Q1+7/D9fu/w7W7v8O1u7/Ddbu/wzW7v8M1u7/Ctbu/wnW7v8J1u7/CNXu/wfV - 7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AdXt/z3k+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R9/0/wKN - qf8AgZ7/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGFAIKg/wCBn/8AgZ7/AICe/wCAnv8AgZ//AJu4/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCNqv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi/yzd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Jt71/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BNXt/wXV7f8H1e3/B9Xt/wjV - 7v8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX7v8Q1+7/Edfu/xLX7v8T1+7/E9fu/xTX - 7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8b2e//HNnv/xzZ - 7/8c2e//HNnv/x3Z7/8d2e//Htnv/x7Z7/8e2e//Htnv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z - 7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x7Z7/8WwNj/BY6o/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wqkvv8V0ej/Ftjv/xXY - 7v8V2O7/FNfu/xPX7v8T1+7/Etfu/xHX7v8Q1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8M1u7/C9bu/wnW - 7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1+//S+j7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P801uz/AIWi/wCCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H0AICfIAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOh4ACCoP8AgZ//AIGe/wCAnv8AgJ3/AH+d/wCs - xv8A0Oj/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AhaP/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGbtv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/wbV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wTV7f8E1e3/BdXt/wfV7f8H1e3/Cdbu/wnW - 7v8K1u7/DNbu/wzW7v8N1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8U1+7/Fdju/xXY - 7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Hdnv/x7Z - 7/8f2e//H9nv/x/Z7/8f2e//H9nv/yDZ7/8g2e//INnv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa - 7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8g2e//INnv/x/Y7f8TtMz/Aoai/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/B5Ww/xXH4P8Z2O//Gdjv/xjY - 7/8Y2O//F9jv/xbY7/8V2O7/Fdju/xTX7v8T1+7/E9fu/xLX7v8Q1+7/ENfu/w/X7v8O1u7/Dtbu/wzW - 7v8M1u7/C9bu/wrW7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xfJ4f8Agp//AIOg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/AIOh/wCCoP8AgZ//AICe/wB/ - nf8AwNj/AM3l/wDM5P8Azeb/ANLq/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJOv/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ywtv/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zLh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Cdbu/wrW - 7v8M1u7/DNbu/w3W7v8O1u7/D9fu/xDX7v8R1+7/Etfu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Ftjv/xjY - 7/8Y2O//GNjv/xnY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/x3Z7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ - 7/8g2e//Idrv/yHa7/8h2u//Idrv/yLa7/8i2u//Itrv/yPa7/8j2u//I9rv/yPa7/8j2u//JNrw/yTa - 8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/I9rv/yPa7/8j2u//I9rv/yLa7/8i2u//Itrv/x/U - 6v8PqMH/AYGd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8EjKf/FLzU/xzZ7/8c2e//G9nv/xvZ - 7/8a2O//Gtjv/xnY7/8Y2O//GNjv/xfY7/8W2O//Fdju/xXY7v8T1+7/E9fu/xLX7v8R1+7/ENfu/w/X - 7v8O1u7/Dtbu/w3W7v8M1u7/C9bu/wnW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wTV7f8C1O3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/OOP4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9I5/r/A7bQ/wCDoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKhOQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ai6n/ANHp/wDO5v8AzOT/AMvj/wDJ4f8AyuL/AM/n/wDT7P8A1O3/ANTt/wC10P8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/Nd/1/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8U2fH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8J1u7/Ctbu/wzW - 7v8M1u7/Dtbu/w7W7v8P1+7/ENfu/xHX7v8S1+7/E9fu/xTX7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY - 7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa - 7/8i2u//Itrv/yPa7/8j2u//JNrw/yTa8P8k2vD/JNrw/yTa8P8l2vD/Jdrw/yXa8P8m2vD/Jtrw/yba - 8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8l2vD/Jdrw/yXa8P8l2vD/JNrw/yTa - 8P8k2vD/JNrw/x7N4/8LnLb/AH+b/wB/m/8Af5v/AH+b/wKFoP8SsMn/H9ft/x/Z7/8f2e//Htnv/x3Z - 7/8c2e//HNnv/xzZ7/8b2e//Gtjv/xrY7/8Z2O//GNjv/xjY7/8W2O//Fdju/xXY7v8U1+7/E9fu/xLX - 7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/C9bu/wnW7v8J1u7/B9Xt/wfV7f8F1e3/BNXt/wPV - 7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX7/9N6fv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yrf9f8Anrn/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ocUA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgwAg6HvAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AKjE/wDU7f8A1O3/ANLr/wDQ6P8AzOT/AMjg/wDI3/8AyN//AMzk/wDS6v8A1O3/AKzI/wCE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5q3/0vo+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9G5/r/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BdXt/wbV7f8H1e3/CNXu/wnW7v8K1u7/DNbu/wzW - 7v8O1u7/Dtbu/w/X7v8Q1+7/Edfu/xPX7v8T1+7/FNfu/xXY7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY - 7/8a2O//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yLa7/8i2u//I9rv/yTa - 8P8k2vD/JNrw/yXa8P8l2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yfb8P8o2/D/KNvw/yjb - 8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yfb8P8n2/D/J9vw/yba - 8P8m2vD/Jtrw/yba8P8m2vD/Jdrw/xzD2/8Hkav/AICc/w6iu/8g0+j/Itrv/yLa7/8h2u//Idrv/yDZ - 7/8f2e//H9nv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8Z2O//GNjv/xjY7/8X2O//Ftjv/xXY - 7v8U1+7/E9fu/xPX7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wbV - 7f8F1e3/BNXt/wLU7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/KN/0/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/Bdbu/wCMqf8Ag6D/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOiUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFqAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wDF3v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8Az+f/AMvj/wDH3v8Axdz/AMjg/wDP - 5/8AxN//AJ67/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISj/xjP5/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Kd/0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BtXt/wfV7f8I1e7/Cdbu/wrW7v8M1u7/DNbu/w7W - 7v8O1u7/D9fu/xDX7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xnY7/8a2O//Gtjv/xvZ - 7/8c2e//HNnv/x7Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa - 8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yjb8P8o2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb8P8p2/D/Ktvw/yrb - 8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb - 8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8n2/D/Jtnv/yHO5f8m2vD/Jdrw/yTa8P8k2vD/JNrw/yPa - 7/8i2u//Idrv/yHa7/8g2e//INnv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8a2O//Gdjv/xjY - 7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xPX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8L1u7/Ctbu/wnW - 7v8I1e7/B9Xt/wXV7f8E1e3/A9Xt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU - 7v9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8AwNv/AIOg/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoccAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKg1QCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCRr/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANDo/wDL - 4/8Axdz/AMXc/wDM5P8A0On/AMHb/wCyzv8ApcH/AIOh/wCDof8Ag6H/AImn/wC10P8y4fb/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wzX8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W - 7v8P1+7/ENfu/xHX7v8T1+7/E9fu/xTX7v8V2O7/Ftjv/xjY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/xzZ - 7/8d2e//Htnv/x/Z7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa8P8m2vD/Jtrw/yfb - 8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/ynb8P8q2/D/Ktvw/yvb8P8r2/D/K9vw/yvb8P8r2/D/LNvw/yzb - 8P8s2/D/LNvw/yzb8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/LNvw/yzb8P8s2/D/LNvw/yvb - 8P8r2/D/K9vw/yvb8P8r2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8m2vD/Jtrw/yba - 8P8l2vD/JNrw/yTa8P8j2u//Itrv/yHa7/8h2u//INnv/x/Z7/8f2e//Htnv/x3Z7/8c2e//HNnv/xrY - 7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW - 7v8L1u7/Cdbu/wnW7v8H1e3/BtXt/wXV7f8E1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J6Pv/A9Xu/wCeuv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AISfOAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKg/wCDof8Ar8v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDQ6P8AyuL/AMTb/wDI3/8A0Oj/ANTt/wDU7f8AyOL/AMTe/wDQ6f8B1e3/Suf7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Dl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8G1e3/B9Xt/wjV7v8J1u7/C9bu/wzW7v8N1u7/Dtbu/w/X - 7v8Q1+7/Edfu/xLX7v8T1+7/FNfu/xXY7v8W2O//GNjv/xjY7/8Z2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z - 7/8f2e//H9nv/yDZ7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/Jtrw/yba8P8n2/D/KNvw/ynb - 8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb8P8s2/D/LNvw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ltzw/y7c - 8P8u3PD/Ltzw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y7c8P8u3PD/Ltzw/y7c - 8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/ynb - 8P8o2/D/J9vw/yba8P8m2vD/Jdrw/yTa8P8k2vD/I9rv/yLa7/8h2u//Idrv/yDZ7/8f2e//H9nv/x3Z - 7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8Q1+7/ENfu/w7W - 7v8O1u7/DNbu/wzW7v8K1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f9B5fr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yLd9P8AyuP/AISi/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAACDobsAg6H/AIOh/wCD - of8Ag6H/AIOh/wCCoP8AhqP/AM3m/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ur/AMri/wDI4P8AzeX/ANTt/wDU7f8A1O3/Etnw/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8n3vX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8K1u7/DNbu/wzW7v8O1u7/Dtbu/xDX - 7v8R1+7/Etfu/xPX7v8U1+7/Fdju/xbY7/8Y2O//GNjv/xrY7/8a2O//G9nv/xzZ7/8d2e//Htnv/x/Z - 7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/yrb - 8P8r2/D/K9vw/yzb8P8s2/D/Ldzw/y3c8P8t3PD/Ltzw/y7c8P8v3PD/L9zw/zDc8P8w3PD/MNzw/zDc - 8P8w3PD/MNzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/MNzw/zDc - 8P8w3PD/MNzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/K9vw/yvb - 8P8r2/D/Ktvw/ynb8P8p2/D/KNvw/yfb8P8m2vD/Jtrw/yXa8P8k2vD/JNrw/yPa7/8i2u//Idrv/yDZ - 7/8f2e//H9nv/x7Z7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Fdju/xXY7v8T1+7/E9fu/xHX - 7v8Q1+7/D9fu/w7W7v8N1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/AdTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5Pn/ANTt/wCe - u/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H4AICkDgAAAAAAAAAAAAAAAACCojcAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqD/AKW//wDS6v8A0en/ANHp/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDN5v8A0Oj/ANPs/yfe - 9f9P6fz/T+n8/zvP5v8ms83/JbPM/yq50f8uvdX/CbfR/wC51P8Avdj/AMDb/wDE3/8AyeL/AM3n/wDQ - 6f8A1O3/AdTt/wLU7f8D1e3/BNXt/wbV7f8H1e3/CNXu/wnW7v8L1u7/DNbu/w3W7v8O1u7/D9fu/xDX - 7v8S1+7/E9fu/xTX7v8V2O7/Ftjv/xfY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/x3Z7/8e2e//H9nv/yDZ - 7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb - 8P8s2/D/Ldzw/y3c8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zDc8P8w3PD/Mdzw/zHc8P8y3PH/Mtzx/zLc - 8f8y3PH/Mtzx/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd - 8f8y3PH/Mtzx/zLc8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c - 8P8t3PD/Ldzw/yzb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/yjb8P8n2/D/Jtrw/yba8P8l2vD/JNrw/yPa - 7/8i2u//Idrv/yHa7/8f2e//H9nv/x7Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xjY7/8W2O//Fdju/xTX - 7v8T1+7/Etfu/xHX7v8Q1+7/Dtbu/w7W7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8F1e3/BNXt/wLU - 7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f9E5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wvX - 8P8AxN7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAAAAAAg6GzAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIKg/wDG4f8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDQ6P8A0ur/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f895Pn/T+n8/yKvyf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCIpf8Ai6j/AY6s/wGUsP8Cl7T/A5q3/wOeuv8Fo77/BabD/weqxf8IsMr/CbPN/wq2 - 0f8Nu9X/Dr/Z/xDE3P8SyOD/FMzk/xbR6P8Y1Ov/Gtjv/xvZ7/8c2e//Hdnv/x/Z7/8f2e//INnv/yHa - 7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/Kdvw/ynb8P8q2/D/K9vw/yvb8P8s2/D/Ldzw/y3c - 8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8y3PH/M93x/zPd8f803fH/NN3x/zXd - 8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd - 8f813fH/Nd3x/zXd8f813fH/Nd3x/zTd8f803fH/M93x/zPd8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc - 8P8w3PD/L9zw/y/c8P8u3PD/Ldzw/y3c8P8s2/D/K9vw/yvb8P8q2/D/Kdvw/ynb8P8o2/D/Jtrw/yba - 8P8l2vD/JNrw/yTa8P8i2u//Idrv/yHa7/8f2e//H9nv/x3Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xfY - 7/8W2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wXV - 7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8m3vX/ANTt/wCXtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GoAAAAAAAAAAAAgqExAIOh/gCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCbuP8A0+z/ANLq/wDR6f8A0Oj/AM/n/wDP5/8Azub/AM3m/wDN - 5v8Az+f/ANLr/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8E1e7/Tuj8/0DW6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8CiKX/A4uo/wSPrP8GlLH/CJi0/wqc - t/8MoLv/DqXA/xCoxP8SrMb/FbLL/xe1zv8ZudL/H8Xd/yva7/8r2/D/LNvw/y3c8P8t3PD/Ltzw/y/c - 8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zXd8f813fH/Nt3x/zbd - 8f833fH/N93x/zfd8f833fH/N93x/zfd8f833fH/ON3x/zjd8f843fH/ON3x/zjd8f843fH/ON3x/zfd - 8f833fH/N93x/zfd8f833fH/N93x/zfd8f823fH/Nt3x/zXd8f813fH/Nd3x/zXd8f803fH/M93x/zPd - 8f8y3PH/Mtzx/zHc8P8x3PD/MNzw/zDc8P8v3PD/Ltzw/y3c8P8t3PD/LNvw/yvb8P8r2/D/Kdvw/ynb - 8P8o2/D/J9vw/yba8P8l2vD/JNrw/yTa8P8i2u//Idrv/yDZ7/8f2e//H9nv/x3Z7/8c2e//G9nv/xrY - 7/8Z2O//GNjv/xfY7/8V2O7/FNfu/xPX7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8L1u7/Cdbu/wjV - 7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v9M6Pv/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/QOX6/wDU7f8AtdD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wAAAAAAAAAAAIOgrwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aw93/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN - 5v8AzeX/AMzk/wDL4/8AzeX/ANDo/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/GNrx/0/p/P8wwdj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fjqr/Isfd/y7c8P8v3PD/MNzw/zDc - 8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f833fH/ON3x/zjd - 8f843fH/Od7x/zne8f853vH/Od7x/zne8f853vH/Ot7x/zre8f863vH/Ot7x/zre8f863vH/Ot7x/zre - 8f863vH/Od7x/zne8f853vH/Od7x/zne8f853vH/ON3x/zjd8f843fH/N93x/zfd8f833fH/Nt3x/zXd - 8f813fH/Nd3x/zTd8f8z3fH/M93x/zLc8f8y3PH/Mdzw/zDc8P8w3PD/L9zw/y7c8P8t3PD/LNvw/yvb - 8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ7/8f2e//Htnv/x3Z - 7/8c2e//Gtjv/xrY7/8Y2O//F9jv/xbY7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8O1u7/DNbu/wvW - 7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/NeL3/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8H1u7/AM7m/wCDov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AAAAAAIKiPwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A0uv/ANHp/wDO5v8AzOT/AMri/wDK4v8AyeH/AM3l/wDR6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yzg9f9P6fz/PdLo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSLqP8v2+7/Mdzw/zLc - 8f8y3PH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Od7x/zre - 8f863vH/O97x/zve8f873vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze - 8f883vH/PN7x/zze8f883vH/PN7x/zze8f873vH/O97x/zve8f863vH/Ot7x/zne8f853vH/Od7x/zjd - 8f843fH/N93x/zfd8f823fH/Nd3x/zXd8f813fH/NN3x/zPd8f8y3PH/Mtzx/zHc8P8w3PD/L9zw/y7c - 8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ - 7/8f2e//Hdnv/xzZ7/8b2e//Gtjv/xnY7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/D9fu/w7W - 7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xrb8v9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/GNrx/wDU7f8AlrP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICAAgCD - oc4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AMDb/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0en/AM7m/wDL4/8AyOD/AMnh/wDN5v8A0uv/ANTt/wDU - 7f8A1O3/ANTt/wDU7f895Pn/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/KMrh/zPd - 8f803fH/Nd3x/zXd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/PN7x/zze - 8f883vH/PN7x/z3e8f893vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e - 8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z3e8f893vH/PN7x/zze8f883vH/PN7x/zve - 8f863vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/N93x/zbd8f813fH/Nd3x/zTd8f8y3PH/Mtzx/zHc - 8P8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa - 7/8h2u//INnv/x/Z7/8e2e//Hdnv/xzZ7/8a2O//Gtjv/xjY7/8X2O//Fdju/xXY7v8T1+7/Etfu/xDX - 7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8A1O3/AKfD/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC - oWIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDS6v8Azub/AMvj/wDM - 5P8A0Oj/ANTt/wDU7f8C1O7/Ten8/0/p/P9P6fz/Tef5/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y/T - 6f813fH/Nt3x/zfd8f833fH/ON3x/zne8f853vH/Ot7x/zve8f873vH/PN7x/zze8f893vH/Pd7x/z7e - 8f8+3vH/Pt7x/z/f8f8/3/H/P9/x/0Df8v9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf - 8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9A3/L/QN/y/0Df8v8/3/H/P9/x/z7e8f8+3vH/Pt7x/z7e - 8f893vH/PN7x/zze8f883vH/O97x/zre8f853vH/Od7x/zjd8f833fH/N93x/zbd8f813fH/Nd3x/zTd - 8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/Jtrw/yba - 8P8k2vD/I9rv/yLa7/8h2u//INnv/x/Z7/8d2e//HNnv/xvZ7/8a2O//GNjv/xjY7/8W2O//Fdju/xPX - 7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k - +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P834/j/ANTt/wC10f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wDE3v8A1O3/ANPs/wDS6/8A0+z/ANPs/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANLr/wDR6f8A0+z/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/M8Xb/wOHpf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP82yd//O8/m/zjM4v81x97/MMHY/y291P8quND/JbPM/yGu - x/8eqsT/GqW//xafuv8Tm7b/D5ey/wuRrv8HjKn/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKi - vP833fH/N93x/zjd8f853vH/Od7x/zre8f873vH/PN7x/zze8f893vH/Pt7x/z7e8f8+3vH/P9/x/z/f - 8f9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Lf8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf - 8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Lf8v9C3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Df - 8v9A3/L/P9/x/z7e8f8+3vH/Pt7x/z3e8f883vH/PN7x/zve8f863vH/Od7x/zne8f843fH/N93x/zfd - 8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yvb8P8r2/D/Kdvw/ynb - 8P8n2/D/Jtrw/yXa8P8k2vD/I9rv/yHa7/8g2e//H9nv/x7Z7/8c2e//G9nv/xrY7/8Z2O//GNjv/xbY - 7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8t4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+f6/wDU7f8AwNr/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wChvf8A1O3/ANTt/wDT7P8A0uv/ANLr/wDS - 6v8A0en/ANHp/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B2O3/AZCt/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Boqo/znM4/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/0fe8v9E2u//P9Tq/zvP - 5v84zOL/Ncfe/zDB2P8tvdT/KrjQ/yWzzP8hrsf/HqrE/xqlv/8Wn7r/Epu2/w+Xsv8Ag6H/CJCt/xuw - yf812e7/Od7x/zne8f863vH/O97x/zze8f883vH/Pd7x/z7e8f8+3vH/P9/x/0Df8v9A3/L/Qd/y/0Hf - 8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9E3/L/RN/y/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg - 8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9E3/L/RN/y/0Tf8v9D3/L/Q9/y/0Pf - 8v9C3/L/Qt/y/0Hf8v9B3/L/Qd/y/0Df8v8/3/H/Pt7x/z7e8f893vH/PN7x/zze8f873vH/Ot7x/zne - 8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y7c8P8t3PD/LNvw/yvb - 8P8q2/D/Kdvw/yjb8P8m2vD/Jtrw/yTa8P8j2u//Itrv/yHa7/8f2e//Htnv/x3Z7/8c2e//Gtjv/xnY - 7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV - 7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/H9zz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8G1e7/AMHb/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCHpP8AzOX/ANTt/wDU7f8A0+z/ANLr/wDS - 6/8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDP5/8A0Oj/ANLq/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8w4fb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sej7/wDQ - 6f8Anrr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/J7XO/07o+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PeDz/zjd - 8f853vH/Od7x/zve8f883vH/PN7x/z3e8f8+3vH/Pt7x/z/f8f9A3/L/Qd/y/0Hf8v9C3/L/Q9/y/0Pf - 8v9D3/L/RN/y/0Tf8v9F4PL/ReDy/0Xg8v9G4PL/RuDy/0bg8v9H4PL/R+Dy/0fg8v9I4PL/SODy/0jg - 8v9I4PL/SODy/0jg8v9I4PL/SODy/0jg8v9I4PL/R+Dy/0fg8v9H4PL/R+Dy/0bg8v9G4PL/ReDy/0Xg - 8v9F4PL/RN/y/0Tf8v9D3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9A3/L/P9/x/z7e8f8+3vH/Pd7x/zze - 8f883vH/O97x/zne8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y7c - 8P8t3PD/LNvw/yvb8P8p2/D/Kdvw/yfb8P8m2vD/KNvw/yrb8P8r2/D/J9vv/yPa7/8f2e//Hdnv/xzZ - 7/8a2O//Gdjv/xjY7/8X2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV - 7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DNjw/wDG3/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ArMj/ANTt/wDU7f8A1O3/ANPs/wDS - 6/8A0ur/ANLq/wDR6f8A0Oj/ANDo/wDP5/8Azub/AM3m/wDN5v8AzeX/AM3m/wDP5/8A0ur/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zvj - +f8A1O3/ANTt/wCxzP8AhKP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Wn7r/SOH0/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/zre - 8f863vH/O97x/zze8f883vH/Pt7x/z7e8f8/3/H/QN/y/0Hf8v9B3/L/Qt/y/0Pf8v9D3/L/RN/y/0Xg - 8v9F4PL/ReDy/0bg8v9H4PL/R+Dy/0jg8v9I4PL/SODy/0jg8v9J4PL/SeDy/0ng8v9K4PL/SuDy/0rg - 8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9J4PL/SeDy/0ng8v9I4PL/SODy/0jg - 8v9I4PL/R+Dy/0fg8v9G4PL/ReDy/0Xg8v9F4PL/RN/y/0Pf8v9D3/L/Qt/y/0Hf8v9B3/L/QN/y/z/f - 8f8+3vH/Pt7x/zze8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8z3fH/Mtzx/zHc - 8P8w3PD/Nd3w/0Lf8f9L4PL/VOLz/13k9P9k5fT/aOb0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuT0/1zj - 8/9T4vP/SODy/zze8f8w3PH/Htnv/xfY7/8V2O7/FNfu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW - 7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xHY8P8Axt//AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/ANLr/wDU7f8A1O3/ANTt/wDU - 7f8A0+z/ANPs/wDS6/8A0ur/ANHp/wDQ6P8Az+f/AM7m/wDN5v8Azeb/AM3l/wDM5P8Ay+P/AMvj/wDL - 4/8AzeX/AM/n/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8u4PX/ANTt/wDU7f8A1O3/AMLc/wCLqf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmP - rP8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nm - +f873vH/PN7x/z3e8f8+3vH/Pt7x/0Df8v9B3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Tf8v9F4PL/ReDy/0bg - 8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9K4PL/SuDy/0rg8v9K4PL/S+Hy/0vh8v9M4fL/TOHy/0zh - 8v9M4fL/TOHy/0zh8v9N4fL/TeHy/0zh8v9M4fL/TOHy/0zh8v9M4fL/TOHy/0vh8v9L4fL/SuDy/0rg - 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Lf - 8v9B3/L/Qd/y/z/f8f8+3vH/Pt7x/z3e8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nt3x/0Pg - 8v9V4vP/ZeX0/27m9P9t5vT/bOb0/2zm9P9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9l5fT/ZeX0/2Pl - 9P9i5PT/YuT0/2Dk9P9g5PT/X+T0/17k9P9R4vP/P9/y/ybb7/8V1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W - 7v8M1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/D9jv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8W2vH/AMLc/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/AMXe/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDN - 5v8AzOT/AMri/wDI4P8AyOD/AMnh/wDN5f8A0Oj/ANPs/wfW7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wDU7f8Azub/AJm1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AoWj/y691f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9F4/f/Pd7x/z7e8f8/3/H/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9E3/L/ReDy/0Xg8v9G4PL/R+Dy/0jg - 8v9I4PL/SeDy/0rg8v9K4PL/SuDy/0vh8v9M4fL/TOHy/03h8v9N4fL/TeHy/03h8v9N4fL/TuHz/07h - 8/9O4fP/TuHz/0/h8/9P4fP/T+Hz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TuHz/07h8/9N4fL/TeHy/03h - 8v9N4fL/TeHy/0zh8v9L4fL/S+Hy/0rg8v9K4PL/SeDy/0ng8v9I4PL/SODy/0fg8v9G4PL/ReDy/0Xg - 8v9E3/L/Q9/y/0Lf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/z3e8f883vH/O97x/zne8f9C3/L/V+Lz/2zm - 9f9x5/X/cef1/3Dn9f9v5vX/bub0/27m9P9s5vT/bOb0/2vm9P9q5vT/aeb0/2nm9P9n5vT/Z+X0/2Xl - 9P9l5fT/Y+X0/2Lk9P9i5PT/YeT0/2Dk9P9f5PT/XuT0/13k9P9c5PT/U+Lz/zrd8f8d2e//Edfu/xDX - 7v8O1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wvX7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/G9zy/wC6 - 1P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AK7J/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN5f8Q0+n/TuX4/0/o+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/xja8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7P8Aq8b/AISi/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/G6XA/0vk+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/QuHz/z/f8f9A3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Xg8v9F4PL/RuDy/0fg8v9I4PL/SODy/0ng - 8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h8v9N4fL/TuHz/07h8/9P4fP/T+Hz/0/h8/9P4fP/UOHz/1Dh - 8/9Q4fP/UOHz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UOHz/1Dh8/9Q4fP/T+Hz/0/h - 8/9P4fP/T+Hz/0/h8/9O4fP/TeHy/03h8v9N4fL/TOHy/0vh8v9L4fL/SuDy/0rg8v9J4PL/SODy/0jg - 8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/0rg8v9l5fT/dej1/3To - 9f9z5/X/c+f1/3Ln9f9x5/X/cef1/2/m9f9u5vT/bub0/23m9P9s5vT/a+b0/2rm9P9p5vT/aeb0/2fm - 9P9n5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Lk9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk9P9b5PP/WuPz/0Tf - 8v8h2u//D9fu/w7W7v8M1u7/C9bu/wnW7v8I1e7/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8N1/D/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xja - 8v8Asc3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8P2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wO91v8Biaf/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Mk7D/Qtnt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Tun8/0Df8v9B3/L/Qt/y/0Pf8v9E3/L/ReDy/0Xg8v9H4PL/SODy/0jg8v9J4PL/SuDy/0rg - 8v9L4fL/TOHy/03h8v9N4fL/TuHz/0/h8/9P4fP/T+Hz/1Dh8/9Q4fP/UeLz/1Li8/9S4vP/UuLz/1Li - 8/9S4vP/UuLz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9S4vP/UuLz/1Li - 8/9S4vP/UuLz/1Hi8/9R4vP/UOHz/0/h8/9P4fP/T+Hz/07h8/9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg - 8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/UeLz/2vm9P946PX/d+j1/3bo - 9f916PX/dej1/3To9f9z5/X/c+f1/3Hn9f9x5/X/cOf1/2/m9f9u5vT/bub0/2zm9P9s5vT/aub0/2nm - 9P9p5vT/Z+b0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuT0/2Hk9P9g5PT/X+T0/17k9P9d5PT/W+T0/1vk - 8/9Z4/P/WePz/0fg8v8j2u//Dtbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8T2fD/AKjE/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AImo/wDK5P8A1O3/ANPs/wDS - 6v8A0en/ANHp/wDP5/8Az+f/AM/n/wDO5v8Azub/AM7m/wDO5v8Azub/AM/n/wDP5/8A0Oj/ANHp/wDS - 6v8A0uv/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yLe9P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/BtXu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8E1e3/Es3l/wSU - sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSIpf8zxdz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0zn+v9C3/L/Q9/y/0Tf8v9F4PL/RuDy/0fg8v9I4PL/SeDy/0rg8v9K4PL/S+Hy/0zh - 8v9N4fL/TeHy/07h8/9P4fP/T+Hz/1Dh8/9R4vP/UuLz/1Li8/9S4vP/U+Lz/1Pi8/9U4vP/VOLz/1Ti - 8/9U4vP/VOLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VOLz/1Ti - 8/9U4vP/VOLz/1Ti8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Hi8/9Q4fP/T+Hz/0/h8/9P4fP/TuHz/03h - 8v9N4fL/S+Hy/0rg8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg8v9M4PP/aeb1/3rp9v966fb/eun2/3jp - 9f946PX/d+j1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn9f9w5/X/b+b1/23m9P9m5fT/YOT0/13k - 8/9a4/P/WuPz/1zk9P9g5PT/ZeX0/2bl9P9l5fT/ZOX0/2Pl9P9i5PT/YeT0/2Dk9P9e5PT/XuT0/13k - 9P9b5PT/W+Tz/1nj8/9Z4/P/V+Pz/z/e8f8X2O//DNbu/wrW7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Dtfv/wCYtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wC/2v8A1O3/ANTt/wDS - 6/8A0ur/ANHp/wDP5/8Azub/AM3m/wDM5P8Ay+P/AMri/wDI4P8AyN//AMfe/wDF3P8AxNv/AMPa/wDC - 2f8Awdj/AMDX/wC+1f8AvtX/AL7V/wC+1f8Av9b/AMDX/wDA1/8Awdj/AMPZ/wDH3v8p1ur/TeT3/0/o - +/9P6fz/T+n8/0/p/P9P6fz/TOj7/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BdXt/xXY - 7v8X1uz/CqbA/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGvyP9N5/n/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9J5Pf/RN/y/0Xg8v9G4PL/R+Dy/0jg8v9J4PL/SuDy/0rg8v9M4fL/TeHy/03h - 8v9O4fP/T+Hz/0/h8/9Q4fP/UeLz/1Li8/9S4vP/U+Lz/1Ti8/9U4vP/VOLz/1Xi8/9V4vP/VuPz/1bj - 8/9W4/P/VuPz/1fj8/9X4/P/V+Pz/1fj8/9X4/P/WOPz/1jj8/9Y4/P/V+Pz/1fj8/9X4/P/V+Pz/1fj - 8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li8/9R4vP/UOHz/0/h - 8/9P4fP/TuHz/03h8v9N4fL/TOHy/0vh8v9K4PL/SeDy/0ng8v9g5PT/e+n2/3zp9v986fb/e+n2/3rp - 9v966fb/een2/3jo9f946PX/duj1/3Xo9f916PX/cuj1/2Ti9f9c1fT/Usjz/0+99P9NsfT/UKv0/1Ko - 9f9TpfX/VKT1/1Ol9f9RqPX/Tqv0/0ux8/9JvPP/S8bz/0/T8/9V4PP/YOT0/2Lk9P9g5PT/YOT0/17k - 9P9e5PT/XeT0/1vk9P9b5PP/WePz/1jj8/9X4/P/VOLz/y7c8P8N1u7/Cdbu/wjV7v8H1e3/BdXt/wPV - 7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8d3PL/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/wjW7v8AiKX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCzzv8A1O3/ANTt/wDU - 7f8A0uv/ANLq/wDR6f8Az+f/AM7m/wDN5f8AzOT/AMvj/wDJ4f8AyOD/AMjf/wDH3v8Axdz/AMTb/wDD - 2f8Awtn/AMLZ/wDD2f8Aw9r/AMXc/wDH3v8AyOD/AMri/wDN5f8Az+f/ANLq/wDU7f8A1O3/NOH3/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pl+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXV - 7f8X2O//Gdjv/xrY7/8Su9P/AYek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0bd - 8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/SOP0/0bg8v9H4PL/SODy/0ng8v9K4PL/S+Hy/0zh8v9N4fL/TuHz/0/h - 8/9P4fP/UOHz/1Hi8/9S4vP/UuLz/1Pi8/9U4vP/VOLz/1Xi8/9W4/P/VuPz/1bj8/9X4/P/WOPz/1jj - 8/9Z4/P/WePz/1nj8/9Z4/P/WePz/1nj8/9a4/P/WuPz/1rj8/9a4/P/WuPz/1rj8/9a4/P/WePz/1nj - 8/9Z4/P/WePz/1nj8/9Z4/P/WOPz/1jj8/9X4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9U4vP/U+Lz/1Li - 8/9S4vP/UeLz/1Dh8/9P4fP/T+Hz/07h8/9N4fL/TOHy/1Di8v9y5/X/f+n2/3/p9v9+6fb/fen2/3zp - 9v986fb/e+n2/3rp9v966fb/eOn1/3Tn9f9j3/T/Vcnz/06x9P9So/T/U6L0/1Oi9P9TovT/U6L0/1Oi - 9P9TovT/U6L0/1Oi9P9TovT/U6L0/1Oi9P9TovT/UqLz/1Ki8/9SovP/UaPz/0ew8v9HxvL/Tdzz/13k - 9P9g5PT/XuT0/17k9P9d5PT/W+Tz/1rj8/9Z4/P/WOPz/1fj8/9W4vP/Qd/y/xLX7v8J1u7/B9Xt/wbV - 7f8E1e3/A9Xt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07o/P8Cwt7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7gCmwv8A1O3/ANTt/wDU - 7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDO5v8AzeX/AM3l/wDM5P8AzeX/AM3l/wDN5v8Azub/AM/n/wDQ - 6P8A0ur/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk - +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8G1e3/GNjv/xrY7/8c2e//Hdnv/xrL4v8FkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Giqj/Oczj/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8v9I4PL/SeDy/0rg8v9L4fL/TOHy/03h8v9O4fP/T+Hz/1Dh - 8/9R4vP/UuLz/1Li8/9T4vP/VOLz/1Ti8/9V4vP/VuPz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WePz/1rj - 8/9a4/P/W+Pz/1vj8/9b4/P/W+Pz/1vj8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk - 8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj8/9a4/P/WePz/1nj8/9Z4/P/WOPz/1fj8/9W4/P/VuPz/1Xi - 8/9U4vP/VOLz/1Pi8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/1rj9P9+6fb/ger2/4Hq9v+A6fb/f+n2/3/p - 9v9+6fb/fOn2/3zp9v976fb/c+f1/1zT9P9PtPL/UqLz/1Kh8/9SofP/UqHz/1Kh8/9RofL/UaHy/1Gh - 8v9RofL/UaHy/1Gh8v9RofL/UaHy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Ch - 8v9GsfL/Rs7y/1fi8/9e5PT/XeT0/1zk9P9b5PP/WuPz/1nj8/9X4/P/V+Pz/1bi8/9P4fP/Gtnv/wnW - 7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zHh9v9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9D5fn/AKvG/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA1Oz/ANTt/wDU - 7f8A1O3/ANPs/wDT7P8A0uv/ANPs/wDT7P8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDP - 6f8Avtn/AKbC/wDH4f8A0er/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/B9Xt/xrY7/8c2e//Hdnv/x/Z7/8g2e//H9Xr/wygu/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wGEov8ntc7/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03n+v9I4PL/SuDy/0vh8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi - 8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj8/9W4/P/V+Pz/1jj8/9Z4/P/WePz/1rj8/9b4/P/W+Pz/1vj - 8/9c5PP/XOTz/13k9P9d5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k - 9P9e5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj - 8/9X4/P/VuPz/1bj8/9V4vP/VOLz/1Ti8/9S4vP/UuLz/2Hl9P+B6fb/g+r3/4Lq9/+B6vb/ger2/3/p - 9v9/6fb/fun2/33p9v946Pb/Xs7z/0+r8f9QoPH/UKDx/1Cg8f9QoPH/UJ/x/1Cf8f9Qn/H/UJ/x/1Cf - 8f9Qn/H/UJ/x/1Cf8f9Qn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/D/T5/w/0+f - 8P9Pn/D/T5/w/0+f8P9IqO//R8nx/1ni8/9c4/T/W+P0/1rj8/9Y4/P/WOPz/1bj8/9V4vP/VOLz/1Hi - 8/8g2vD/CNXu/wfV7f8F1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8+5fn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/NuL3/wCTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqCBANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCz - zf8AiKb/AIOh/wCDof8Ag6H/AIOh/wCKp/8Al7T/AKTA/wCxzP8Avtj/AMrk/wDU7P8A1O3/ANTt/wDU - 7f8A1O3/POT5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zji+P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wbV7f8b2e//Hdnv/x/Z7/8g2e//Idrv/yPa7/8k2vD/FbTO/wGEo/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xafuv9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5ff/SuDy/0zh8v9N4fL/TuHz/0/h8/9Q4fP/UeLz/1Li - 8/9T4vP/VOLz/1Xi8/9W4/P/VuPz/1fj8/9Y4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XeT0/17k - 9P9e5PT/XuT0/1/k9P9f5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk - 9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/W+Pz/1vj - 8/9a4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/2fl9P+C6vb/g+r2/4Lq9v+B6fb/gen2/3/p - 9v9/6fb/fun2/3zp9v9m1vT/Uarw/0+e8P9PnvD/T57w/0+e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e - 8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06d7/9One//Tp3v/06d - 7/9One//Tp3v/06d7/9One//TZ3v/02d7/9Kp+//StDy/1jj8/9Y4/T/V+Pz/1bi8/9U4vP/U+Lz/1Li - 8/9R4vP/T+Hz/yXb8P8H1e3/BtXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S+j7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/ynb8P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgOwDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC2 - 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/AIyq/wCa - t/8AudP/ANLq/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P844vj/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8E1e3/HNnv/x7Z7/8g2e//Idrv/yPa7/8k2vD/Jtrw/yjb8P8gx97/BYyp/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY+s/z7T6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOP0/03h8v9N4fL/T+Hz/0/h8/9R4vP/UuLz/1Pi - 8/9U4vP/VeLz/1bj8/9W4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/XOTz/13k9P9e5PT/XuT0/1/k - 9P9g5PT/YOT0/2Dk9P9h5PT/YeT0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9j5fT/Y+X0/2Pl - 9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k - 9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Y4/P/V+Pz/2bm9P+C6vb/gur2/4Lq9v+B6vb/ger2/3/p - 9v9/6fb/fun2/3jk9f9Xt/H/TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d - 7/9Nne//TJzu/0yc7v9Mne7/VJ/r/1qh6f9couj/WqHp/1Sf6/9Mne7/TJzu/0yc7v9MnO7/TJzu/0yc - 7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9Js+//Ut3z/1Tj8/9U4vP/U+Lz/1Hi - 8/9Q4vP/T+Hz/07h8/9L4fP/Idrw/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Dtfv/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cxd3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCA - gAIA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDA - 2/8AhaP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCZtf885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/A9Xt/x7Z7/8f2e//Idrv/yPa7/8k2vD/Jtrw/yfb8P8p2/D/K9vw/yjU - 6f8Mm7b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ChaP/Lr3V/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03h8v9O4fP/T+Hz/1Hi8/9S4vP/U+Lz/1Ti - 8/9V4vP/VuPz/1fj8/9Y4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Dk - 9P9h5PT/YuX0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl - 9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl9P9k5fT/ZOX0/2Pl9P9j5fT/YuX0/2Ll9P9h5PT/YeT0/2Dk - 9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/2bl9P+D6vb/g+r2/4Lq9v+C6vb/ger2/4Dq - 9v9/6vb/f+r2/27X8/9Po+//TJzu/0yc7v9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0uc - 7f9gouX/fK3b/5i20f+pvcv/usPG/73ExP+9xMT/vcTE/73ExP+9xMT/usPG/6q9y/+XttH/fK3b/2Ci - 5f9LnO3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprs/0ih7P9L0vH/UuLy/1Hi - 8v9Q4vL/T+Ly/03h8v9N4fL/S+Hy/0nh8v8b2O//BtXt/wTV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd - 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DqK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oaAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDI - 4/8Aiaj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Otzx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrk+P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb8P8p2/D/Ktvw/yzb - 8P8t3PD/Ltvv/xivyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8bpsD/S+T4/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/n+v9P4fP/UOHz/1Li8/9S4vP/VOLz/1Ti - 8/9W4/P/VuPz/1jj8/9Z4/P/WuPz/1vj8/9c5PP/XeT0/17k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll - 9P9i5fT/Y+X0/2Tl9P9l5fT/ZeX0/2Xl9P9m5fT/ZuX0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl - 9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2bl9P9l5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Pl - 9P9i5fT/YuX0/2Hk9P9g5PT/YOT0/17k9P9e5PT/XeT0/2Xl9P+C6vb/g+r2/4Pq9v+C6vb/ger2/4Dq - 9v+A6fb/f+n2/2bL8v9Km+3/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/X6Ll/4qy - 1v+xwMj/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/scDI/4uy1f9fouT/SZnr/0mZ6/9Jmev/SZnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/R5rr/0bG - 7/9P4fL/TuHy/03h8v9L4fL/SuDy/0ng8v9H4PL/RODy/xXX7v8F1e3/BNXt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f804ff/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKJVAAAAAADP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDN5v8Azeb/AM3m/wDN5v8Azub/AM3m/wDK - 4f8Ajqz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/zbT6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5fn/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/H9nv/yLa7/8k2vD/Jdrw/yfb8P8p2/D/Ktvw/yvb - 8P8t3PD/L9zw/zDc8P8y3PH/JMPZ/wSJp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wyT - sP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5ff/UeLz/1Li8/9T4vP/VOLz/1Xi - 8/9W4/P/WOPz/1nj8/9a4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Hk9P9i5fT/Y+X0/2Tl - 9P9l5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2jm9P9p5vT/aeb0/2rm9P9q5vT/aub0/2rm - 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9p5vT/aeb0/2jm9P9o5vT/Z+X0/2fl9P9n5fT/ZuX0/2Xl - 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/YOT0/2Pl9P+B6vb/hOr3/4Pq9v+C6vb/ger2/4Hq - 9v+A6vb/fur2/1/A8f9Imev/SJnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/SJnr/0iY6/9iouL/nbjP/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/vsXF/77F - xf++xcX/v8bG/7/Gxv+/xsb/wMbG/6C5z/9ioeL/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX - 6v9Gl+n/RLru/0vg8v9L4fL/SeHy/0jg8v9H4PL/RuDy/0Tg8v893vL/Ddbt/wTV7f8C1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrY7f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H0AICqDAAAAAAAzOT/AMvj/wDK4v8AyOD/AMjf/wDH3v8Ax97/AMjg/wDK4v8AzeX/AM/n/wDN - 5v8Aka//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xy+L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Q+X5/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x3Z7/8k2vD/Jdrw/yba8P8p2/D/Ktvw/yvb - 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f8v0ef/C5Wx/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/BIil/zPF3P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/UeT1/1Li8/9U4vP/VeLz/1bj - 8/9X4/P/WePz/1nj8/9b4/P/W+Pz/13k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl - 9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/a+b1/2vm9f9s5vX/bOb1/2zm - 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/a+b1/2vm9f9r5vX/aub0/2rm9P9q5vT/aeb0/2jm - 9P9n5fT/Z+X0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Pl9P976fb/hOr3/4Pq9/+D6vf/gur3/4Dp - 9v+A6fb/f+n2/13A7/9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Gl+n/Rpfp/1Gb5f+QstL/vMTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xcX/vsXF/77Fxf+/xsb/v8bG/7/Gxv/Ax8f/wMfH/8DH - x//Bx8f/wcjI/8HIyP/CyMj/wsnJ/8LJyf/Dycn/wsnJ/5W21P9SmuX/RZbp/0WW6f9Flun/RZbp/0WV - 6P9Flej/RZXo/0WV6P9Auez/SODy/0fg8v9G4PL/Rd/y/0Pf8v9C3/L/Qd/y/zLc8P8G1e3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Ddfw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8mudL/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhogAAAAAAAAAAAM3l/wDN5f8Azeb/AM/n/wDR6f8A0uv/ANTt/wDU7f8A1O3/ANTt/wDQ - 6P8AlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/LMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn - +v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8c2e//JNrw/yba8P8o2/D/Kdvw/yvb - 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f823fH/N93x/zfc7v8YqcH/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Ia/I/03n+f9P6fz/T+n8/0/p/P9P6fz/T+n8/1Pi8/9U4vP/VuPz/1bj - 8/9Y4/P/WePz/1rj8/9b4/P/XeT0/17k9P9f5PT/YOT0/2Hk9P9i5fT/YuX0/2Tl9P9l5fT/ZeX0/2bl - 9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/23m9f9u5/X/buf1/27n - 9f9u5/X/b+f1/2/n9f9v5/X/b+f1/27n9f9u5/X/buf1/27n9f9t5vX/beb1/2zm9f9s5vX/bOb1/2vm - 9f9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P916PX/hOv2/4Tr9v+C6vb/gur2/4Hq - 9v+B6vb/f+r2/2DF8P9Flun/RZbp/0WW6f9Flun/RZbp/0WV6P9Flej/RZXo/2uk3f+zwcf/vcTE/77F - xf++xcX/vsXF/7/Gxv+/xsb/v8bG/8DGxv/Ax8f/wMfH/8HHx//ByMj/wcjI/8LIyP/CyMj/wsnJ/8PJ - yf/Dycn/w8rK/8TKyv/Eysr/xMrK/8XLy//Fy8v/xcvL/8bMzP/GzMz/vcjN/2+m3f9DlOf/Q5Tn/0OU - 5/9DlOf/Q5Tn/0OU5/9DlOf/Q5Tn/z6+7f9F4PL/RODy/0Lf8v9B3/L/QN/y/z7f8v893vL/Itrv/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yPd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/E5u2/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCBo0UAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO - 5v8Ak7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HOAICZCgCAnRoAg6JCAIKhagCD - oZIAg6G7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yjA1/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9M6Pv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/F9jv/yba8P8n2/D/Kdvw/yvb - 8P8s2/D/Ltzw/zDc8P8x3PD/Mtzx/zXd8f823fH/N93x/zne8f873vH/PN7x/yi91P8Ch6T/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbX/Rt3x/0/p/P9P6fz/T+n8/1Dn+v9U4vP/VuPz/1fj - 8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k9P9g5PT/YOT0/2Ll9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2fl - 9P9o5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9v5/X/cOf1/3Dn - 9f9w5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cOf1/3Dn9f9w5/X/b+f1/2/n9f9v5/X/b+f1/27n - 9f9t5vX/bOb1/2zm9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9s5vT/hOr2/4Xq9v+D6vb/g+r2/4Lq - 9v+B6vb/gOr2/2rM8f9ElOf/RJTn/0SU5/9DlOf/Q5Tn/0OU5/9DlOf/RZXm/4ux1P+/xsb/wMbG/8DH - x//Ax8f/wcfH/8HIyP/ByMj/wsjI/8LIyP/Cycn/w8nJ/8PJyf/Dysr/xMrK/8TKyv/Eysr/xcvL/8XL - y//Fy8v/xszM/8bMzP/GzMz/x83N/8fNzf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8nPz//Jz8//lbfW/0SU - 5f9Ck+b/QpPm/0KT5v9Ck+b/QZLm/0GS5v9Bkub/QcTu/0Lf8v9C3/L/QN/y/z/f8v893/L/PN/y/zre - 8f8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f875Pj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/wKFpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoeQAgIACAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDJ - 4/8Aj6z/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8juNH/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPX7v8n2/D/Kdvw/yvb - 8P8s2/D/Ldzw/y/c8P8x3PD/Mtzx/zTd8f823fH/N93x/zne8f863vH/PN7x/z7e8f8/3/H/NtDk/wqR - rf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP85zeP/T+n8/0/p/P9S5vf/VuPz/1jj - 8/9Z4/P/WuPz/1vj8/9d5PT/XuT0/1/k9P9g5PT/YuX0/2Ll9P9k5fT/ZeX0/2bl9P9n5fT/Z+X0/2nm - 9P9q5vT/aub0/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9w5/X/cef1/3Hn9f9x5/X/cuf1/3Ln - 9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9y5/X/cuf1/3Hn9f9x5/X/cef1/3Hn - 9f9w5/X/b+f1/2/n9f9u5/X/beb1/2zm9f9s5vX/a+b1/2rm9P9p5vT/f+r2/4Xq9v+E6vb/g+r2/4Lq - 9v+C6vb/ger2/3DO5f9CleX/QpPm/0KT5v9Ck+b/QpPm/0KT5v9Ck+b/SJXk/5250P/CyMj/wsjI/8LJ - yf/Dycn/w8nJ/8PKyv/Eysr/xMrK/8TKyv/Fy8v/xMrK/8LJyf/Ax8f/v8bG/77Fxf+9xMT/vcTE/73E - xP++xcX/v8bG/8HIyP/Fy8v/yM3N/8nPz//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8vR0f/M0dH/zNHR/8zS - 0v+qwtb/SJXj/0CR5P9AkeT/QJHk/0CR5P9AkeT/QJHk/0CU5f9A0e//P9/x/z3e8f883vH/PN7x/zne - 8f843fH/Kdvw/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8F1u7/Tej8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrP5f8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDG - 3/8Ai6n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HpAIahJgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Gq7I/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zXH3v8DmbX/AJOw/wCmwf8Azuf/ANTt/wDU7f8A1O3/ANTt/wDU7f8O1u7/KNvw/yrb - 8P8r2/D/Ldzw/y/c8P8w3PD/Mtzx/zTd8f813fH/N93x/zne8f863vH/PN7x/z3e8f8/3/H/Qd/y/0Lf - 8v8/2u7/F6K8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/ye2z/9O6Pv/VOX1/1jj - 8/9Z4/P/W+Pz/1zk8/9e5PT/XuT0/2Dk9P9h5PT/YuX0/2Pl9P9l5fT/ZeX0/2fl9P9n5fT/aeb0/2rm - 9P9r5vX/bOb1/2zm9f9t5vX/b+f1/2/n9f9w5/X/cef1/3Hn9f9y5/X/cuf1/3Pn9f9z5/X/c+f1/3To - 9f906PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dOj1/3To9f9z5/X/c+f1/3Pn - 9f9z5/X/cuf1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m9f9s5vX/dej2/4Xr9v+F6/b/hOv2/4Pq - 9v+C6vb/ger2/37l9f84dqz/OYHM/0CR5f9AkeX/QJHl/0CR5f9AkeX/SJTk/6m+z//Eysr/xMrK/8XL - y//Fy8v/xcvL/8bMzP/GzMz/xszM/8fNzf/Eysr/wMbG/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/x83N/83S0v/N0tL/zdPT/87T0//O09P/ztTU/8/U - 1P/P1NT/z9TU/7fI1v9GleL/PpDj/z6Q4/8+kOP/PpDj/z6Q4/8+j+P/P5rl/z/a8P873vH/Ot7x/zne - 8f833vH/Nt7x/zDc8P8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H9AICjJAAAAAAAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC5 - 0/8AhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfLQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISiNACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKguv9P6fz/T+n8/0/p - /P9P6fz/Rt3x/xegu/8Ag6H/AIOh/wCDof8Ag6H/AJaz/wDT7P8A1O3/ANTt/wDU7f8A1O3/CNXu/ynb - 8P8r2/D/Ldzw/y7c8P8w3PD/Mtzx/zPd8f813fH/N93x/zjd8f853vH/PN7x/z3e8f8+3vH/QN/y/0Lf - 8v9D3/L/ReDy/0bg8v8ot87/AYSj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fp+6/1Db - 7v9Z4/P/W+Pz/1zk8/9e5PT/X+T0/2Dk9P9i5fT/YuX0/2Tl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm - 9f9s5vX/beb1/27n9f9v5/X/b+f1/3Hn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/duj1/3bo - 9f926PX/duj1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3bo9f926PX/duj1/3bo - 9f916PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn9f9v5/X/b+f1/4Pq9v+F6vf/her3/4Tq - 9v+D6vb/gen2/3LV+f9Zr+H/JleJ/zBurf8/kOP/P5Dj/z+Q4/8/kOP/QZLj/6W90f/GzMz/x83N/8fN - zf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8bMzP+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/y9HR/9DV1f/Q1dX/0dbW/9HW - 1v/R1tb/0tfX/9LX1//S19f/s8fX/0GQ4v89juL/PY7i/z2O4v89juL/PY7i/z2O4v9Asej/Od7x/zjd - 8f833fH/Nd3x/zPd8f8t2/D/Jtrw/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zPi9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/C5Gu/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOitwAAAAAAAAAAAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTs/wCp - xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAISiNAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoRsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj63/T+n8/0/p - /P9O6Pv/Lb3V/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Awtz/ANTt/wDU7f8A1O3/ANTt/wPV - 7f8r2/D/LNvw/y3c8P8w3PD/Mdzw/zLc8f813fH/Nt3x/zfd8f853vH/O97x/zze8f8+3vH/QN/y/0Hf - 8v9D3/L/ReDy/0bg8v9I4PL/SeDy/zrM4P8Ijar/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Lj6v/TtXn/13k9P9e5PT/X+T0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2zm - 9f9s5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9z5/X/c+f1/3To9f916PX/duj1/3bo9f936PX/d+j1/3jo - 9f946PX/eOj1/3no9v956Pb/eej2/3no9v966fb/eun2/3rp9v956Pb/eej2/3no9v956Pb/eOj1/3jo - 9f946PX/d+j1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Pn9f9x5/X/cef1/3jo9v+G6/f/hev3/4Tq - 9/+E6vf/gur2/3/l9/9au/z/M3q7/yRVh/8mWY3/PY7g/z2P4v89juL/PY7i/5K11v/Jzs7/yc/P/8nP - z//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Hzc3/0tfX/9PY - 2P/U2Nj/1NnZ/9TZ2f/V2dn/1dra/9Xa2v+fvdr/O4zg/zuM4P87jOD/O4zg/zuM4P87jOD/PIzg/z3M - 7f823fH/Nd3x/zPd8f8x3fH/Kdvw/ynb8P8P1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f9K5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qtjt/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoUwAAAAAAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/AMvk/wCZ - tf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfNQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/07p - /P9C2O3/Epq1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/J9vw/y3c8P8v3PD/MNzw/zLc8f803fH/Nd3x/zfd8f853vH/Ot7x/zze8f8+3vH/P9/x/0Hf - 8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg8v9N4fL/SNns/xWct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/w+Srv9d4/P/YOT0/2Hk9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm - 9f9u5/X/b+f1/3Dn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3bo9f926PX/d+j1/3jo9f946PX/eej2/3no - 9v966fb/e+n2/3vp9v976fb/e+n2/3vp9v986fb/fOn2/3zp9v986fb/fOn2/3vp9v976fb/e+n2/3vp - 9v976fb/eun2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3bo9f906PX/c+f1/3Pn9f+E6vb/h+v3/4br - 9/+F6/f/hOv3/4Lq9/9u0fn/SbT8/zKCw/8kVIb/I1SG/zR7xf87jeH/O43h/3Cl2//L0ND/y9HR/8vR - 0f/M0dH/zNLS/83S0v/N0tL/zNHR/8HHx/+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/8LJ - yf/U2Nj/1tvb/9fb2//X29v/19zc/9jc3P/Y3Nz/2Nzc/3us3f85i9//OYvf/zmL3/85i9//OYvf/zmL - 3/89nuT/Ndzx/zPc8f8x3PH/MNzx/yfb8P8n2/D/INnv/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8X2/H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq5 - 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDod4AgIACAAAAAAAAAAAAAAAAAAAAAADU7f8A1O3/ALfR/wCI - pv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HiAIafKAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8jtc3/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wDO5v8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yDZ7/8u3PD/MNzw/zLc8f8z3fH/Nd3x/zfd8f843fH/Ot7x/zze8f893vH/P9/x/0Hf - 8v9C3/L/RN/y/0Xg8v9H4PL/SeDy/0rg8v9M4fL/TeHy/0/h8/9Q4fL/KLHJ/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/TtLl/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9q5vT/bOb1/23m - 9f9v5/X/b+f1/3Hn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f946PX/eOj1/3no9v966fb/e+n2/3vp - 9v986fb/fOn2/33p9v996fb/fen2/33p9v9+6fb/fun2/37p9v9+6fb/fun2/37p9v9+6fb/fen2/33p - 9v996fb/fen2/3zp9v976fb/e+n2/3vp9v966fb/eej2/3jo9f936PX/duj1/3bo9f956fX/h+r2/4fq - 9v+F6vb/her2/4Pq9v+C6fb/V7r7/z+v/P85oen/I1OF/yNThf8qZaP/Oovf/0eS3//CzdT/ztPT/87T - 0//O1NT/z9TU/8/U1P/P1NT/zdLS/7/Gxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/wMfH/9Xa2v/Z3d3/2t7e/9re3v/a3t7/297e/9vf3//S2+D/S5Tf/ziK3v84id7/OIne/ziJ - 3v84id7/OYne/znE7P8w3PD/L9zw/y3c8P8k2vD/JNrw/yTa8P8H1e3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAzOb/AJ26/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAISeHQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqHXAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Aoak/wC61f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8W2O//L9zw/zHc8P8y3PH/Nd3x/zbd8f843fH/Od7x/zve8f893vH/Pt7x/0Df - 8v9C3/L/Q9/y/0Xg8v9H4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/UuLz/1Ti8/88x9v/Boqn/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/1LV5/9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/bOb1/27n - 9f9v5/X/cOf1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo9f946PX/eun2/3vp9v976fb/fOn2/33p - 9v996fb/fun2/37p9v9/6fb/f+n2/3/p9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/3/p - 9v9/6fb/f+n2/3/p9v9+6fb/fen2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f946PX/gOr2/4jr - 9v+G6/b/her2/4Tq9v+D6vb/ddr4/0iz/P8+r/z/Pq/8/yZlnP8hUoT/IlSH/zeH2f+cvNr/0NXV/9DV - 1f/R1tb/0dbW/9HW1v/S19f/0NXV/8DGxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+6t7H/spqD/6yDYf+peFH/pm9D/6dxRv+qe1X/rYho/7Sh - kP+7vrv/vcTE/73ExP/Ax8f/2d3d/9zg4P/d4OD/3eDg/93h4f/e4eH/3uHh/7DJ4P82iNz/Nojc/zaI - 3P82iNz/Nojc/zaI3P85m+H/Ltzw/y3b8P8q2/D/Idrv/yHa7/8h2u//E9fu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9H3/P/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6HzAICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAIek/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKG8AICcEgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/E5y2/0PZ7v8U2fH/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/DNbu/zDc8P8y3PH/NN3x/zXd8f833fH/Od7x/zre8f883vH/Pt7x/0Df - 8v9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9L4fL/TeHy/0/h8/9Q4fP/UuLz/1Ti8/9V4vP/VuPz/03X - 6f8SlrL/AIOh/wCDof8Ag6H/AIOh/xWXs/9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9r5vX/bOb1/27n - 9f9v5/X/cef1/3Ln9f9z5/X/dOj1/3bo9f926PX/eOj1/3jo9f966fb/e+n2/3vp9v996fb/fen2/37p - 9v9/6fb/f+n2/4Dq9v+B6vb/ger2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq - 9v+C6vb/gur2/4Lq9v+B6vb/gOr2/3/p9v9/6fb/f+n2/33p9v996fb/fOn2/3vp9v976fb/eej2/4bq - 9v+H6/b/huv2/4Xr9v+D6vb/g+r2/1/C+f8/r/z/Pq/8/z6v/P8xh8f/IVKE/yBRhP9IgLn/0NbY/9LX - 1//T2Nj/09jY/9TY2P/U2Nj/1NnZ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/7izqv+sgmH/pGY2/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pmo7/7CRdv+7vbn/vcTE/8bMzP/f4uL/3+Pj/+Dj4//g4+P/4OPj/+Dk5P/g4+T/Y6De/zWH - 2/81h9v/NYfb/zSG2/80htv/NYbb/zHL7P8q2/D/Jtvw/x7Z7/8e2e//Htnv/xzZ7/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Lb3U/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAKqqAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoJcAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiaf/Lr/W/0/p/P9P6fz/Qub5/wLV - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f8x3PD/M93x/zXd8f833fH/ON3x/zne8f883vH/Pd7x/z/f - 8f9B3/L/Q9/y/0Tf8v9F4PL/SODy/0ng8v9K4PL/TeHy/07h8/9P4fP/UuLz/1Pi8/9U4vP/VuPz/1jj - 8/9Z4/P/WeHw/yquxv8GiKb/AYSi/x+iu/9Y2+v/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Ln9f9z5/X/dej1/3bo9f936PX/eOj1/3rp9v976fb/fOn2/33p9v996fb/f+n2/3/p - 9v+A6vb/ger2/4Lq9v+C6vb/g+r2/4Pq9v+E6vb/hOr2/4Tq9v+E6vb/her2/4Xq9v+F6vb/her2/4Tq - 9v+E6vb/hOr2/4Tq9v+E6vb/g+r2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/f+n2/37p9v996fb/fOn2/37p - 9v+I6/f/h+r3/4bq9v+F6vb/her2/4Dp9v9KtPv/Pq/8/z6v/P8+r/z/O6bx/yBShf8gUIP/b4KU/9XZ - 2f/V2tr/1tra/9ba2v/W2tr/19vb/8zS0v+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/77ExP++xcX/vcLB/66KbP+kZjb/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pWo8/7Oei/+9xMT/0tfX/+Ll5f/i5eX/4+bm/+Pm5v/j5ub/5Obm/77S - 5P8zhdr/M4Xa/zOF2v8zhdr/M4Xa/zOF2v8zreT/KNrw/yLZ8P8b2e//G9nv/xvZ7/8b2e//BdXt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f804vf/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xKatf8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCAoyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofUAgqJaAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqKBAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/R97y/0/p/P9P6fz/T+n8/0/p - /P8k3vT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ktvw/zTd8f813fH/N93x/zne8f873vH/PN7x/z7e - 8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TOHy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1fj - 8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Hk9P9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3bo9f946PX/eej2/3vp9v976fb/fen2/33p9v9/6fb/f+n2/4Hq - 9v+C6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr9/+H6/f/h+v3/4fr9/+H6/f/h+v3/4fr - 9/+H6/f/h+v3/4fr9/+G6/b/huv2/4Xq9v+E6vb/hOr2/4Pq9v+C6vb/gur2/4Hq9v+A6vb/f+n2/37p - 9v+D6vb/iev3/4jr9/+G6vf/her2/4Tq9v9z2vj/Q7H8/z6v/P8+r/z/Pq/8/z6v/P8obqj/LViD/36C - g//FyMj/2Nzc/9jc3P/Y3d3/2d3d/9fb2/+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/77F - xf+/xcX/v8bG/8DHx//Bx8f/wMG+/6p5Uv+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/r49z/7/Gxv/h5OT/5ejo/+Xo6P/m6Oj/5unp/+bp - 6f/l6er/V5nd/zGE2P8xhNj/MYTY/zGE2P8xhNj/M43b/yfb8P8f2e//Gdjv/xnY7/8Z2O//Gdjv/wzW - 7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0bd8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocYAg58lAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh8gCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/weMqf81x97/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/S+j7/wjX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/Z7/813fH/N93x/zjd8f863vH/PN7x/z7e - 8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UOHz/1Li8/9U4vP/VeLz/1fj - 8/9Z4/P/WuPz/1zk8/9e5PT/X+T0/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v986fb/fen2/3/p9v9/6fb/ger2/4Lq - 9v+C6vb/hOr2/4Tq9v+F6vb/huv2/4fr9/+H6/f/iOv3/4jr9/+J6/f/iev3/4nr9/+J6/f/iev3/4nr - 9/+J6/f/iev3/4nr9/+J6/f/iOv3/4jr9/+H6/f/h+v3/4br9v+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq - 9v9/6fb/h+r3/4jr9/+H6/f/huv3/4Xr9/+E6vb/YMb3/z6v/P8+r/z/Pq/8/z6v/P8+r/z/NZXa/1Vs - g/+ChIT/oqWl/9ve3v/b39//29/f/9zf3//L0dH/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/v8XF/8DG - xv/Bx8f/wcjI/8LJyf/Dycn/w8nI/61/W/+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+ymIH/0NXV/+fq6v/o6ur/6Ovr/+jr - 6//p6+v/6ezs/6bF4v8wgtf/MILX/zCC1/8vgtf/L4LX/y+C1/8ny+z/G9nv/xbY7/8W2O//Ftjv/xbY - 7/8Q1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8ru9P/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ84AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoHkAqqoDAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnyAAhKG4AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/x6qxP9K4vb/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P814vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8S1+7/Nt3x/zfd8f853vH/O97x/zze - 8f8+3vH/QN/y/0Lf8v9D3/L/ReDy/0fg8v9J4PL/SuDy/03h8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1bj - 8/9Y4/P/WuPz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p9v+A6vb/gur2/4Lq - 9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iev3/4rr9/+K6/f/i+v3/4vr9/+L6/f/i+v3/4vr - 9/+L6/f/i+v3/4vr9/+L6/f/i+v3/4vr9/+K6/f/iev3/4nr9/+I6/f/h+v3/4fr9/+G6/b/hOr2/4Tq - 9v+C6vb/gur2/4nr9/+J6/f/iOv3/4fr9/+F6/f/hOv3/062+P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z6v - +/9uhZf/hIaG/4WHh//T19f/3uHh/97h4f/c4OD/v8XF/73ExP+9xMT/vcTE/73ExP+/xsb/wMfH/8HI - yP/Cycn/w8rK/8TKyv/Fy8v/xszM/7KRdP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGY1/7eqm//n6ur/6+3t/+vt - 7f/r7e3/7O7u/+zu7v/c5Ov/LoHW/y6A1v8ugNb/LoDW/y6A1v8ugNb/KbTl/xjY7v8T1+7/E9fu/xPX - 7v8T1+7/Etfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCDof8Ag6H/AIOitwCGpCoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCEonAAg6H0AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Gu/zrO5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ8f8A1O3/ANTt/wDU7f8A1O3/BNXt/zbd8f843fH/Ot7x/zze - 8f8+3vH/P9/x/0Hf8v9D3/L/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Hi8/9S4vP/VOLz/1bj - 8/9Y4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f916PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fun2/3/p9v+B6vb/gur2/4Pq - 9v+E6vb/huv2/4fr9/+H6/f/iev3/4nr9/+K6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+O7Pf/juz3/47s - 9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iuv3/4nr9/+J6/f/h+v3/4fr - 9/+F6vb/hOr2/4Tq9v+K6/f/iev3/4fq9/+G6vf/her3/4Dp9/9Esvv/Pq/8/z6v/P8+r/z/Pq/8/z6v - /P8+r/z/XZ3H/4WHh/+Fh4f/r7Gx/+Dj4//g5OT/0tfX/73ExP+9xMT/vcTE/77Fxf/Axsb/wcjI/8PJ - yf/Eysr/xcvL/8bMzP/Hzc3/yM7O/8C1qf+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/oWMz/25E - I/8/JxT/JxgM/y0cDv9LLhj/gU8p/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+nbkL/19jV/+3v - 7//t7+//7vDw/+7w8P/u8PD/7/Dx/1CU2f8sf9T/LH/U/yx/1P8sf9T/LH/U/yub3P8T2O7/ENfu/xDX - 7v8Q1+7/ENfu/xDX7v8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW7/9O6Pz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/RNrv/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISgnwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOh/wCDof8AgqDVAIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDockAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhaL/JLHL/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/AtTt/wDU7f8A1O3/ANTt/wDU7f8q2/D/Od7x/zve - 8f883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9H4PL/SeDy/0rg8v9N4fL/TuHz/1Dh8/9S4vP/VOLz/1Xi - 8/9X4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9h5PT/YuX0/2Xl9P9m5fT/Z+X0/2rm9P9r5vX/bOb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq - 9v+F6vb/h+v3/4fr9/+J6/f/iev3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/4/s9/+P7Pf/kOz3/5Ds - 9/+Q7Pf/kOz3/5Ds9/+Q7Pf/kOz3/5Ds9/+P7Pf/j+z3/47s9/+O7Pf/jez3/4zs9/+L6/f/iuv3/4nr - 9/+I6/f/h+v3/4br9v+H6vb/iuv2/4jr9v+H6/b/hur2/4Xq9v933/f/QbD8/z6v/P8+r/z/Pq/8/z6v - /P8+r/z/Pq/8/0Gt9v+Di5D/h4mJ/42Pj//e4eH/4+bm/8bMzP+9xMT/vcTE/7/Gxv/Bx8f/wsnJ/8TK - yv/Fy8v/x8zM/8jOzv/Jz8//ys/P/8vQ0P+uf1r/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/jVct/x0S - Cf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP88JRP/nmEy/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7id - hv/w8fH/8PLy//Hy8v/x8vL/8fPz//Lz8/+Ot9//K37T/yt+0/8rftP/Kn3T/yp90/8rh9b/D9bu/w3W - 7v8N1u7/Ddbu/w3W7v8N1u7/BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8i3vT/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yi3z/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoXIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDodMAgqFcAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkpIHAIKigwCDofkAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8PlrL/P9Tq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yXe9P8A1O3/ANTt/wDU7f8A1O3/Gtjv/zne - 8f883vH/Pt7x/z/f8f9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/U+Lz/1Ti - 8/9W4/P/WOPz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n - 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq - 9v+G6/b/h+v3/4jr9/+J6/f/i+v3/4vr9/+M7Pf/juz3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Lt - 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Hs9/+Q7Pf/kOz3/4/s9/+O7Pf/jez3/4zs - 9/+L6/f/iuv3/4nr9/+I6/f/iev3/4rr9v+J6/b/h+v2/4br9v+F6vb/bNT3/0Cw/P8+r/z/Pq/8/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/ZZvA/4mKiv+Jior/ury8/+Xo6P++xcX/vsXF/8DGxv/ByMj/w8nJ/8XL - y//GzMz/yM7O/8nPz//K0ND/zNHR/83S0v/JyMP/pGY1/6RlNP+kZTT/pGU0/6RlNP+kZTT/ilUs/wsH - A/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/yUXDP+hYzP/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+pd0//8fPz//P09P/z9PT/8/X1//T19f/09fX/u9Lo/yl80v8pfNL/KXzS/yl80v8pfNL/K33S/w7R - 7P8L1u7/C9bu/wvW7v8L1u7/C9bu/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/P+T5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq7/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaNFAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIKh1wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOGpP8quND/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdbv/wDU7f8A1O3/ANTt/wrW - 7v873vH/PN7x/z7e8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0ng8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti - 8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9l5fT/Z+X0/2jm9P9q5vT/bOb1/23m - 9f9v5/X/cef1/3Ln9f906PX/duj1/3fo9f946PX/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq - 9v+G6/b/h+v3/4nr9/+K6/f/i+v3/4zs9/+O7Pf/juz3/5Ds9/+Q7Pf/kez3/5Lt9/+T7ff/k+33/5Tt - 9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt+P+V7fj/lO33/5Pt9/+T7ff/k+33/5Lt9/+R7Pf/kOz3/4/s - 9/+O7Pf/jez3/4vr9/+L6/f/iev3/4rr9/+L7Pf/iev2/4jr9v+G6/b/hev2/13E9f8+r/z/Pq/8/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/0es8f+JjI7/iouL/5KTk//d4OD/vsXF/8DGxv/CyMj/xMrK/8bM - zP/Hzc3/yc7O/8rQ0P/M0dH/zdLS/87T0//Q1dX/v6mW/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/y4c - Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Xjoe/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/+Pf2v/19vb/9vf3//b39//29/f/9/j4/9rm8P8ne9D/J3vQ/yd70P8ne9D/J3vQ/yd7 - 0P8Qw+j/CNXu/wjV7v8I1e7/CNXu/wjV7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWmFwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApA4AhKGVAIOh/QCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xScuP9D2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbi9/8A1O3/ANTt/wDU - 7f8A1O3/Nd3x/z3e8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UeLz/1Pi - 8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/23m - 9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq - 9v+H6/f/iOv3/4nr9/+K6/f/i+v3/43s9/+O7Pf/j+z3/5Ds9/+R7Pf/k+33/5Pt9/+U7ff/le34/5Xt - +P+W7fj/lu34/5ft+P+X7fj/l+34/5ft+P+X7fj/l+34/5bt+P+V7fj/le34/5Xt+P+U7ff/k+33/5Lt - 9/+R7Pf/kOz3/47s9/+O7Pf/jOz3/4vr9/+K6/f/iuv3/4nr9/+H6vb/hur2/4Tq9v9TvfX/Pq/8/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/bpy7/4yNjf+MjY3/sbW1/8DGxv/CyMj/xMrK/8bM - zP/Izc3/yc/P/8vR0f/N0tL/ztPT/9DV1f/R1tb/0tfX/7aSdP+kZTT/pGU0/6RlNP+kZTT/pGU0/4ZS - Kv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xILBv+jZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP/Qvq7/+Pn5//n5+f/5+fn/+fr6//r6+v/u8/f/JnnP/yZ5z/8mec//JnnP/yV5 - z/8lec//ELvl/wXV7f8F1e3/BdXt/wXV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf - 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ia7H/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh3wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEok0Ag6HjAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/BYqn/zDB2P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ftrx/wDU - 7f8A1O3/ANTt/yLa7/8+3vH/QN/y/0Hf8v9D3/L/ReDy/0fg8v9I4PL/SuDy/0zh8v9O4fP/UOHz/1Li - 8/9U4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl9P9p5vT/aub0/2zm - 9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3vp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq - 9v+H6/f/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Hs9/+T7ff/k+33/5Xt+P+V7fj/lu34/5ft - +P+Y7vj/mO74/5nu+P+Z7vj/me74/5ru+P+Z7vj/me74/5nu+P+Y7vj/mO74/5ju+P+W7fj/le34/5Xt - +P+U7ff/k+33/5Hs9/+Q7Pf/j+z3/47s9/+M7Pf/i+v3/4rr9/+J6/f/iOv3/4bq9v+F6vb/Tbj1/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/06v8P+LkJL/jo6O/2JkZP+Dh4f/xMrK/8bM - zP/Izs7/ys/P/8zR0f/O09P/z9TU/9HV1f/S19f/1NjY/9XZ2f+xg1//pGU0/6RlNP+kZTT/pGU0/6Rl - NP9hPB//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8CAgL/BQUF/wYGBv8FBQX/kFku/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/yK2W//v7+//7/Pz/+/z8//z8/P/8/Pz/+/z9/yR4zv8keM7/JHjO/yR4 - zv8keM7/JHjO/w+14v8C1O3/AtTt/wLU7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU - 7f9F5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef7/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oKcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWmFwCDoKcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/GaO+/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm - +f8C1O3/ANTt/wDU7f8M1u7/Pt7x/0Df8v9C3/L/RN/y/0Xg8v9I4PL/SeDy/0vh8v9N4fL/T+Hz/1Hi - 8/9S4vP/VOLz/1bj8/9Y4/P/WePz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2bl9P9n5fT/aub0/2vm - 9f9t5vX/b+f1/3Hn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+C6vb/g+r2/4Tq - 9v+G6/b/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/lO33/5Xt+P+W7fj/mO74/5ju - +P+Z7vj/mu74/5ru+P+b7vj/m+74/5zu+P+c7vj/nO74/5vu+P+b7vj/mu74/5ru+P+Z7vj/mO74/5ju - +P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs9/+L6/f/iev3/4jr9/+H6/f/hOr2/0u1 - 9f8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbL8/0iz/P9KtPz/cqC//4+QkP8lJib/HB0d/8TK - yv/Izs7/ys/P/8zR0f/O09P/0NXV/9HW1v/T2Nj/1dnZ/9ba2v/Y3Nz/r3xV/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/VzYc/wAAAP8AAAD/AAAA/wwMDP8WFhb/GRkZ/xwcHP8gICD/IyMj/yYmJv8pKSn/LCws/5ds - S/+maTn/pGU0/6RlNP+kZTT/pGU0/8Ojiv/9/v7//v7+//7+/v/+//////////////8re8//InbN/yJ2 - zf8ids3/InbN/yJ2zf8NsOH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfP5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6FvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIShXwCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ijar/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/J971/wDU7f8A1O3/ANTt/zTd8f9B3/L/Q9/y/0Tf8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h - 8/9S4vP/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2rm - 9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Xo9f936PX/eOj1/3rp9v986fb/fen2/3/p9v+B6vb/gur2/4Tq - 9v+G6/b/h+v3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt+P+Y7vj/mO74/5ru - +P+a7vj/m+74/5zu+P+c7vj/ne74/57v+P+e7/j/nu/4/57v+P+d7vj/ne74/5zu+P+c7vj/m+74/5ru - +P+a7vj/mO74/5ju+P+W7fj/le34/5Pt9/+S7ff/kOz3/4/s9/+O7Pf/jOz3/4vr9/+J6/f/h+v3/4br - 9v9Mt/X/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/0iz/P9KtPz/TbX8/1az8v94fX//AQEB/wAA - AP98gID/ys/P/8zR0f/O09P/0NXV/9LW1v/U2Nj/1tra/9fb2//Z3d3/2t7e/7SIZf+kZTT/pGU0/6Rl - NP+kZTT/pGU0/2Y/IP8AAAD/AAAA/yAgIP80NDT/Nzc3/zo6Ov89PT3/QUFB/0RERP9HR0f/SkpK/01N - Tf+3kHH/vpFu/6ZoOP+kZTT/pGU0/6RlNP/Mspz////////////////////////////+/v//IXXL/yF1 - y/8hdcv/IXXL/yF1y/8gdMv/C7Ph/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/MeH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8bpsD/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8fqsT/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0zo+/8K1+//ANTt/wDU7f8d2e//Qd/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TeHy/07h - 8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm - 9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p9v+A6vb/gur2/4Tq - 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+U7ff/le34/5ft+P+Y7vj/me74/5ru - +P+b7vj/nO74/53u+P+e7/j/n+/4/5/v+P+g7/j/oO/4/6Dv+P+g7/j/oO/4/5/v+P+f7/j/nu/4/53u - +P+c7vj/m+74/5ru+P+Z7vj/mO74/5bt+P+V7fj/k+33/5Lt9/+Q7Pf/j+z3/43s9/+L6/f/iuv3/4nr - 9/+H6/f/Tbj2/z6v/P8+r/z/Pq/8/z6v/P9BsPz/RLH8/0ez/P9KtPz/TbX8/1C2/P9St/z/L0JQ/wAA - AP8AAAD/FRYW/8jOzv/O09P/0NXV/9LW1v/U2Nj/1tra/9jc3P/Z3d3/29/f/9zg4P+9mn7/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+RWS7/AwIB/xQUFP9SUlL/VVVV/1hYWP9bW1v/Xl5e/2FhYf9lZWX/aGho/2tr - a/9/eXT/zKmO/86skf+7jGf/pGU0/6RlNP+kZTT/2cm7////////////////////////////9vn7/x9z - yv8fc8r/H3PK/x9zyv8fc8r/H3PK/wm34/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/A9Xu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5vn/Aoak/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh8gCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAhKFyAIOh9QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuSrv870OX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/N+P3/wDU7f8A1O3/BtXt/0Hf8v9D3/L/ReDy/0jg8v9J4PL/S+Hy/03h - 8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9l5fT/Z+X0/2jm - 9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo9f976fb/fOn2/37p9v9/6fb/ger2/4Pq - 9v+E6vb/h+v3/4jr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu - +P+c7vj/ne74/5/v+P+f7/j/oO/4/6Hv+P+h7/j/ou/4/6Lv+P+i7/j/ou/4/6Lv+P+h7/j/oe/4/6Dv - +P+f7/j/nu/4/53u+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jez3/4vr - 9/+J6/f/h+v3/1S/9v8+r/z/Pq/8/z6v/P9AsPz/Q7H8/0ay/P9KtPz/TbX8/1C2/P9Tt/z/RpnR/wED - BP8AAAD/BwcH/wAAAP9xdHT/z9TU/9HW1v/T2Nj/1tra/9jc3P/a3t7/3N/f/97h4f/f4uL/zLmp/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/0IoFf9AQED/cnJy/3Z2dv95eXn/fHx8/39/f/+CgoL/hoaG/4mJ - if+MjIz/wa6f/9i9qP/Zv6v/07We/6RlNP+kZTT/pGY1//Hu6////////////////////////////+Ts - 8/8dcsn/HXLJ/x1yyf8dcsn/HXLJ/x1yyf8Gweb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/x3c8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/L83k/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoS4Ag6HLAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AYWi/yWzzP9M5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8X2/H/ANTt/wDU7f8v3PD/RN/y/0bg8v9I4PL/SuDy/0zh - 8v9N4fL/T+Hz/1Hi8/9T4vP/VeLz/1bj8/9Z4/P/WuPz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl - 9P9p5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/duj1/3jo9f956Pb/e+n2/33p9v9/6fb/gOr2/4Lq - 9v+E6vb/huv2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu - +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+k8Pn/pfD5/6Tw+f+k8Pn/pPD5/6Pv - +f+i7/j/oe/4/6Dv+P+f7/j/ne74/5zu+P+a7vj/me74/5ju+P+V7fj/lO33/5Pt9/+R7Pf/j+z3/47s - 9/+L6/f/iuv3/4jr9/9gy/n/Pq/8/z6v/P8/r/z/QrD8/0ay/P9Js/z/TLX8/0+2/P9St/z/Vbn8/xcy - Q/8AAAD/FyMr/xscHP8AAAD/EBER/8nNzf/T2Nj/1dra/9jc3P/a3t7/3N/f/97h4f/g4+P/4eTk/+De - 2/+laDn/pGU0/6RlNP+kZTT/pGU0/6RlNP+XXTD/dW1n/5OTk/+Wlpb/mpqa/52dnf+goKD/o6Oj/6am - pv+qqqr/wrq0/+PPwP/k0cL/5dPF/+TRwv+kZTT/pGU0/6+BXf/+/v7///////////////////////// - ///F1+r/HHDH/xxwx/8ccMf/HHDH/xxwx/8ccMf/As7r/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f854/j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKjvf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqFsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICfCACCoYUAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/D5ey/0DV6/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Reb6/wLU7v8A1O3/Fdju/0Xg8v9G4PL/SODy/0rg - 8v9M4fL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl - 9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq - 9v+D6vb/hOr2/4fr9/+I6/f/iuv3/4vr9/+O7Pf/j+z3/5Hs9/+T7ff/le34/5bt+P+Y7vj/mu74/5vu - +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+k8Pn/pfD5/6bw+f+m8Pn/p/D5/6fw+f+m8Pn/pvD5/6bw - +f+l8Pn/pPD5/6Pv+f+h7/j/oO/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt+P+T7ff/ku33/5Ds - 9/+O7Pf/jOz3/4vr9/+J6/f/atT5/z6v/P8+r/z/QbD8/0Wx/P9Is/z/S7T8/0+2/P9St/z/Vbj8/zt9 - qv8AAAD/AQID/0uRwv8TFxn/AAAA/wAAAP9gYmL/1NnZ/9fb2//Z3d3/29/f/97h4f/g4+P/4uXl/+Tn - 5//l6Oj/u5Jx/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/8Kcfv/Gv7r/t7e3/7u7u/++vr7/wcHB/8TE - xP/JyMj/39fR/+7h2P/v49r/8OXd//Hn3//k0sP/pGU0/6RlNP/EsJ7///////////////////////// - ////////kLbd/xpvxv8ab8b/Gm/G/xpvxv8ab8b/F3rK/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8J1+//Tun8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISgPgCDofsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Boqo/ym40P8ntc7/JbPM/yWzzP8ls8z/JbPM/yWzzP8ir8n/IKzG/yCsxv8grMb/IKzG/yCs - xv8dqcP/GqW//xqlv/8apb//GqW//xqlv/8Yor3/FZ65/xWeuf8Vnrn/FZ65/xWeuf8TnLf/EJiz/xCY - s/8QmLP/EJiz/xCYs/8PlrH/C5Gu/wuRrv8Lka7/C5Gu/wuRrv8Fjqv/AIim/wCNq/8dr8j/RNzv/0ng - 8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9h5PT/YuX0/2Xl - 9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo9f936PX/eOj1/3vp9v996fb/fun2/4Dq - 9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/lO33/5Xt+P+X7fj/me74/5ru - +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+l8Pn/pvD5/6fw+f+o8Pn/qPD5/6nw+f+p8Pn/qfD5/6jw - +f+o8Pn/pvD5/6bw+f+k8Pn/o+/5/6Hv+P+g7/j/n+/4/5zu+P+b7vj/mu74/5ju+P+W7fj/lO33/5Pt - 9/+Q7Pf/j+z3/43s9/+L6/f/iev3/3je+P8+r/z/P6/8/0Ox/P9Gsvz/SrT8/061/P9Rt/z/VLj8/02k - 3/8HDhP/AAAA/zNkhf9jvvz/BgsO/wAAAP8BAgL/CwwM/77Cwv/Y3Nz/2t7e/93h4f/f4+P/4eTk/+Tn - 5//m6Oj/6Orq/+Da0/+laTr/pGU0/6RlNP+kZTT/pGU0/6RlNP+xe1H/7+Tb/+7m3//p5N//6eXi/+vo - 5f/w7On/9vHt//jz7//59fL/+vf1//v59//9+/n/zKmO/6RlNP+selL/6+3s//////////////////// - /////////////0KHzf8ZbsX/GW3F/xltxf8ZbcX/GW3F/xGO0f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8myuL/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKiaACDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmj - vf9J4PL/S+Hy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9g5PT/YeT0/2Pl - 9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n9f9w5/X/cuf1/3Pn9f926PX/eOj1/3no9v976fb/fen2/3/p - 9v+B6vb/gur2/4Tq9v+G6/b/iOv3/4nr9/+L6/f/juz3/4/s9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5ru - +P+c7vj/ne74/5/v+P+h7/j/ou/4/6Tw+f+m8Pn/p/D5/6jw+f+p8Pn/q/H5/6vx+f+r8fn/q/H5/6vx - +f+r8fn/qfD5/6jw+f+n8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt - +P+T7ff/kez3/4/s9/+O7Pf/i+v3/4rr9/+F6ff/Pq/8/0Gw/P9Fsfz/SLP8/0y1/P9Ptvz/U7j8/1a3 - +f8VLDv/AQEC/zJigv9jvvz/Wqnf/wAAAP8CAwP/IiMj/wAAAP9MTk7/19vb/9zf3//e4uL/4eTk/+Pm - 5v/l6Oj/6Orq/+rs7P/s7u7/0Lek/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/9Cvlv/9+/r//v39//// - ////////////////////////////////////////6tvQ/6ZoOP+mbD7/ysS8//39/f////////////// - //////////////L1+P8XbMP/F2zD/xdsw/8XbMP/F2zD/xdsw/8KqNz/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0Lm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Cp66/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIWfMACDofwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Qtjr/0vh8v9N4fL/T+Hz/1Hi8/9T4vP/VOLz/1bj8/9Y4/P/WuPz/1zk8/9e5PT/YOT0/2Ll - 9P9j5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9v5/X/cef1/3Ln9f906PX/duj1/3jo9f966fb/e+n2/33p - 9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/l+34/5nu - +P+a7vj/nO74/5/v+P+g7/j/oe/4/6Tw+f+l8Pn/p/D5/6jw+f+q8fn/q/H5/6zx+f+t8fn/rfH5/63x - +f+t8fn/rPH5/6vx+f+q8fn/qPD5/6fw+f+l8Pn/pPD5/6Hv+P+g7/j/nu/4/5zu+P+a7vj/me74/5ft - +P+V7fj/k+33/5Lt9/+Q7Pf/juz3/4zs9/+K6/f/iev3/0m2+v9Dsfz/RrL8/0q0/P9Otfz/Ubf8/1W4 - /P8lT2v/ESIt/0mRwf9jvvz/Zb/8/0mHsP8AAAD/FCIs/0FKUP8AAAD/BgYG/4GDg//d4OD/3+Pj/+Ll - 5f/k5+f/5+np/+nr6//r7e3/7e/v//Dx8f/Go4f/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/zKmO//z6 - +f//////////////////////////////////////4Mq5/6ltPv+lZzb/xbep/+fq6v////////////// - //////////////////+0zOX/FWrC/xVqwv8VasL/FWrC/xVqwv8VasL/A8Tm/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xDY8P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O+H2/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/z3P4/9L4fL/TeHy/0/h8/9R4vP/U+Lz/1Xi8/9W4/P/WePz/1vj8/9c5PP/XuT0/2Dk - 9P9i5fT/ZOX0/2Xl9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp - 9v9+6fb/f+n2/4Lq9v+D6vb/her2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+U7ff/lu34/5ju - +P+a7vj/m+74/53u+P+f7/j/oe/4/6Pv+f+k8Pn/pvD5/6jw+f+q8fn/q/H5/63x+f+u8fn/sPL5/7Dy - +f+w8vn/r/L5/67x+f+t8fn/q/H5/6nw+f+o8Pn/pvD5/6Tw+f+i7/j/oe/4/5/v+P+d7vj/m+74/5nu - +P+Y7vj/le34/5Tt9/+S7ff/kOz3/47s9/+M7Pf/i+v3/4nr9/9eyfr/RLH8/0iz/P9Mtfz/T7b8/1O4 - /P8ya5H/NW2T/125+P9hvvz/Zb/8/2jA/P8sUGj/AAAA/zJXcP9Qfp3/AAAA/wAAAP8KCgr/srW1/+Dk - 5P/j5ub/5ejo/+jq6v/q7e3/7e/v/+/x8f/y8/P/8/Pz/8SdgP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+udkr/0rOb/+vd0v/28Ov/+PPv/+/k3P/awa3/t4Zf/6RlNP+maTn/xK+e/9fb2//8/Pz///////// - ////////////////////////RojM/xRpwf8UacH/FGnB/xRpwf8UacH/EnTG/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8u4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x7E - 3f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HrAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wGNqv9H3/H/TOHy/03h8v9P4fP/UuLz/1Pi8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k - 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3Xo9f926PX/eOj1/3vp - 9v986fb/fun2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Ds9/+T7ff/le34/5bt - +P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx+f+u8fn/sPL5/7Hy - +f+y8vr/svL6/7Hy+f+w8vn/rfH5/6zx+f+q8fn/qPD5/6bw+f+k8Pn/o+/5/6Hv+P+f7/j/ne74/5vu - +P+a7vj/mO74/5Xt+P+U7ff/ku33/5Ds9/+O7Pf/jOz3/4vr9/+J6/f/dtz4/0ay/P9KtPz/TrX8/1G3 - /P9Rr+//U67s/1y7/P9gvfz/ZL/8/2fA/P9ovvj/DBUb/wAAAP9ZmcT/Xp7J/wAAAP8AAAD/AAAA/x8f - H//S1dX/4+bm/+bp6f/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+Pj/2MCt/6xyRv+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+vflf/zsS6/9bb2//v8fH///////// - ////////////////////////2+bw/xJowP8SZ7//Eme//xJnv/8SZ7//Eme//wqY1P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo - +/8ElLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICkDgCCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCLqf8Awt3/LNvw/0zh8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/13k - 9P9e5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/23m9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo - 9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s9/+R7Pf/k+33/5Xt - +P+X7fj/mO74/5ru+P+c7vj/nu/4/6Dv+P+h7/j/pPD5/6bw+f+n8Pn/qfD5/6vx+f+t8fn/r/L5/7Dy - +f+y8vr/tfP6/7Ty+v+y8vr/sPL5/67x+f+s8fn/qvH5/6jw+f+m8Pn/pPD5/6Pv+f+h7/j/n+/4/53u - +P+b7vj/mu74/5ju+P+V7fj/lO33/5Lt9/+Q7Pf/juz3/4zs9/+L6/f/iev3/4bq9/9KtPv/S7T8/0+2 - /P9Tt/z/V7n8/1q7/P9evPz/Yr78/2a//P9pwfz/SIGn/wAAAP8ZKzj/dsb9/2Sk0P8AAAD/AAAA/wAA - AP8AAAD/Ozw8/+Ll5f/n6en/6evr/+zu7v/v8PD/8fPz//T19f/39/f/+fr6//v8/P/59vP/07af/6x0 - SP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7F/WP/Muar/3N/f/9nd3f/k5+f///////// - /////////////////////////////1mSz/8RZr7/EWa+/xFmvv8RZr7/EWa+/xFmvv8DweX/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdry/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8z2/H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFoUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKEbAISimQCDof8Ag6H/AIOh/wCE - ot0Ag6HTAIOhzACDocwAg6HMAIOhzACDocwAg6HEAIOhuwCDobsAg6G7AIOhuwCDobsAgqK0AIOhqgCD - oaoAg6GqAIOhqgCDoaoAg6KkAISimQCEopkAhKKZAISimQCDob0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/D67I/yq50f8qudH/KrnR/yq50f8rutL/L8DX/y/A1/8ivNX/ALPO/wCzzv8AtM//ALnU/wC5 - 1P8AudT/ALnU/wC92P8A0uv/ANTt/wfV7f9J4PL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj - 8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3fo - 9f946PX/e+n2/33p9v9+6fb/gOr2/4Lq9v+E6vb/huv2/4jr9/+J6/f/i+v3/43s9/+P7Pf/kez3/5Pt - 9/+V7fj/l+34/5ju+P+a7vj/nO74/57v+P+g7/j/oe/4/6Tw+f+m8Pn/p/D5/6nw+f+r8fn/rfH5/6/y - +f+w8vn/svL6/7Py+v+y8vr/sfL5/7Dy+f+t8fn/rPH5/6rx+f+o8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v - +P+d7vj/m+74/5nu+P+Y7vj/le34/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iuv3/4nr9/+H6/f/Ysr5/0y1 - /P9Qtvz/VLj8/1i6/P9cu/z/YL38/2S//P9nwPz/ab/4/xIgKv8AAQH/V5S9/3jH/f9mpc//AAAA/wAA - AP8AAAD/AAAA/wAAAP9eYGD/5+np/+nr6//s7u7/7/Dw//Hz8//09fX/9/f3//n6+v/8/Pz//f39//z8 - /P/4+fj/5NbL/86ymv/Foob/vpRz/76Xd//DoYf/y7Ke/9zVzv/h5OT/3+Li/9zg4P/g4+P//f39//// - /////////////////////////////9Df7P8PZb3/D2W9/w9lvf8PZb3/D2W9/w9lvf8Ofcf/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zbj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/FrfQ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoewAgKoGAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJkKAIOh8gCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wKnwv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w3X8P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/J9vw/07h8/9P4fP/UuLz/1Ti8/9V4vP/V+Pz/1nj - 8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo - 9f936PX/eOj1/3vp9v996fb/fun2/4Dq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Hs - 9/+T7ff/le34/5bt+P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx - +f+t8fn/r/L5/7Dy+f+x8vn/sPL5/7Dy+f+u8fn/rfH5/6vx+f+p8Pn/p/D5/6bw+f+k8Pn/ou/4/6Dv - +P+f7/j/nO74/5ru+P+Z7vj/l+34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr9/+I6/f/h+v3/3rg - 9/9Ntfz/Ubf8/1W5/P9auvz/Xrz8/2G+/P9lv/z/acH8/zxri/8AAAD/M1dw/3jH/f97yP3/XZW7/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AgIC/4CCgv/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+fn/+vr6//r7 - +//6+vr/+Pn5//X29v/z9PT/8fLy/+7w8P/r7e3/6evr/+bp6f/k5+f/4eTk/97i4v/i5eX/+/z8//// - //////////////////////////////r7/P8sdsP/DmO8/w5jvP8OY7z/DWO7/w1ju/8NY7v/BbPe/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v9N6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/R+b6/wGIp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GSAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaAAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AiKX/Kd3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8y4fb/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7f9I4PL/T+Hz/1Li8/9U4vP/VeLz/1fj - 8/9Z4/P/W+Pz/13k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn - 9f916PX/d+j1/3jo9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s - 9/+Q7Pf/k+33/5Xt+P+W7fj/mO74/5ru+P+c7vj/ne74/5/v+P+h7/j/o+/5/6Tw+f+m8Pn/qPD5/6nw - +f+r8fn/rPH5/63x+f+u8fn/rvH5/67x+f+t8fn/rfH5/6vx+f+q8fn/qPD5/6bw+f+l8Pn/o+/5/6Hv - +P+f7/j/nu/4/5zu+P+a7vj/mO74/5ft+P+V7fj/k+33/5Hs9/+P7Pf/juz3/4vr9/+J6/f/iOv3/4br - 9v+E6vb/XMT6/1O3/P9Xufz/Wrv8/1+8/P9jvvz/Z8D8/1mi0v8EBwn/IzxN/3XE+v96yP3/fcn9/1B+ - nf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/nZ+f/+rt7f/t7+//7/Hx//Lz8//09fX/9fb2//f4 - +P/3+Pj/9/j4//b39//09fX/8vPz//Dx8f/t7+//6+3t/+jq6v/l6Oj/4+bm/+Dk5P/n6en//f39//// - //////////////////////////////////92pNL/DGK7/wxiuv8MYrr/DGK6/wxiuv8MYrr/DXvF/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8h3fT/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/yrQ5v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhMQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wm41P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tuj8/wrW - 7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/JNrw/0/h8/9S4vP/VOLz/1Xi - 8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Hn - 9f9z5/X/dej1/3fo9f946PX/e+n2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+J6/f/i+v3/43s - 9/+O7Pf/kOz3/5Lt9/+U7ff/le34/5ju+P+a7vj/m+74/5zu+P+f7/j/oO/4/6Lv+P+k8Pn/pfD5/6bw - +f+o8Pn/qfD5/6vx+f+r8fn/rPH5/6zx+f+s8fn/q/H5/6vx+f+p8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv - +P+g7/j/n+/4/53u+P+b7vj/mu74/5ju+P+W7fj/lO33/5Lt9/+Q7Pf/juz3/43s9/+L6/f/iev3/4fr - 9/+F6vb/hOr2/3be9/9TuPz/V7n8/1y7/P9gvfz/ZL/8/2S48f8SISr/KUhd/3HA9/94x/3/fMn9/3/K - /f82VWn/AAAA/wsRFf8AAAD/AAAA/wAAAP8AAAD/AAAA/wsLC/+lqKj/6+3t/+7w8P/w8fH/8vPz//P0 - 9P/09fX/9Pb2//T19f/z9PT/8vPz//Dx8f/u8PD/7O7u/+ns7P/n6ur/5Ofn/+Pm5v/x8vL///////// - //////////////////////////////////+1zOL/CmC5/wpguf8KYLn/CmC5/wpguf8KYLn/CmG6/w69 - 4/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Tuj8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8Nnrv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCC - odcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/NuL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f9G4PL/UuLz/1Ti - 8/9V4vP/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n - 9f9x5/X/c+f1/3Xo9f926PX/eOj1/3rp9v986fb/fun2/3/p9v+C6vb/g+r2/4Xq9v+H6/f/iev3/4vr - 9/+M7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/me74/5ru+P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw - +f+l8Pn/pvD5/6jw+f+o8Pn/qfD5/6rx+f+q8fn/qvH5/6nw+f+o8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv - +P+h7/j/n+/4/53u+P+c7vj/mu74/5ju+P+X7fj/le34/5Pt9/+R7Pf/kOz3/47s9/+M7Pf/iuv3/4nr - 9/+H6/f/her2/4Pq9v+B6vb/Ysj6/1i6/P9cu/z/Yb38/2W++/8pTGP/RHmc/3HE/f91xv3/ecj9/33J - /f+By/3/FSAo/wAAAP8oOkf/AAAA/wAAAP8OFBj/Exgb/wAAAP8AAAD/DQ0N/6utrf/s7u7/7e/v/+/x - 8f/w8vL/8fPz//Hz8//x8/P/8PLy/+/x8f/u8PD/7O7u/+rs7P/o6ur/5ejo/+vt7f/6+/v///////// - ///////////////////////////////////R3ej/C2C5/wlfuP8JX7j/CV+4/wlfuP8JX7j/CV+4/xCS - z/8J1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8/4PX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoHEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAe5eHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xDG3v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/wjW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdjv/1Li - 8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl9P9q5vT/a+b1/23m - 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f966fb/e+n2/33p9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4jr - 9/+K6/f/i+v3/47s9/+P7Pf/kez3/5Pt9/+V7fj/lu34/5ju+P+a7vj/m+74/5zu+P+e7/j/oO/4/6Hv - +P+i7/j/pPD5/6Xw+f+m8Pn/pvD5/6fw+f+o8Pn/qPD5/6jw+f+n8Pn/pvD5/6bw+f+k8Pn/pPD5/6Lv - +P+h7/j/n+/4/57v+P+c7vj/mu74/5nu+P+Y7vj/le34/5Tt9/+T7ff/kOz3/4/s9/+N7Pf/i+v3/4nr - 9/+I6/f/huv2/4Tq9v+C6vb/ger2/3nj9/9au/z/Xbz8/2G+/P9Un9L/Xqvf/27D/P9yxf3/dsb9/3rI - /f9+yv3/barS/wAAAP8JDhH/P11x/wAAAP8AAAD/GSQr/0NecP8AAAD/AAAA/wAAAP8PDw//paam/+vt - 7f/s7u7/7e/v/+7w8P/v8PD/7vDw/+3v7//s7u7/6+3t/+rs7P/p6+v/7vDw//j5+f////////////// - ///////////////////////////////////a5Oz/EGK4/wddt/8HXbf/B123/wddt/8HXbb/B122/wpw - vv8Y0uz/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zvk+P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Ir3V/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAgJ8QAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAXHEkAF1y6QCBnv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Al7P/OuT4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f853vH/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2vm - 9f9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4br - 9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/mO74/5ru+P+c7vj/ne74/5/v - +P+f7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+l8Pn/pfD5/6bw+f+l8Pn/pfD5/6Tw+f+k8Pn/o+/5/6Hv - +P+h7/j/n+/4/57v+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr - 9/+J6/f/h+v3/4Xq9v+D6vb/gur2/3/p9v9+6fb/bNP5/168/P9ivvz/ZsD8/2rB/P9vw/z/c8X9/3fH - /f97yP3/gMr9/zRRZP8AAAD/Vn+b/zxXaf8AAAD/AAAA/yo6Rf91o8D/AAAA/wAAAP8AAAD/AAAA/wwM - DP+RkZH/7/Hx/+7w8P/t7+//7O7u/+3v7//u8PD/8PLy//T19f/4+fn//v7+//////////////////// - ///////////////////////////////////M2uj/DmG3/wZctf8GXLX/Bly1/wVctf8FXLX/BVy1/wZi - t/8Xw+b/FNfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPZ8P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Tej7/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6CUAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW20cAFpv4QBab/8Acoz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xTL4/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Tej8/wvX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Ctbu/0/h8/9U4vP/VuPz/1jj8/9a4/P/XOTz/17k9P9g5PT/YeT0/2Pl9P9l5fT/Z+X0/2nm - 9P9q5vT/bOb1/27n9f9w5/X/cef1/3Pn9f926PX/d+j1/3no9v976fb/fen2/37p9v+A6vb/gur2/4Tq - 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kez3/5Pt9/+U7ff/le34/5ju+P+Z7vj/mu74/5zu - +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+i7/j/o+/5/6Pv+f+j7/n/o+/5/6Pv+f+i7/j/oe/4/6Hv - +P+f7/j/n+/4/53u+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+O7Pf/jez3/4vr - 9/+J6/f/iOv3/4br9v+E6vb/gur2/4Hq9v9/6fb/fen2/3vp9v9myPv/Yr78/2fA/P9rwvz/b8P8/3PF - /f93x/3/fMn9/3e97P8FBwn/KT5M/43Q/f8wRVP/AAAA/wAAAP9HYXP/ndX7/wwRFP8AAAD/AAAA/wAA - AP8AAAD/BAQE/11dXf/29vb///////////////////////////////////////////////////////// - //////////////////////////////3+/v+nwtz/BVy0/wRbtP8EWrT/BFq0/wRatP8EWrT/BFq0/wRc - tf8Us93/Gtjv/wvW7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f885Pn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y7K4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIakKgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVW0VAFpv2gBab/8AWm//AGF3/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Amrf/PuX5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P804vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8m2vD/VOLz/1bj8/9Y4/P/WuPz/1vj8/9e5PT/X+T0/2Hk9P9i5fT/ZeX0/2bl - 9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq - 9v+C6vb/hOr2/4br9v+I6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5nu - +P+a7vj/m+74/5zu+P+d7vj/nu/4/5/v+P+g7/j/oO/4/6Hv+P+h7/j/oe/4/6Hv+P+h7/j/oO/4/5/v - +P+f7/j/nu/4/5zu+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/jez3/4vr - 9/+K6/f/iOv3/4fr9/+F6vb/g+r2/4Lq9v9/6fb/fun2/33p9v976fb/duP3/2TB+/9nwPz/a8L8/2/D - /P9zxf3/eMf9/3zJ/f86XHP/DxYc/3275v+N0P3/Hyw2/wAAAP8AAAD/a5Ks/6HY/f8yRFD/AAAA/wAA - AP8KDQ//AAAA/wAAAP8AAAD/Dg4O/4KCgv/n5+f///////////////////////////////////////// - //////////////////////////////Dz9v9Wjcf/A1mz/wJZs/8CWbP/Almz/wJZs/8CWbP/Almz/wJa - s/8Tpdb/HNnv/xrY7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vH/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/8KkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShuAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVXcPAFpv0gBab/8AWm//AFpv/wBab/8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xXO - 5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w/Y8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/0Xg8v9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl - 9P9m5fT/Z+X0/2rm9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p - 9v+A6vb/gur2/4Tq9v+F6vb/h+v3/4nr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt - +P+X7fj/mO74/5ru+P+a7vj/m+74/5zu+P+d7vj/nu/4/57v+P+f7/j/n+/4/5/v+P+f7/j/nu/4/57v - +P+d7vj/nO74/5zu+P+a7vj/mu74/5ju+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs - 9/+K6/f/iev3/4fr9/+F6vb/hOr2/4Lq9v+A6vb/f+n2/33p9v976fb/eun2/3jo9f9y4Pb/aMP7/2vC - /P9vw/z/c8X9/3fH/f9np9L/DRUa/3Gu2P+Jzv3/jdD9/wsQE/8AAAD/BwkL/5fP8/+e1/3/VnWL/wAA - AP8AAAD/P1tu/wAAAP8AAAD/AAAA/wAAAP8AAAD/AgIC/zY2Nv97e3v/vLy8//b29v////////////// - ////////////////////////+fr6/6jD3P8NX7T/AVix/wFYsf8BWLH/AVix/wFYsf8BWLH/AVix/wFY - sf8RndL/Hdnv/xvZ7/8W2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/Qeb6/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8xzeT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - okIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAXXQLAFtvyABab/8AWm//AFpv/wBab/8AWm//AGJ5/wCCoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ambb/OuP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P885Pn/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTs/wCuyf8DkK3/DJGt/yqxyf9X4fD/W+Pz/1zk8/9e5PT/YOT0/2Ll - 9P9k5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9u5/X/cOf1/3Hn9f9z5/X/dej1/3fo9f946PX/e+n2/3zp - 9v996fb/f+n2/4Hq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/ku33/5Pt - 9/+U7ff/le34/5ft+P+Y7vj/mO74/5ru+P+a7vj/m+74/5zu+P+c7vj/nO74/5zu+P+c7vj/nO74/5zu - +P+c7vj/m+74/5ru+P+a7vj/me74/5ju+P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs - 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Lq9v+B6vb/f+n2/33p9v986fb/e+n2/3jo9f936PX/dej1/2/d - 9/9rwfz/b8P8/3PF/f90wvf/JDpJ/2ahyf+EzP3/iM79/4C95/8AAAD/AAAA/z5WZ/+a1f3/m9b9/3ak - wv8AAAD/AAAA/2eWtv8OFRr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/Pj4+/2xs - bP+YmJj/tLS0/8rKyv/Fxsb/mKq7/xpRi/8APHv/AFSr/wBXsf8AV7H/AFex/wBXsf8AV7H/AFex/wBY - sf8RndL/H9nv/xzZ7/8b2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6Pv/CpGu/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ocQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAASW0HAFpvvQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8GgZz/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/xDH4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x3c8/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wClwP8Ag6H/AIOh/wCDof8Ag6H/I6nB/1rj8/9c5PP/XuT0/2Dk - 9P9h5PT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3To9f926PX/eOj1/3no - 9v976fb/fen2/37p9v+A6vb/gur2/4Pq9v+E6vb/huv2/4jr9/+J6/f/i+v3/4zs9/+O7Pf/j+z3/5Ds - 9/+R7Pf/k+33/5Tt9/+V7fj/le34/5ft+P+Y7vj/mO74/5nu+P+Z7vj/mu74/5ru+P+a7vj/mu74/5ru - +P+a7vj/me74/5nu+P+Y7vj/mO74/5ft+P+V7fj/le34/5Pt9/+T7ff/kez3/5Ds9/+P7Pf/juz3/4zs - 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/f+n2/37p9v996fb/e+n2/3no9v946PX/duj1/3To - 9f9z5/X/btv3/27E/P9yxf3/Qm6N/2iq1/9/yv3/g8z9/4fN/f9klbf/AAAA/wYICv+Dut//l9T9/5fU - /f+Hv+T/AAAA/wAAAP9Ygp7/RGd+/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAIEf8AL1//AFKn/wBXsf8AV7H/AFex/wFb - s/8Wrdr/INnv/x7Z7/8c2e//Gtjv/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0no - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LMje/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAZmYFAFlvsQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Qd4v/Ocrh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ak6//NeL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/BNXu/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC81v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9W3+//W+Pz/13k - 9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo - 9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iOv3/4nr9/+L6/f/jOz3/43s - 9/+O7Pf/kOz3/5Hs9/+S7ff/k+33/5Tt9/+V7fj/le34/5bt+P+X7fj/l+34/5ju+P+Y7vj/mO74/5ju - +P+Y7vj/mO74/5ft+P+X7fj/lu34/5Xt+P+V7fj/lO33/5Pt9/+S7ff/kOz3/5Ds9/+O7Pf/jez3/4vr - 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/e+n2/3rp9v946PX/duj1/3Xo - 9f9z5/X/cef1/3Dn9f9u3/b/XqbT/2y36v95yP3/fcn9/4HL/f+Fzf3/RmqC/wAAAP9Ue5X/kdL9/5PS - /f+U0/3/kdD6/wIDBP8AAAD/UnqV/3m34P8BAgL/AAAA/wAAAP8BAgL/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAGDP8AMmf/AFew/wVq - uv8cwOL/Idrv/x/Z7/8d2e//HNnv/xbY7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y/h - 9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sub6/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVVUDAFpupABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Ncob/SN3w/0/p/P8Xobv/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wq92P9N6fv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yzg - 9f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO5v8Aiqf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Utvt/1vj - 8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3To - 9f926PX/eOj1/3no9v976fb/fOn2/33p9v9/6fb/ger2/4Lq9v+E6vb/her2/4fr9/+I6/f/iev3/4rr - 9/+L6/f/jez3/47s9/+P7Pf/kOz3/5Hs9/+S7ff/k+33/5Pt9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt - +P+V7fj/le34/5Xt+P+V7fj/lO33/5Tt9/+T7ff/k+33/5Lt9/+Q7Pf/kOz3/4/s9/+O7Pf/jOz3/4vr - 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3rp9v946PX/d+j1/3Xo - 9f9z5/X/cuf1/3Hn9f9v5/X/beb1/2vg9/9yyvv/eMf9/3zJ/f+Ayv3/g8z9/yAwPP8qP03/jND9/47Q - /f+Q0f3/kNH9/5DR/f8IDA//AAAA/1B4lP+Fzf3/Gyk0/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wQi - Mv8duM3/Itrv/yHa7/8f2e//Hdnv/xvZ7/8L1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xLZ - 8P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yW81f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAABAFtvmABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8LboP/Rtns/0/p/P9P6fz/Qdfs/wGE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ajar/KN3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9O6Pz/Edjw/wDU7f8A1O3/ANTt/wDU7f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/BpOv/1jj - 8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Hn - 9f9z5/X/dej1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+B6vb/gur2/4Tq9v+F6vb/h+v3/4fr - 9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Pt9/+T7ff/k+33/5Pt - 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Ds9/+Q7Pf/juz3/47s9/+N7Pf/i+v3/4vr - 9/+J6/f/iOv3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v946PX/d+j1/3bo - 9f906PX/cuf1/3Hn9f9v5/X/buf1/2zm9f9q5vT/aeX0/3DS+v96yP3/fsn9/3e86v8THST/fb7q/4nO - /f+Lz/3/jND9/4zQ/f+Mz/3/AwQF/wAAAP9KcYz/g8z9/z5hev8AAAD/AAAA/wAAAP8RHCX/AAAA/wEC - Av8dNkf/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AgkK/xaQnf8g2e//Htnv/xzZ7/8Z2O//AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU - 7f9C5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Ph9f8ChaT/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpwiwBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Jan//RNXo/0/p/P9P6fz/T+n8/0/p - /P8eqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOrx/9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0Lm+v8C1O3/ANTt/wDU7f8AtM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/zbQ - 5v833/L/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n - 9f9x5/X/cuf1/3Pn9f916PX/d+j1/3jo9f966fb/e+n2/33p9v9+6fb/f+n2/4Hq9v+C6vb/hOr2/4Tq - 9v+G6/b/h+v3/4jr9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+O7Pf/j+z3/5Ds9/+Q7Pf/kOz3/5Ds - 9/+R7Pf/kez3/5Hs9/+R7Pf/kOz3/5Ds9/+Q7Pf/j+z3/4/s9/+O7Pf/juz3/43s9/+L6/f/i+v3/4rr - 9/+J6/f/h+v3/4fr9/+F6vb/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo - 9f906PX/c+f1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aeb0/2fl9P9m5fT/atv3/3nL/P9OfJz/aqfQ/4TM - /f+Gzf3/iM79/4nO/f+Jzv3/hMbz/wAAAP8AAAD/W46w/4HL/f9Wiq7/AAAA/wAAAP8AAAD/L1Ns/wAA - AP8AAAD/JEZc/zhvlP8BAwT/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAQH/EXWA/x3Z7/8c2e//ENfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8p3/T/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8WpsD/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/gCEojQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvfABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8HZnv/QtHl/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Q9js/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/GdLp/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/KN/0/wDU7f8AyOP/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ya0 - zv9P6fz/Ten7/0bj9v9a4/P/XOTz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9p5vT/aub0/2zm - 9f9u5/X/b+f1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3rp9v976fb/fen2/37p9v9/6fb/ger2/4Lq - 9v+D6vb/hOr2/4Xq9v+H6/f/h+v3/4nr9/+J6/f/iuv3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/47s - 9/+O7Pf/juz3/4/s9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iev3/4nr - 9/+I6/f/h+v3/4br9v+E6vb/hOr2/4Lq9v+B6vb/f+n2/37p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/2zm9f9r5vX/aub0/2jm9P9m5fT/ZeX0/2Pl9P9i4/T/XbXT/37L - /f+By/3/g8z9/4TM/f+Fzf3/hc39/2Wbv/8AAAD/AAAA/2uq1P9+yv3/aqzZ/wAAAP8AAAD/AAAA/1KS - vP8EBQb/AQEB/wEBAv9PnNH/P4Kv/wMGCP8AAAD/AAAA/wAAAP8BAQL/Dig6/wABAv8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8MW2T/G9ju/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8Q2PD/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P800un/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvbgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8FY3f/P8zg/0/p/P9P6fz/SuDz/ze/ - 0v8knLD/EnqO/wJdcv8Ab4n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCYtP824vf/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o/P8P1+7/AJWy/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xCY - tP9N5/r/T+n8/0/p/P9P6fz/Tub4/1rj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/aOb0/2rm - 9P9r5vX/bOb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p - 9v+A6vb/gur2/4Lq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iuv3/4rr9/+L6/f/i+v3/4vr - 9/+M7Pf/jOz3/4zs9/+M7Pf/jOz3/4zs9/+M7Pf/i+v3/4vr9/+L6/f/iuv3/4rr9/+J6/f/iev3/4fr - 9/+H6/f/huv2/4Xq9v+E6vb/g+r2/4Lq9v+A6vb/f+n2/37p9v996fb/e+n2/3rp9v946PX/d+j1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Pl9P9i5fT/YOT0/17k - 9P9h4fX/btX5/37L/P+By/3/gcv9/4LL/f9DaoT/AAAA/w4WHP98x/v/e8j9/3O+8v8AAAD/AAAA/wAA - AP9jsuf/KEBQ/w8PD/8NDQ3/Kk1l/1q6/P9Ci73/CQ0Q/wQEBP8DAwP/AQEB/wouM/8mtcb/E15n/wIJ - Cv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wheaP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8E1e7/ROb5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J5/r/B46r/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDofUAgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpwYABab/4AWm//AFpv/wBab/8AWm//AFpv/wBab/8DX3X/NbrO/zW6zv8imKz/D3WJ/wFb - cP8AWm//AFpv/wBab/8AWm//AFtx/wB+mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CLzW/0rn - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/IrXO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOH - pf9D2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6Pv/WuT1/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl - 9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/c+f1/3To9f926PX/d+j1/3jo9f966fb/e+n2/3zp - 9v996fb/f+n2/3/p9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+I6/f/iev3/4nr - 9/+J6/f/iuv3/4rr9/+K6/f/iuv3/4rr9/+K6/f/iuv3/4nr9/+J6/f/iev3/4jr9/+I6/f/h+v3/4fr - 9/+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v9/6fb/f+n2/33p9v986fb/e+n2/3rp9v946PX/d+j1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/1/k - 9P9e5PT/W+Pz/1rj8/9b4vT/aNj3/3XP+v9+yv3/FyQu/wAAAP84W3L/esj9/3jH/f91xv3/AQIC/wAA - AP8HDRH/asH8/0l9oP8eHh7/HBwc/x4kKP9MsO//PbLs/yyNqv8TFxj/ERER/w8PD/8NDQ3/FktR/yrY - 7f8ozeD/FWNs/wUHCP8CAgL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AHB9/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HbPM/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtwVABab/wAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AZ37/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ - p/8e1+7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tp/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8uvtb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Do+/9Y5fb/XuT0/2Dk9P9h5PT/YuX0/2Tl - 9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/cOf1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3no - 9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr - 9/+H6/f/h+v3/4fr9/+I6/f/iOv3/4jr9/+I6/f/iOv3/4fr9/+H6/f/h+v3/4fr9/+G6/b/her2/4Xq - 9v+E6vb/hOr2/4Lq9v+C6vb/ger2/3/p9v9/6fb/fun2/33p9v976fb/e+n2/3no9v946PX/duj1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/1/k - 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/RLLC/wAAAP8FCgz/Ybjg/3HL+/9yyPz/cMf8/wAA - AP8AAAD/HTpK/13I+f9Rtdv/LCws/yoqKv8oKCj/M6e0/zfd8f813fH/LJai/x8fH/8eHh7/HBwc/xoa - Gv8heIP/Kdvw/yfb8P8iucv/FUlP/w8PD/8NDQ3/CwsL/wkJCf8ICAj/BgYG/wQFBf8BhZX/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N9jt/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HaAJKSBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtwSQBab/kAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wB2kv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AJu3/zjj+P9P6fz/T+n8/0/p/P9P6fz/TOX4/wyTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8XoLv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1bm+P9e5PT/YOT0/2Ll - 9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3bo - 9f946PX/eej2/3rp9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Lq9v+D6vb/hOr2/4Tq - 9v+E6vb/hOr2/4Xq9v+F6vb/huv2/4br9v+G6/b/huv2/4Xq9v+F6vb/her2/4Tq9v+E6vb/hOr2/4Pq - 9v+C6vb/gur2/4Lq9v+A6vb/f+n2/3/p9v9+6fb/fen2/3zp9v976fb/eun2/3jo9f936PX/duj1/3Xo - 9f9z5/X/cuf1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2nm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/xxOVP8AAAD/K3uE/0zh8v9K4PL/SeDy/0XZ - 6v8AAAD/AAAA/yV+if9B3/L/Pt7x/ztPUf84ODj/Nzc3/zaDjP823fH/NN3x/zLc8f8vdX3/LCws/yoq - Kv8oKCj/Jikp/yequf8m2vD/JNrw/yPa7/8fnKr/Gykr/xkZGf8XFxf/FhYW/xQUFP8SEhL/DxcY/wKz - yP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Dtjv/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOb5/wePq/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AX3X/AIGe/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8IutX/Sej7/0/p/P9P6fz/T+n8/yGux/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Giqj/R9/z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/U+j6/17k - 9f9g5PT/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/cuf1/3Pn - 9f916PX/duj1/3fo9f946PX/eej2/3vp9v976fb/fOn2/33p9v9+6fb/f+n2/3/p9v+A6vb/ger2/4Lq - 9v+C6vb/gur2/4Lq9v+D6vb/g+r2/4Pq9v+D6vb/hOr2/4Pq9v+D6vb/g+r2/4Pq9v+C6vb/gur2/4Lq - 9v+B6vb/gOr2/3/p9v9/6fb/fun2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f926PX/duj1/3To - 9f9z5/X/cuf1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9U4vP/U+Lz/0S8yv8BAgL/G09V/03g8f9L4fL/SeDy/0jg - 8v86uMf/AAAA/wIFBv88zd7/P9/x/z7e8f9Eg4v/R0dH/0VFRf8/cnn/Nd3x/zPd8f8y3PH/Mdjr/zhU - WP84ODj/Nzc3/zU1Nf8yRUf/JtHm/yTa8P8h2u//INnv/x/H2/8kV13/JiYm/yQkJP8jIyP/ISEh/x8f - H/8YOz//Ac3l/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/B9bu/0Tm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xatxv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOhowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFttKgBab/AAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/oAFluqwBzjqkAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIel/xfT6v9P6fz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9R6Pv/XOX1/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/b+f1/3Hn - 9f9y5/X/c+f1/3To9f926PX/duj1/3jo9f946PX/eej2/3vp9v976fb/fOn2/33p9v996fb/fun2/3/p - 9v9/6fb/f+n2/4Dq9v+A6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+A6vb/gOr2/3/p - 9v9/6fb/f+n2/37p9v996fb/fen2/3zp9v976fb/e+n2/3rp9v956Pb/eOj1/3fo9f926PX/dej1/3Pn - 9f9z5/X/cef1/3Dn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/8aSE7/GERJ/0va6v9L4fL/SuDy/0jg - 8v9G4PL/LpWh/wAAAP8WSlH/QN/y/z7e8f883vH/Rqm0/1VVVf9TU1P/TWhr/zTd8f8y3PH/MNzw/y/c - 8P80u8r/RkhI/0VFRf9DQ0P/QUFB/zSDjP8j2u//Idrv/x/Z7/8d2e//Gtfu/xqHlP8yMzP/MTEx/y8v - L/8tLS3/Kysr/xhvev8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/A9Xu/z7l+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yvN4/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh5wCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABbb7cAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnDgAFpwogBbb2UAWWwoAAAAAAAAAAAA//8BAISgugCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AlLH/LN/0/0ni9f8Hjar/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/HqrD/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/o/P9Y5/j/YOT0/2Ll9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/bOb1/27n - 9f9v5/X/cOf1/3Hn9f9z5/X/c+f1/3Xo9f926PX/duj1/3jo9f946PX/eej2/3rp9v976fb/e+n2/3zp - 9v996fb/fen2/33p9v9+6fb/fun2/37p9v9/6fb/f+n2/3/p9v9/6fb/f+n2/3/p9v9/6fb/fun2/37p - 9v996fb/fen2/33p9v986fb/e+n2/3vp9v966fb/eej2/3jo9f946PX/d+j1/3bo9f916PX/dOj1/3Pn - 9f9y5/X/cef1/2/n9f9v5/X/beb1/2zm9f9q5vT/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/88qbf/HVJZ/0vd7f9M4fL/SuDy/0jg - 8v9H4PL/ReDy/yJye/8AAAD/L6Ox/z7e8f893vH/PN7x/0HG1v9kZGT/YmJi/1tvcf8z3fH/Mdzw/zDc - 8P8t3PD/LNvw/0eEi/9TU1P/UVFR/1BQUP9NUFD/J8bY/yDZ7/8e2e//HNnv/wzW7v8A1O3/FaO0/z1E - RP89PT3/PDw8/zo6Ov84OTn/DLDD/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AdXt/zfj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zzf9P8ChaT/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm/vAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/XAFpumwBabl0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - nh0Ag6LwAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKoxP8Uor3/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/CpCt/0vk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Xo+f9f5PX/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm - 9f9s5vX/beb1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3Xo9f926PX/duj1/3fo9f946PX/eOj1/3no - 9v966fb/e+n2/3vp9v976fb/fOn2/3zp9v986fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3zp - 9v986fb/e+n2/3vp9v976fb/eun2/3no9v946PX/eOj1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Ln - 9f9x5/X/cOf1/2/n9f9u5/X/bOb1/2vm9f9q5vT/aeb0/2fl9P9m5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4fL/OaOw/03h8v9M4fL/SuDy/0ng - 8v9H4PL/ReDy/0Tf8v8RNzz/EDY7/z/f8f8+3vH/PN7x/zre8f863e//cHZ3/3BwcP9ne33/Mtzx/zDc - 8P8u3PD/Ldzw/yvb8P8yydv/YWNj/2BgYP9eXl7/XFxc/0SMlP8f2e//Hdnv/xXY7v8A1O3/ANTt/wDU - 7f8Rtsn/RldZ/0pKSv9ISEj/RkZG/zhdYf8B0en/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fm+v8IlbH/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFpvywBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/0AWm/RAFtwkgBbcFQAWW8XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKgXgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AYSi/zzR5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Uun6/13l9v9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm - 9P9q5vT/aub0/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/duj1/3bo - 9f936PX/eOj1/3jo9f946PX/eej2/3no9v966fb/eun2/3rp9v976fb/e+n2/3vp9v976fb/eun2/3rp - 9v966fb/eej2/3no9v946PX/eOj1/3jo9f936PX/duj1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn - 9f9w5/X/b+f1/27n9f9t5vX/bOb1/2rm9P9q5vT/aOb0/2fl9P9m5fT/ZeX0/2Pl9P9i5fT/YOT0/1/k - 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9M4fL/SuDy/0ng - 8v9I4PL/RuDy/0Tf8v89zd7/AQME/zOxwP8+3vH/PN7x/zve8f853vH/N93x/3GTl/9+fn7/cI2R/zHc - 8P8v3PD/Ldzw/yvb8P8q2/D/Kdvw/1mQlv9ubm7/bGxs/2pqav9obG3/ItLn/xvZ7/8D1e3/ANTt/wDU - 7f8A1O3/ANTt/xG90f9SYWP/VlZW/1VVVf9TU1P/JZmm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8Srcf/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeAAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm/8AFpv/wBab/8AWm//AFpv/wBab/0AWm/HAFtvigBb - b0wAYHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6fv/Wub3/2Ll9P9j5fT/ZOX0/2Xl - 9P9n5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9y5/X/c+f1/3Pn - 9f906PX/dej1/3bo9f926PX/duj1/3fo9f936PX/eOj1/3jo9f946PX/eOj1/3jo9f946PX/eOj1/3jo - 9f946PX/eOj1/3fo9f936PX/duj1/3bo9f926PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn - 9f9v5/X/buf1/23m9f9s5vX/a+b1/2rm9P9p5vT/Z+X0/2fl9P9l5fT/ZOX0/2Ll9P9i5fT/YOT0/1/k - 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/KIiU/xhVXP8+3vH/PN7x/zve8f853vH/ON3x/zbd8f9uq7L/jY2N/3Ki - qP8w3PD/Ltzw/yzb8P8r2/D/Kdvw/yfb8P9Bv87/fX19/3t7e/95eXn/d3d3/z21w/8K1u7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Fb/T/19rbf9jY2P/YWFh/1hnaf8GzeT/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/x3c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8hxt3/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAhKM6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFttOABab58AWm+qAFtvgQBZb0UAXXQLAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICqDACCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/w+Xsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9X5/j/YeX0/2Ll - 9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9v5/X/cef1/3Hn - 9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/dej1/3bo9f926PX/duj1/3bo9f926PX/duj1/3bo - 9f926PX/duj1/3Xo9f916PX/dej1/3To9f9z5/X/c+f1/3Pn9f9y5/X/cef1/3Hn9f9w5/X/b+f1/2/n - 9f9u5/X/beb1/2zm9f9r5vX/aub0/2nm9P9o5vT/Z+X0/2bl9P9l5fT/Y+X0/2Ll9P9h5PT/YOT0/17k - 9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt7x/xNBRv870uT/Pd7x/zze8f853vH/ON3x/zfd8f813fH/Zb7J/5ub - m/9vtLz/Ltzw/y3c8P8r2/D/Kdvw/yjb8P8m2vD/J9jt/4WPkP+JiYn/h4eH/4WFhf9Zoar/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8avtH/bnN0/29vb/9ubm7/OZ2p/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/x/c8/9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u1uz/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6J7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaEuAIOh9gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wOGpP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Po - +v9f5fX/YuX0/2Pl9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n - 9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Ln9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn - 9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Ln9f9y5/X/cef1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m - 9f9s5vX/bOb1/2vm9f9q5vT/aeb0/2jm9P9n5fT/ZuX0/2Xl9P9k5fT/YuX0/2Ll9P9g5PT/X+T0/17k - 9P9d5PT/W+Pz/1rj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0rg - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/zCksv8wq7n/Pd7x/zze8f863vH/Od7x/zfd8f813fH/NN3x/1HE - 0/+UlJT/XL3J/y3c8P8r2/D/Ktvw/ynb8P8m2vD/Jdrw/yTa8P9zrbT/l5eX/5aWlv+UlJT/eZ2h/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y62xf9+fn7/fHx8/3V9fv8KzOP/ANTt/wDU - 7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P833fP/AYel/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKC6AKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWQAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8tvdT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Uej7/1zl9v9h5PT/XeDx/0rM3/9Dxdn/WNjp/2fl9P9n5fT/aOb0/2nm9P9q5vT/a+b1/2zm - 9f9s5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn - 9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/cOf1/2/n9f9v5/X/b+f1/27n9f9t5vX/bOb1/2zm - 9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/XuT0/17k - 9P9c5PP/W+Pz/1rj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Dc7v8yssD/Pt7x/zze8f863vH/Od7x/zfd8f823fH/NN3x/zLc - 8f9By9v/g4OD/0XE0/8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//WrnE/5mZmf+bm5v/nZ2d/5ei - o/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S625/4qKiv+IiIj/Ray4/wDU - 7f8A1O3/ANTt/yTe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P894vf/A4+s/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HgAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISinwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Wn7r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9A1ev/JazF/waIpv8Ag6H/AIOh/wKEo/89vtP/ZeX0/2bl9P9n5fT/aOb0/2nm - 9P9q5vT/aub0/2vm9f9s5vX/bOb1/2zm9f9t5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/b+f1/2/n - 9f9v5/X/b+f1/2/n9f9v5/X/b+f1/2/n9f9u5/X/buf1/27n9f9t5vX/bOb1/2zm9f9s5vX/a+b1/2rm - 9P9q5vT/aeb0/2jm9P9n5fT/Z+X0/2bl9P9l5fT/ZOX0/2Pl9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k - 9P9b4/P/WuPz/1nj8/9Y4/P/VuPz/1bj8/9U4vP/U+Lz/1Li8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8+2+z/Pt7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zLc - 8f8x3PD/NNXo/3Jycv8z0eT/K9vw/ynb8P8o2/D/Jtrw/yTa8P8j2u//Idrv/zzB0f+IiIj/ioqK/4yM - jP+Ojo7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7P92p63/l5eX/4qa - nP8H0en/ANTt/ybe9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5fn/BZa0/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIKhMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBopQAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Giqj/R97y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/S+T4/zDB2P8QmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/1rb7P9l5fT/ZeX0/2bl - 9P9n5fT/Z+X0/2jm9P9p5vT/aub0/2rm9P9q5vT/a+b1/2vm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm - 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9r5vX/a+b1/2rm9P9q5vT/aub0/2nm - 9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9k5fT/YuX0/2Ll9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk - 8/9b4/P/WePz/1nj8/9X4/P/VuPz/1Xi8/9U4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y7c8P9adHf/K9vw/ynb8P8o2/D/Jtrw/yXa8P8j2u//Idrv/yDZ7/8j0OT/d3d3/3l5 - ef97e3v/e35//wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Eszi/4+c - nv+bm5v/QrzL/yrf9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/CaC8/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoEYAg6H+AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8/1Or/IKzG/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof9Ex9r/YuX0/2Pl - 9P9k5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9p5vT/aub0/2rm9P9q5vT/aub0/2rm - 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aeb0/2nm9P9o5vT/Z+X0/2fl - 9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Pl9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9d5PT/XOTz/1vj - 8/9a4/P/WePz/1jj8/9W4/P/VuPz/1Ti8/9T4vP/UuLz/1Hi8/9P4fP/TuHz/03h8v9L4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y7c8P8t3PD/QI2W/yrb8P8p2/D/Jtrw/yXa8P8k2vD/Itrv/yDZ7/8b2e//BNXt/15u - cP9oaGj/ampq/2F3ef8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8yucn/ioqK/3ybnv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5/v/DqjC/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnBIAg6HkAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/HajC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/y/A - 1/8QmLP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T9Pl/2Dk - 9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl9P9l5fT/ZeX0/2bl9P9m5fT/Z+X0/2fl9P9n5fT/Z+X0/2jm - 9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2fl9P9n5fT/Z+X0/2fl9P9m5fT/ZuX0/2Xl - 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Ll9P9h5PT/YOT0/2Dk9P9f5PT/XuT0/13k9P9c5PP/W+Pz/1rj - 8/9Z4/P/WOPz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UuLz/1Dh8/9P4fP/TeHy/03h8v9L4fL/SuDy/0jg - 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/K9vw/zGruv8p2/D/J9vw/yXa8P8k2vD/Itrv/yHa7/8a2O//A9Xt/wDU - 7f9HaW3/V1dX/1paWv9CfYT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/B9bv/2mWm/97e3v/WtLh/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/D6zF/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhvQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GqAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Co+s/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/H6vF/wSI - pf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HqK8/17k - 9P9e5PT/X+T0/2Dk9P9g5PT/YeT0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9Q0OL/Pb3S/03N - 4f9l5fT/ZuX0/2bl9P9m5fT/ZuX0/2bl9P9m5fT/ZuX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZOX0/2Tl - 9P9j5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/1nj - 8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/0/h8/9O4fP/TeHy/0zh8v9K4PL/SeDy/0jg - 8v9H4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p0OT/J9vw/yba8P8k2vD/Itrv/yHa7/8W2O//AtTt/wDU - 7f8A1O3/Mmpw/0ZGRv9JSUn/J4yX/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Dtjv/0Xn+v9T1+f/aW5v/2Gdpf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/DavG/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AYSi/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+P3/y+/1/8Pl7L/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5Ov/1jg - 8P9b4/P/XOTz/13k9P9e5PT/XuT0/1/k9P9g5PT/YOT0/2Dk9P9h5PT/YuX0/2Ll9P8prMP/AIOh/wCD - of8Ag6H/IaO8/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/Y+X0/2Pl9P9j5fT/Y+X0/2Ll9P9i5fT/YuX0/2Ll - 9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1jj - 8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9N4fL/TeHy/0vh8v9K4PL/SeDy/0jg - 8v9G4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p2/D/J9vw/yba8P8k2vD/I9rv/yHa7/8P1+7/ANTt/wDU - 7f8A1O3/ANTt/yFsdf81NTX/ODg4/xOgsf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/GNry/0vo+/9P6fz/T+n8/1WbpP9bZWb/UOb4/0/p/P9P6fz/T+n8/0/p/P9G5/r/DKnE/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh4wCApBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKUfAIOi8ACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/ySyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+1On/H6rE/wSIpf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCat/8AutX/BdTr/zfd - 8f9Z4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/17k9P9e5PT/X+T0/2Dk9P9Gyd3/AIOh/wCD - of8Ag6H/AIOh/wCDof89wNX/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9g5PT/YOT0/2Dk - 9P9g5PT/X+T0/1/k9P9e5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Z4/P/WOPz/1bj - 8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9K4PL/SODy/0fg - 8v9F4PL/ReDy/0Pf8v9C3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/I9rv/xzZ7/8I1e7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Ub3n/JCQk/ycpKv8DyN//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Jd70/07p/P9P6fz/T+n8/0/p/P9O3u//S1ZX/0+5xv9P6fz/T+n8/0/p/P9F5vn/CqXB/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOhwACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6Vsf9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/y6+1v8PlrL/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKrF/wDJ4/8A1O3/ANTt/wDU - 7f8A1O3/Ftjv/0jg8v9Z4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XOTz/13k9P9e5PT/LbHJ/wCD - of8Ag6H/AIOh/wCDof8Ag6H/JanC/1/k9P9f5PT/X+T0/1/k9P9f5PT/X+T0/1/k9P9e5PT/XuT0/17k - 9P9e5PT/XuT0/13k9P9d5PT/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1bj - 8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/1Dh8/9P4fP/TuHz/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0bg - 8v9F4PL/RN/y/0Pf8v9B3/L/Qd/y/z/f8f8+3vH/PN7x/zve8f853vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/Itrv/xPX7v8B1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Cm56/xQUFP8RQ0n/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8H1u//OeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0WUnf9DeoH/T+n8/0/p/P9D5fn/CaK+/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCCoz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdQCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKGpP9B2O3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+j/HqrE/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCbt/8Autb/ANPs/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/JNrw/1Dh8/9Y4/P/WOPz/1nj8/9Z4/P/WuPz/1rj8/9b4/P/W+Pz/yWq - w/8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGlvv9d5PT/XeT0/13k9P9d5PT/XeT0/13k9P9c5PP/XOTz/1zk - 8/9c5PP/W+Pz/1vj8/9b4/P/WuPz/1rj8/9Z4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9W4/P/VeLz/1Ti - 8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9O4fP/TeHy/0zh8v9L4fL/SuDy/0ng8v9I4PL/R+Dy/0Xg - 8v9F4PL/Q9/y/0Lf8v9B3/L/QN/y/z7e8f893vH/PN7x/zve8f853vH/ON3x/zfd8f813fH/NN3x/zLc - 8f8x3PD/MNzw/y/c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/yba8P8f2e//ENfu/wPV7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wBue/8DAwP/AnSB/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8X2/H/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N3e//MENG/0/n+v864vf/Bpq3/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCBo0UAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD - ofkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/SuP2/y691f8OlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKvG/wDK5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1e3/L9zw/1Ti8/9W4/P/V+Pz/1jj8/9Y4/P/WePz/1nj - 8/8lqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8dp8H/WOT2/1vj8/9b4/P/W+Pz/1vj8/9a4/P/WuPz/1rj - 8/9a4/P/WePz/1nj8/9Z4/P/WePz/1jj8/9Y4/P/V+Pz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Pi - 8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0vh8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg - 8v9E3/L/Q9/y/0Hf8v9B3/L/P9/x/z7e8f893vH/PN7x/zre8f853vH/N93x/zfd8f813fH/NN3x/zLc - 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/x/Z7/8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AdYP/AAAA/wCvw/8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU - 7v8u4PX/T+n8/0Xc8P81x97/PNDm/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zJ4gf8qscL/AZCt/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfCACD - odMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P890uj/HqrD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AYSi/xehvP8nxt3/B9Xu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8M1u7/NN3x/1Pi8/9W4/P/VuPz/1bj - 8/9W4/P/JKrD/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9R6Pr/VuX1/1nj8/9Y4/P/WOPz/1jj - 8/9Y4/P/V+Pz/1fj8/9X4/P/VuPz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li - 8/9R4vP/UOHz/0/h8/9P4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0fg8v9G4PL/ReDy/0Tf - 8v9D3/L/Qt/y/0Hf8v9A3/L/Pt7x/z3e8f883vH/O97x/zne8f853vH/N93x/zbd8f813fH/M93x/zLc - 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/Itrv/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AH2L/wAZHP8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y - 7/9B5vr/T+n8/yy80/8BhaL/AIOh/wCDof8Vn7n/Tef6/0/p/P9P6fz/T+n8/07o/P8dtMj/BkpZ/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDolIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oY0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiqf/Rt7x/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/SuL2/y291f8OlLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Jj6v/J7bP/0bd8f9P6fz/T+n8/0no+/8Z2/L/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u7/MNzw/1Hi - 8/9U4vP/VOLz/yOqw/8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p/P9R5/r/VOX1/1bj - 8/9W4/P/VuPz/1Xi8/9V4vP/VeLz/1Ti8/9U4vP/VOLz/1Pi8/9T4vP/UuLz/1Li8/9R4vP/UeLz/1Dh - 8/9P4fP/T+Hz/07h8/9N4fL/TeHy/0zh8v9K4PL/SuDy/0ng8v9I4PL/R+Dy/0bg8v9F4PL/RN/y/0Pf - 8v9C3/L/Qd/y/0Df8v8/3/H/Pt7x/z3e8f883vH/Ot7x/zne8f843fH/N93x/zXd8f813fH/M93x/zLc - 8f8w3PD/L9zw/y3c8P8t3PD/K9vw/yrb8P8n2/D/E9fu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCImP8AV2L/ANTt/wDU7f8A1O3/AtTt/yfe - 9f9N6fv/T+n8/0Ta7v8BhKL/AIOh/wCDof8Ag6H/AIOh/yq50f9P6fz/T+n8/0rn+/8Vw9z/AIaj/wA8 - Sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCFoEsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - okIAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8bc/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P890ef/HanD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AYSi/xihvP83y+H/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8H1e3/K9vw/03h8v8iqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p - /P9Q5/r/U+P1/1Pi8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Li8/9R4vP/UeLz/1Dh8/9P4fP/T+Hz/0/h - 8/9O4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Lf - 8v9B3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f873vH/Od7x/zne8f833fH/Nt3x/zXd8f803fH/Mtzx/zHc - 8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8a2O//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Aj6D/AIyc/wDU7f8A1O3/Fdnx/0Pm - +f9P6fz/T+n8/0/p/P82yd//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T+n8/0Lm+v8MsMv/AIOh/wCD - of8AVWn/AHqW/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCDoUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH - pREAg6HiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKbB/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/y29 - 1P8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKH/FqrE/0bd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/xja - 8v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8E1e3/DabB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P5/n/UOT0/1Hi8/9Q4fP/UOHz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TeHy/03h - 8v9M4fL/TOHy/0vh8v9K4PL/SuDy/0ng8v9I4PL/SODy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf - 8v9B3/L/P9/x/z7e8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc - 8P8w3PD/Ltzw/y3c8P8r2/D/K9vw/yHa7/8K1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJao/wCzyP8I1u//M+L3/0/p - /P9P6fz/T+n8/0/p/P9P6fz/MsTb/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqn/y7e8/8Dm7f/AIOh/wCD - of8Ag6H/AH6b/wBkev8Ag6H/AIOh/wCDof8Ag6H/AIOh7QCCoDsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6GmAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY6s/0ri9v9P6fz/T+n8/0/p/P880Of/HajC/wKG - pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ats7/S+f7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/N+P3/wnW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5vn/TuL0/03h8v9N4fL/TeHy/03h8v9M4fL/TOHy/0vh - 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Df - 8v8/3/H/Pt7x/z3e8f883vH/PN7x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8y3PH/Mdzw/zDc - 8P8v3PD/Ldzw/y3c8P8r2/D/JNrw/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wGqv/8h2vH/Suf7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3S6P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ai6j/AIOh/wCD - of8Ag6H/AIOh/wCDof8AaYH/AIOh/wCDof8Ag6H/AISh3wCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKBZAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zvP5f9P6fz/SeL1/yy80/8Mk7D/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wybt/9B4vf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9L6Pv/JN30/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMH/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vj/S+L0/0rg8v9K4PL/SuDy/0ng - 8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Hf8v9B3/L/QN/y/z/f - 8f8+3vH/Pd7x/zze8f883vH/Ot7x/zne8f843fH/N93x/zbd8f813fH/NN3x/zPd8f8y3PH/MNzw/zDc - 8P8u3PD/Ldzw/yzb8P8l2vD/ENfu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8v8/3vL/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AHSP/wCDof8Ag6H/AIOgzQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yOwyv87z+b/HKfC/wKFpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoMEAg6DtAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4in/y7Q5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/Gtvy/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKTB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5fj/SeH0/0jg - 8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v8/3/H/Pt7x/z7e - 8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/N93x/zXd8f813fH/M93x/zLc8f8x3PD/MNzw/y/c - 8P8t3PD/LNvw/x7Z7/8N1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/z7l+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wB/nP8Ag6H/AIKgsgCAqgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAA//8BAIOhvQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8KkK3/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKHfAIOiewCAnRoAAAAAAICkHACD - ocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fq3G/0fm+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+5fn/Etnw/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn - wf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9K5fj/RuHz/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pd7x/zze - 8f883vH/O97x/zre8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/yzb - 8P8g2e//Etfu/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8R2PD/OuT4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKigwCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIShcgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoa0AgaJHAICAAgAAAAAAAAAAAAAAAAAA - AAAAjqoJAIOhqACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8GjKr/Ndbs/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P854/j/Etnw/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Ap8T/AIOh/wCDof8Ag6H/AIOh/wCD - of8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07p/P9J5ff/Q+Dz/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pt7x/z7e8f893vH/PN7x/zve - 8f863vH/Od7x/zne8f843fH/N93x/zbd8f813fH/Nd3x/zPd8f8y3PH/Mdzw/yjb8P8g2e//Fdju/wvW - 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xfa8f864/j/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HanD/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HzAIOhVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIWiLACDofcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKLdAIOgeQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqFsAIOh+ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Yr8j/RuX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P865Pj/F9rx/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKnF/wCDof8Ag6H/AIOh/wCD - of8Ag6H/GKG8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo+/844fX/I9rw/yHa7/8j2u//JNrw/yTV6v8bvtb/HcTb/yja - 7/8q2/D/Kdvw/yjb8P8l2vD/Itrv/yDZ7/8c2e//F9jv/xLX7v8N1u7/B9Xt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/IN3z/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq40P8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICqBgCDotIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoasAgaNFAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqgYQAfZr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wSJpv8szOL/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9E5vn/It30/wTV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wCpxf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/8n3vX/Bdbu/wC40v8Ah6T/AIOh/wCD - of8AkrD/AM/o/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrW7/8q3/X/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P81x97/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKCfAIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoYoAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HcAIOhdwCFphcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/KAFxy/wB1kP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w+duP873fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/L+H2/xLZ8P8A1O3/ANTt/wDU7f8AqcX/AIOh/wCD - of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3Y7f8BhqT/AIOh/wCD - of8Ag6H/AIOh/wCivv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8G1e7/INzz/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEofQAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACGpCoAg6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh+QCDoakAg6FEAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXG0vAFpv/wBab/8AWm//AGiB/wCAnv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmwyf9E4/f/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/ROb5/yfe9f8K1u//AKnF/wCD - of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8QmLP/AIOh/wCD - of8Ag6H/AIOh/wCDof8Aj6v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AtTt/xnb8v824vf/Ten8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl - +P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKJ+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HaAISidgCAohYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvlQBab/8AWm//AFpv/wBab/8AXnT/AHeT/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/yG81f9I5vn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x6w - yv8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ots7/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV - 7v8b2/L/MuH2/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/CY6s/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKiaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhmACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+ACD - oKcAg6JCAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBab+8AWm//AFpv/wBab/8AWm//AFpv/xaB - lv8xwdn/BYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4im/yPB - 2v9H5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AoWj/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC30f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8Q2PD/J971/z3k - +f9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HYAISgdACG - nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbbl8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/81us7/T+n8/0jh9P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Aoel/yG/2P9G5fn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vn/DZSw/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCduP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v8U2fH/Jd70/zXi9/9I5/v/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/SeH1/0/p - /P9P6fz/T+n8/0nn+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8grcb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6G1AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISieACDofUAg6H/AIOh+ACDoaYAg59AAP//AQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/EAFpv/wBab/8AWm//AFpv/wBa - b/8HZnv/Teb5/0/p/P9P6fz/T+n8/zvP5v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x+40f9C4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/JLHK/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJqP8Azef/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/B9bv/xLZ8P8h3fT/MuH2/0Lm+f9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0LY7f8QmLP/AIOh/wGF - ov8eq8T/Od/1/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/LLzT/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8QAICeIgCApA4AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW20qAFpv/wBab/8AWm//AFpv/wBa - b/8AWm//JZ2x/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/yu70/8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xaowv842e7/Tun8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PNDn/wGE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AutX/ANTt/wDU7f8D1e7/B9bv/wzX8P8Q2fD/Fdnx/xrb - 8v8i3fT/LOD1/zfj9/9B5vr/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8Lkq7/AIOh/wCD - of8Ag6H/AIOh/xehvP9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfL4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAmZkFAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlvjwBab/8AWm//AFpv/wBa - b/8AWm//AFtw/0PT5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuL2/ySxyv8ChqT/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuUsP8syeD/SOX5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/wuR - rv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8dqcP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D2e7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEltBwBab+wAWm//AFpv/wBa - b/8AWm//AFpv/xR+k/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rt3x/x2o - wv8BhKL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKF - o/8YqML/NdTq/03o+/9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x+r - xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Lm+f8m3fP/CLbR/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb1oAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8zt8r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/RNru/x2pw/8BhKH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Giqj/HbHK/zbV6/8kssv/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/znM - 4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Os3k/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/K97z/xDG3f8Aor7/AIim/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yy91P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq//AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKFyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm++AFpv/wBa - b/8AWm//AFpv/wBab/8GZHr/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0zj9v8glKn/AGB3/wBzjf8AgqD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0ni - 9f8Ijqv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/N+P3/yDX7/8KutT/AJu4/wCGpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8Xb/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AISglwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV3ImAFpv/gBa - b/8AWm//AFpv/wBab/8AWm//I5it/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/z3J3f8LboP/AFpv/wBab/8AWm//AF90/wBxjP8AgZ7/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xeh - vP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8cpsH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Ku/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9L6Pv/O+T4/yrb8f8UxNz/AqTB/wCPrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/87z+X/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yOwyv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpviABa - b/8AWm//AFpv/wBab/8AWm//AFpv/0LS5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Teb5/yWesv8BXHH/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1x/wBthf8Afpv+AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P81x97/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/z3S6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p - /P8+5fn/Lt7z/x3P5/8NuNP/AZ+8/wCOrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/z7T - 6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8vv9f/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFWABgBa - b+kAWm//AFpv/wBab/8AWm//AFpv/xJ7j/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/QdDk/w90if8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXDQAFpwMACE - oU8AhKC6AIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9I3/P/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/9A5fr/NeL3/yvc8f8g0ej/FsLa/wuw - y/8CnLj/AI6r/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Hi6n/RNrv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZ - b1MAWm//AFpv/wBab/8AWm//AFpv/wBab/8xs8f/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9O6Pv/Kqa6/wJec/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/wAWm+IAFWABgAA - AAAAAAAAAAAAAACAoh4Ag6F9AIOh3ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSPrP8cyN//Gsnh/x/L5P8gzub/IM/n/yTS - 6f8l0en/JM/m/x/M5P8ayOD/FcLa/xG61P8Nsc3/CKrF/wOfu/8AlbL/AIup/wCEov8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDocEAhKKkAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuSrv9G3fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be - 8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWm+4AFpv/wBab/8AWm//AFpv/wBab/8FY3f/TOT2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9E1ej/E3yQ/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab9wAWG46AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAhKA+AIShmwCDoesAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/McHZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HQAIOhdwCAnyAAAAAAAAAAAACCoYcAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/DpWx/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/A4el/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWHAgAFpv/QBab/8AWm//AFpv/wBab/8AWm//IZaq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o - +/8vsMP/BGF2/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/gBbb5gAXXQLAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiQgCDoJQAg6HlAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Rd3x/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqD/AIOhywCDoX8Ag6IpAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOipACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/Tef6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvgwBab/8AWm//AFpv/wBab/8AWm//AFpv/0DO4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0fb - 7v8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv5gBZbkgAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCD - oaAAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIGf/wB2kf8AaoL/AGB4lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq - qgMAg6GxAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xiivf9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GiAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAECABABZb+QAWm//AFpv/wBab/8AWm//AFpv/xB3jP9P6fz/T+n8/0/p/P9P6fz/T+n8/zW5 - zf8GZHr/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpvqABVcRIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/y291P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/wB3 - kv8AbYb/AGN6/wBbcP8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICZCgCDocgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/JbTN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhxgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABZcU0AWm//AFpv/wBab/8AWm//AFpv/wBab/8vsMP/T+n8/0/p/P9P6fz/St/y/xyM - oP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/tAFpuWAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0PZ7v8DhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8DhqT/EZm1/yGvyP8xwtr/Qdfs/y+x - xf8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhp4VAIOh1ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ot8//T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe0AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+zAFpv/wBab/8AWm//AFpv/wBab/8EYXb/S+L1/0/p/P9P6fz/OcLV/whp - fv8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AW2+3AF5xGwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07n+/8RmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ChaP/Co+s/xOctv8fq8X/K7rS/zjL4f9E2u//Tuj7/0/p/P9P6fz/T+n8/0/p - /P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AW2/IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/ - 1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICcEgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAW20cAFpv+wBab/8AWm//AFpv/wBab/8AWm//H5Km/0/p/P9M5Pb/IZWp/wBb - cP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/QAWnBpAAAAAQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8puM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh9gCCon4Ag6F/AIKikQCD - oaMAgqG2AIOhuwCDobsAg6HLAIOhzACDocwAg6HMAIOhzACDosoAg6G7AIOhuwB3lPIAdI7/AHGK/wBu - h/8Aa4T/AGd//wt3jP9H3/P/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFlwiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8BhKL/N8rg/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC - oTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFpufQBab/8AWm//AFpv/wBab/8AWm//AFpv/z/M3/89yd3/C2+E/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab8YAXHEkAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9A1ev/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoGEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW252AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//P8zg/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqI3AIOh9QCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wKGpP87z+X/T+n8/0/p/P9P6fz/T+n8/0/p/P8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBacOAAWm//AFpv/wBab/8AWm//AFpv/wxxhv8mnrP/AV1x/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+QBbcHkAVVUDAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9N5/n/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFxwGQBa - b/sAWm//AFpv/wBab/8AWm//AFpv/yGWqv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoE4Ag6H8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/BYmn/0HX7P9P6fz/T+n8/0/p/P9P6fz/Epq1/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFlv0wBcbS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/JbLL/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoesAgJ8YAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW2+0AFpv/wBab/8AWm//AFpv/wBab/8GZHn/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiYwCD - of4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/RNvv/0/p/P9P6fz/T+n8/x2owv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+tAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/9AFlwiQBJbQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/PdHn/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJVAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtwVABab/8AWm//AFpv/wBab/8AWm//AFpv/zS4zP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFpviAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuSrv9I4fT/T+n8/0/p/P8puND/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW8XAFpv+gBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/dAFlvPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xeh - vP9P6fz/T+n8/0/p/P9P6fz/TOX4/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GlAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABVgAYAWm/sAFpv/wBab/8AWm//AFpv/wBab/8Xg5f/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0zl+P9P6fz/NMfd/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlveABab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnCZAGJ2DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD - of8Xobz/T+n8/0/p/P9P6fz/T+n8/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HlAIelEQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFtwkgBab/8AWm//AFpv/wBab/8AWm//AVxx/0fa7f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgIACAIOipACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/Tef6/0DW - 6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBab9sAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab+YAWHBLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD - of8Ag6H/F6G8/0/p/P9P6fz/T+n8/znN4/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOfSAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYbTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8qpbn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjaH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn - wv9L5Pf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZcEIAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab6oAXmsTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCD - of8Ag6H/AIOh/xehvP9P6fz/T+n8/0ri9v8Jj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhmAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlw0ABab/8AWm//AFpv/wBab/8AWm//DHCF/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/JLHK/wiOq/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2+mAFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv7gBbb1oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCD - of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P8dqML/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi3QCA - qgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabm8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/89yNz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBb - cEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhp4VAISh2wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv+ABab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpvugBecRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCD - of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P82yN//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACC - oz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVW0VAFpv+QBab/8AWm//AFpv/wBa - b/8AWm//H5Kn/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoyQAg6HqAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhswAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdyJgBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/1AFlvagAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9I4PT/B4up/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb64AWm//AFpv/wBa - b/8AWm//AFpv/wRid/9M5Pb/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjKH/AFpv/wBab/8AWm//AFpv/wBa - b/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD - ovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodgAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWm/wAFpv/wBa - b/8AWm//AFpv/wBab/8AW2/IAFpuJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/GaO9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - odQAgJ8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXFNAFpv/wBa - b/8AWm//AFpv/wBab/8AWm//MrTI/0/p/P9P6fz/T+n8/0/p/P9P6fz/CGl+/wBab/8AWm//AFpv/wBa - b/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6JCAIOh+QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H6AIC/BAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpuXQBa - b/4AWm//AFpv/wBab/8AWm96AECABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofoAhaMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABa - b+gAWm//AFpv/wBab/8AWm//AFpv/xR9kv9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBZbkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACEoVcAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCA - oyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW3EtAFtuhABabn0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6F/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnCLAFpv/wBab/8AWm//AFpv/wBab/8AW3D/Rdfp/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6LKAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFdwKQBab/8AWm//AFpv/wBab/8AWm//AFpv/yehtf9P6fz/T+n8/0/p/P8cjKH/AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H3AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm/HAFpv/wBab/8AWm//AFpv/wBab/8KbIH/Tuj7/0/p/P9P6fz/CGl+/wBa - b/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCEoJcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCEopkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOgcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFlvZwBab/8AWm//AFpv/wBab/8AWm//AFpv/zvE2P9P6fz/RNXo/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOhsQCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhwACAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8AWm/2AFpv/wBab/8AWm//AFpv/wBab/8djqL/T+n8/zCx - xP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HDAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh8gCDoiEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvpQBab/8AWm//AFpv/wBab/8AWm//A191/0vh - 9P8cjKH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD - oe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDockAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCCoWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb0UAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8wscT/CGl+/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgKQcAIOh4wCDof8Ag6H/AIOh/wCDof8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDobUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAFpv4QBab/8AWm//AFpv/wBa - b/8AWm//B2Z7/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACFphcAhKGdAIOh/QCDoesAg6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoe0AhaMZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab4MAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAXXQLAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDonsAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqBWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV20jAFpv/gBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6GpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b8EAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oZ4Ag6H/AIOh/wCDof8Ag6HQAICfEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnBgAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOgRgCDoXcAhKBbAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFVqDABab/MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/oAXXQLAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+fAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP/////////////A///////gB////////////////////////////////8B//////8AH - ////////////////////////////////wD//////wAP///////w////////////////////////AH/// - ///AA///////+D///////////////////////8AP/////4AD///////gH/////////////////////// - wA//////gAP//////8Af///////////////////////AB/////8AA///////gD////////////////// - /////+AD/////wAB//////8AP///////////////////////4AH/////AAH//////gA///////////// - ///////////gAP////4AAf/////8AD///////////////////////+AAf////gAB//////AAf/////// - ////////////////4AA////8AAH/////4AB////////////////////////gAB////wAAP/////AAH// - //////////////////////AAD////AAA/////4AAf///////////////////////8AAH///gAAAA//// - AAD////////////////////////wAAP/+AAAAAAD//4AAP////////////////////////AAAf8AAAAA - AAAf/AAA////////////////////////8AAA8AAAAAAAAAHwAAH////////////////////4f//wAAAA - AAAAAAAAACAAAf////////////////////Af//gAAAAAAAAAAAAAAAAB////////////////////8Af/ - +AAAAAAAAAAAAAAAAAH////////////////////wA//4AAAAAAAAAAAAAAAAA/////////////////// - //AA//gAAAAAAAAAAAAAAAAD////////////////////+AB/+AAAAAAAAAAAAAAAAAP///////////// - ///////4AB/4AAAAAAAAAAAAAAAAB/////////////////////wAB/wAAAAAAAAAAAAAAAAH//////// - /////////////AAD+AAAAAAAAAAAAAAAAAf////////////////////+AADgAAAAAAAAAAAAAAAAA/// - //////////////////4AAAAAAAAAAAAAAAAAAAAA/////////////////////wAAAAAAAAAAAAAAAAAA - AAA/////////////////////AAAAAAAAAAAAAAAAAAAAAB////////////////////+AAAAAAAAAAAAA - AAAAAAAAB////////////////////4AAAAAAAAAAAAAAAAAAAAAD////////////////////wAAAAAAA - AAAAAAAAAAAAAAD/////////////h//////AAAAAAAAAAAAAAAAAAAAAAH////////////8B/////8AA - AAAAAAAAAAAAAAAAAAAAH////////////wB/////4AAAAAAAAAAAAAAAAAAAAAAP////////////AB// - ///gAAAAAAAAAAAAAAAAAAAAAAf///////////8AD////8AAAAAAAAAAAAAAAAAAAAAAAf////////// - /4AD////gAAAAAAAAAAAAAAAAAAAAAAA////////////gAD///8AAAAAAAAAAAAAAAAAAAAAAAB///// - ///////AAD///gAAAAAAAAAAAAAAAAAAAAAAAD///////////+AAD//8AAAAAAAAAAAAAAAAAAAAAAAA - D///////////4AAD//gAAAAAAAAAAAAAAAAAAAAAAAAH///////////wAAD/8AAAAAAAAAAAAAAAAAAA - AAAAAAP///////////AAAD/gAAAAAAAAAAAAAAAAAAAAAAAAAf//////////+AAAD8AAAAAAAAAAAAAA - AAAAAAAAAAAA///////////4AAADgAAAAAAAAAAAAAAAAAAAAAAAAAB///////////wAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAD///////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////////+AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAP//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////////wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////// - //+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH// - ////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAf//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAB//////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////gAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAH//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////8AAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAf/////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////gH///gAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAD////+AB///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////wAB//+A - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////AAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///8A - AD//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////wAAH/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD - ////gAAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///+AAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP///4AAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gAAD+AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAB///+AAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4AAAfAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAH///gAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//+AAADgAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAA///4AAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///gAAA4AAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAD//+AAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//4AAAMAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//gAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8A - AACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//wAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAD/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAD+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAHAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAB/gAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAB/4AAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAB - /+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AD8AB//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAP/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAA/AB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8Af//gAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAfwD//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/A///gAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP8H//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/x///AAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAH/f//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///+AAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///8AAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4 - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - A///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/4AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/+AAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAA//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//wAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////4AA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///// - ///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - f///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////+AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAH///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAD///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAB///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////+AAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAA///////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////AAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////// - /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/ - //////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAA///////AAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////8AAH/gAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAf//////wAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////gH//8AAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAf///////H///4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////wAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAP////////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////////////+AAAAA - AAAAAAAAAAAAAAAAAAAAAAAAP////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////////// - gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// - /////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/// - //////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAA - AA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAAAAAAA - AAAAAAA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAA - AAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////gAAAAAAAAAAA - AAAAAAAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wAAAAAA - AAAAAAAAAAAAAAAAAAAAAA/////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wA - AAPAAAAAAAAAAAAAAAAAAAAAAAA//////////////AAAD+AAAAAAAAAAAAAAAAAAAAAAAP////////// - ///4AAB/+AAAAAAAAAAAAAAAAAAAAAAB//////////////AAAf/4AAAAAAAAAAAAAAAAAAAAAAP///// - ////////4AAP//gAAAAAAAAAAAAAAAAAAAAAD//////////////gAD//+AAAAAAAAAAAAAAAAAAAAAAf - /////////////+AB///wAAAAAAAAAAAAAAAAAAAAAB//////////////wAf///AAAAAAAAAAAAAAAAAA - AAAAD//////////////gP///8AAAAAAAAAAAAAAAAAAAAAAP//////////////D////gAAAAAAAAAAAA - AAAAAAAAAA///////////////////+AAAAAAAAAAAAAAAAAAAAAAD///////////////////wAAAAAAA - AAAAAAAAAAAAAAAP///////////////////AAAAAAAAAAAAAAAAAAAAAAA///////////////////8AA - AAAAAAAAAAAAAAAAAAAAD///////////////////gAAAAAAAAAAAAAAAAAAAAAAH//////////////// - //+AAAAAAAAAAAAAAAAAAAAAAAf//////////////////wAAAAAAAAAAAAAAAAAAAAAAB/////////// - ////////AAAABgAAAAAAAAAAAAAAAAAH//////////////////8AAAAPwAAAAAAAAAAAAAAAAAf///// - /////////////gAAAD/wAAAAAAAAAAAB4AAAB//////////////////+AAAAf/4AAAAAAAAAAA/wAAAH - //////////////////wAAAH//4AAAAAAAAAAH/gAAAP//////////////////AAAA///gAAAAAAAAAA/ - /AAAA//////////////////8AAAP//+AAAAAAAAAAD/+AAAD//////////////////gAAB///4AAAAAA - AAAAP/8AAAP/////////////////+AAAf///gAAAwAAAAAA//4AAA//////////////////4AAD///+A - AAH//wAAAH//wAAD//////////////////AAA////4AAAf//AAAAf//gAAH/////////////////8AAH - ////gAAD//8AAAB///AAAf/////////////////gAA////+AAAf//4AAAH//+AAB//////////////// - /+AAP////4AAB///gAAA///4AAH/////////////////4AB/////gAAP//+AAAD///wAAf////////// - ///////AAf////+AAB///8AAAP///gAB/////////////////8AD/////4AAH///wAAA////AAH///// - ////////////gA//////gAA////gAAH///+AAP////////////////+AH/////+AAH///+AAAf///8AA - /////////////////4B//////4AAf///4AAB////4AD/////////////////gP//////gAD////wAAH/ - ///wAP/////////////////D//////+AAf////AAA/////gA/////////////////+///////4AD//// - 8AAD/////AD/////////////////////////gAP////4AAP////8AP////////////////////////+A - B/////gAA/////4Af////////////////////////4AP/////AAH/////wB///////////////////// - ////gA/////8AAf/////gH////////////////////////+AH/////wAB//////Af/////////////// - /////////4A//////gAH/////+D/////////////////////////gD/////+AA//////8f////////// - //////////////+Af/////4AD////////////////////////////////8D//////wAP//////////// - ////////////////////wP//////AA/////////////////////////////////B//////+AH/////// - /////////////////////////////////4Af////////////////////////////////////////gB// - ///////////////////////////////////////AH///////////////KAAAAIAAAAAAAQAAAQAgAAAA - AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgJwSAIOh+gCDof8Ag6H/AIKjLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvrwBab/8AWm//AFpv/wBa - b/8AWm+eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuiCwCZmQUAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6H/AIOh/wCDof8Ag6H4AIOfJQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABecRsAWm/7AFpv/wBab/8AWm//AFpv/wBab9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoK8Ag6H/AIOh2gCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpufwBab/8AWm//AFpv/wBa - b/8AWm//AFpv/QBidg0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAg6GYAIOh/wCDof8Ag6H/AIGgQwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCD - of8Ag6HaAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABAgAQAWm/iAFpv/wBab/8qprr/BmR6/wBab/8AWm//AFlwQgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgKoMAIOhtQCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKGbAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HPAIiZDwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBab/8AWm//Al1y/0jd - 8P8Xg5f/AFpv/wBab/8AWW94AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCDoM0Ag6H/AIOh/wCDof8Ag6H/AIOh8wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HDAICZCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+1AFpv/wBab/8bi6D/T+n8/yehtv8AWm//AFpv/wBZb64AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF - oiwAg6HhAIOh/wCDof8Ag6H/AIOh/wCDof8AhKKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/CY+s/yWzzP8Ag6H/AIOh/wCDof8Ag6K3AICqBgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFprHwBab/0AW3H/AF1z/zO9 - 0v9D2e7/LLPJ/wBief8AYXf/AF926QCAoh4AkpIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOi8ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - olIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQcAIOh/wCD - of8Ag6H/SuP2/yGtx/8Ag6H/AIOh/wCDof8Ag6GpAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAICZCgCC - oDsAg6JrAIOhkACDoK8AfpvnAICd/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HqAIOi0gCCoLIAgqCJAIKiWgCFoiwAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShXwCD - ofkAg6H/AIOh/wGEov8grMb/AIOh/wCDof8Ag6H8AIiZDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYFAFxwGQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6LwAIOh/wCDof8+0+j/Tuj7/xynwv8Ag6H/AIOh/wCD - of8Ag6GaAP//AQCAnxgAgqJgAIOhngCDotIAg6H7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofUAhKC6AIOhdwCCnzUAgIACAP//AQCDoX8Ag6H+AIOh/wCDof8EiKX/OMvh/yq50f8Ag6H/AIOh/wCD - ocMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABZb78AWm//AFpv/QBabz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - ocYAg6H/AIOh/zHB2f9P6fz/Tef6/xihvP8Ag6H/AIOh/wCDof8Ag6LoAIOh/gCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoeUAg6HWAIOh/wCD - of8Ag6H/CI2q/z/U6v9P6fz/FJ24/wCDof8Ag6H/AIOhfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW0HAFpv/wBab/8AWm//AFpv/wBZ - cKAAWXMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgnACDof8Ag6H/JLHK/0/p/P9P6fz/TOX4/xSc - uP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wCOrP8AmLT/AKK9/wCq - xf8Cr8r/BbXQ/wW30f8Du9X/ALnU/wC10P8As87/ALHN/wCrxv8Aob3/AJm2/wCPq/8AhKL/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w2UsP9G3fH/T+n8/0vk+P8ChaT/AIOh/wCD - of8AhKE2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm/4AFpv/wBab/8AWm//AFpv/wBab+wAWm9jAAAAAQAAAAAAAAAAAAAAAAAA - AAAAhKFyAIOh/wCDof8WoLv/T+n8/0/p/P9P6fz/SuP2/xCYtP8Ag6H/AIOh/wCDof8Ag6H/AIWj/wGX - s/8Nr8z/Gsff/yfY7/804vf/PeT5/0Lm+f9F5fj/OMzh/yayyv9M5fj/T+n8/0/p/P9P6fz/TOj7/0rn - +/9F5/r/PuX5/zPi9/8o3/T/Htzz/xPW7v8GxN7/ALLO/wCeuv8Aiaf/AIOh/wCDof8Ag6H/AIOh/wCD - of8Unbj/SuL2/0/p/P9P6fz/OMvh/wCDof8Ag6H/AIOh7QCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabz4AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/DAF1uLAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wmPrP9P6fz/T+n8/0/p - /P9P6fz/SOH0/w2TsP8Ag6H/AIOh/wSLqf8+5Pn/Ten8/0/p/P9P6fz/T+n8/03m+v82x93/G6O8/wSF - oP8Af5v/AH+b/zC/1f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9B5vr/L+D2/xfK4v8Biqj/AIOh/wCDof8Ag6H/HanD/03n+v9P6fz/T+n8/0/p/P8hr8j/AIOh/wCD - of8Ag6CnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb8UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AFpviABm - ZgoAgqExAIOhxgCDof8Ag6H/AIOh/0vk9/9P6fz/T+n8/0/p/P9P6fz/Rt3y/wCDof8Ag6H/FZ+5/0/p - /P9P6fz/T+n8/0ri9v8otcz/CIql/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpKs/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P880Ob/BIil/wCDof8Ag6H/AIOh/ye1 - zv9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCEobgAgJ8QAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBa - b/8AWm//AFpv/w92iv8AWm//AFpv/wBab/8AXnT/AHSP8wCDof4Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p - /P9P6fz/T+n8/0/p/P9P6fz/S+T4/zrO5P9N5/n/T+n8/0zl+P8qt87/BYei/wB/m/8Af5v/AH+b/wB/ - m/8djqb/NZqw/wB/m/8Af5v/Hr7U/ynT6P8s1ej/L9bq/zPY7P883fD/R+P1/07m+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/xiivf8Ag6H/AIOh/wKFo/8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/AIOh/wCD - of8Ag6H/AIOh/wCDoesAg6BpAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAFlv0wBab/8AWm//H5Gm/zrC1v8Lb4P/AGuE/wCB - nv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xwtn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P890uf/Co+q/wB/m/8Af5v/AH+b/wGAm/85nrb/j87f/7ro9/+Lydn/EYii/wB/m/8Ak67/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/BcLY/w7F2f8Yy+D/J9Pn/zrc8P9L5fj/Lb3U/wCDof8Fiqf/Os7k/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HFAIOiKQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWW9cAFpv/wBab/8CXXL/Ps/j/xOct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weVsf8Ag6H/AIOh/ySx - yv9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+/8y3/T/EK/I/wCAnP8Af5v/AH+b/wB/m/9ApLv/pdzs/8fw - /v/I8P7/yPD+/7Xk8/86nLL/AH+b/wCAnP8At8//AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wLA1/8Rx9z/H8ng/zXY7f9O5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GaO9/wCD - of8Ag6H/AYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H4AIShegCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6B5AIOh5QCDor8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWnDgAFpv/wBqgv8ChKL/AIOh/wCD - of8Ag6H/AIOh/wqfvP823fL/LLzT/wCDof8Ag6H/F6C7/0/p/P9P6fz/S+j7/zHh9v8S0+n/Acbd/wCf - uP8Af5v/AH+b/wB/m/8RiaT/jtDj/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/3rA0f8EgZ3/AH+b/wCb - tf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wbE - 2f8e0Ob/OuDz/07o/P9P6fz/T+n8/0/p/P8Fiqf/AIOh/wCDof8zy+L/HMXd/wCJp/8Ag6H/AIOh/wCD - of8Ag6H/AIOhxgCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDofYAg6H/AIOh/wCD - of8Ag6GoAIahJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABecnIAdI//AIOh/wCDof8Ag6H/AIOh/wGMqP8nz+f/TOj7/0/p/P83yuD/AIOh/wCD - of8Lkq7/ROb5/yLc8/8Ezeb/AMTb/wDB2P8Apb//AH+b/wB/m/8Af5v/NJ63/7bn9//H8P7/yPD+/8jw - /v/G8P7/uuz+/8jw/v/I8P7/ptzs/yeTqv8Af5v/AIKe/wC81P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/BsXc/yHT6f9E5fj/T+n8/xKa - tf8Ag6H/CI6r/03n+f9P6fz/PuP4/xGvyf8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACColgAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOh8gCDof8Ag6H/AIOh/wCDof8Ag6H7AIShmwCEnh0AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh0QCDof8Ag6H/AIOh/wCD - of8NqMT/P+P4/0/p/P9P6fz/T+n8/0fe8v8Ag6H/AIOh/wicuf8Bzub/AMTb/wDC2f8Awtn/ALTM/wCA - nP8Af5v/AH+b/0Glvv+96/z/xO/+/8bw/v/H8P7/vO3+/6bn/v+i5v7/uOz+/8Lw/v+77vz/W6/D/wB/ - m/8Af5v/AKO9/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHC2P8a0Ob/Ot3x/zXH3v9I3/P/T+n8/0/p/P9P6fz/T+n8/zPZ - 7/8HkrD/AIOh/wCDof8Ag6H/AIOh/wCCoI8AqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhqQqAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCDoo4AgKYUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOhRACDoewAg6H/AIOh/wCDof8Ah6T/Hsrh/03p+/9P6fz/T+n8/0/p/P9P6fz/Suf7/xzP - 5v8Bs83/AMPa/wDD2f8Aw9n/AMPZ/wDB1/8AiqX/AH+b/wB/m/8znrj/u+v8/8Lv/v/D7/7/w+/+/6/q - /v+i5v7/oub+/6Hl/f+g5/3/vO/9/7bu/f+Q0+P/E4mi/wB/m/8AiKP/AMHY/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awtn/Es3j/zjg9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+v8YsMr/AIOh/wCDof8Ag6H/AIOh/wCD - orcAiJkPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KWAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofMAg6GAAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoFYAg6H5AIOh/wCDof8Ag6H/ApGt/y/b - 8P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLh9/8G1e7/AM3l/wDE2/8AxNv/AMTb/wDE2/8AxNv/AKS+/wB/ - m/8Af5v/EYql/6/m+f+/7v7/we7+/8Du/v+p6P7/oub+/6Lm/v+g5f3/l+T9/43k/P+s7P3/sO79/6bq - +f9Bobf/AH+b/wB/m/8Arsb/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/xXP5f9B5Pf/T+n8/0/p - /P9P6fz/T+n8/0/p/P8uz+b/A4ak/wCDof8Ag6H/AIOh/wCCoNUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG - nhUAg6HxAIOh/wCDof8Fiqf/CY+s/wCDof8Ag6H/AIOh/wCDof8Ag6HrAIOicwCOqgkAAAAAAAAAAAAA - AAAAgqBmAIOh/ACDof8Ag6H/AIOh/wSfu/864vf/T+n8/0/p/P9P6fz/T+n8/0jn+/8Z2/L/ANLq/wDI - 4P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDC2v8AhKD/AH+b/wB/m/93x97/ve3+/77u/v+/7v7/qej+/6Lm - /v+i5v7/n+X9/5bk/f+M4/z/g+P8/4/n/P+q7f3/pO38/3rJ2v8Fgp3/AH+b/wCQq/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPZ/wHF2/8g1er/Sej7/0/p/P9P6fz/T+n8/0/p/P9C4vX/D5m0/wCD - of8Ag6H/AIOh/wCDoewAg59AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoXcAg6H/AIOh/wKFpP9C2e3/LL3U/weL - qf8Ag6H/AIOh/wCDof8Ag6H/AIOh5ACCoGYAgL8EAIShcgCDof4Ag6H/AIOh/wCDof8IqMP/QOX5/0/p - /P9P6fz/T+n8/0/p/P895Pn/CNfv/wDQ6P8AyN//AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AK3F/wB/ - m/8Af5v/FIyo/7Xq/f+87f7/ve3+/7Lq/v+i5v7/oub+/57l/f+V5P3/i+P8/4Li/P954vv/ceH7/6Ds - /P+e7Pz/keT0/ymUrP8Af5v/AH+b/wC30P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8Fyd//M97y/0/p/P9P6fz/T+n8/0/p/P9N6Pv/ILHK/wCDof8Ag6H/AIOh/wCDofoAgqJYAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICfCACDoeAAg6H/AIOh/xumwP9P6fz/S+T4/yi3z/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCD - oOoAhKH+AIOh/wCDof8Ag6H/Ca3J/0Tm+v9P6fz/T+n8/0/p/P9P6fz/L+H2/wLV7f8Azeb/AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Al7H/AH+b/wB/m/9UtM3/uez+/7rt/v+87f7/pef+/6Lm - /v+d5f3/lOT9/4rj/P+B4vz/eOH7/27g+/9k4Pr/guf7/5jr+/+T6/v/YLzP/wB/m/8Af5v/AJmz/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/HNPp/0vn+v9P6fz/T+n8/0/p - /P9P6fz/KsLZ/wCDof8Ag6H/AIOh/wCDofwAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiWACDof8Ag6H/AIOh/z3S - 6P9P6fz/T+n8/0nh9f8kssv/A4ak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmyzf9E5vr/T+n8/0/p - /P9P6fz/T+n8/yTe9P8A1O3/AM3l/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCF - oP8Af5v/AH+b/4nS6f+47P7/uez+/7br/v+i5v7/nOX9/5Pk/f+J4/z/gOL8/3fh+/9t4Pv/ZN/6/1rf - +f9d4Pn/kev7/4zq+/9+2+3/FYqj/wB/m/8AgZz/AMDY/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/Ccrh/z3h9f9P6fz/T+n8/0/p/P9P6fz/M8/l/wKFpP8Ag6H/AIOh/wCD - ofwAg6BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAA//8BAIOhyACDof8Ag6H/EZm0/0/p/P9P6fz/T+n8/0/p/P9H3vL/IKzG/wGF - ov8Ag6H/AIOh/wCDof8Ag6H/EJq2/zzQ5v9P6fz/T+n8/03p/P8c2/L/ANTt/wDN5v8AyuL/AMri/wDK - 4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AxNz/AH+b/wB/m/8CgZz/qef8/7fs/v+47P7/r+n+/5vl - /f+S5P3/ieP8/3/i/P924fv/bOD6/2Pf+v9Z3vn/UN75/0bd+P955/r/her6/4Do+v9Dqb//AH+b/wB/ - m/8Aor3/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMfe/wDH3v8Ax97/A8jf/zPa - 7v9P6fz/T+n8/0/p/P9P6fz/PNft/wWKp/8Ag6H/AIOh/wCDof4AgqBmAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKM6AIOh/gCD - of8Ag6H/NMfd/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/xynwv8BhKL/AIOh/wCDof8Ag6H/AIOh/xih - vP9K5fj/GNrx/wDU7f8Azeb/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wC+ - 1/8Af5v/AH+b/xOLp/+u6f7/tuv+/7fs/v+l6P3/keT9/4jj/P9+4vv/deH7/2vg+v9i3/r/WN75/0/d - +f9F3fj/PNz4/1Hg+P9/6fr/fun6/3HS4/8Ggp3/AH+b/wCGof8Ax+D/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/Acjg/yrV6f9O5/r/T+n8/0/p/P9P6fz/QNvw/wWJ - p/8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GrAIOh/wCDof8KkK3/Teb5/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0HX7P8Yobz/AIOh/wCDof8Ag6H/AIOh/w+91v8A1O3/AM7m/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AL7X/wB/m/8Af5v/HZKt/63p/v+16/7/r+v9/5rm - /P+H4/z/feL7/3Ph+/9q4Pr/Yd/6/1je+f9O3fn/RNz4/zvc+P8y2/f/K9v3/3jo+v9+6fr/fuf4/y2a - sf8Af5v/AH+b/wCvyP8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/yDT6f9N5Pj/T+n8/0/p/P9P6fz/Ptrv/wWJp/8Ag6H/AIOh/wCDof4AhKBbAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA - niIAg6H4AIOh/wCDof8qutL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/FJ23/wCD - of8Bh6T/AMfh/wDP5/8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AxN3/AH+b/wB/m/8akKv/q+j+/6vq/f+m6v3/kub8/3zi+/9y4fv/aeD6/2Df+v9X3vn/Td35/0Pc - +P862/j/Mdr3/yfa9/8n2vf/WOL5/37p+v9+6fr/Ycfa/wB/m/8Af5v/AI6p/wDM5P8AzOT/AMzk/wDM - 5P8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xvR5/9M4/b/T+n8/1vr - /P9h6/z/R9ru/wKFo/8Ag6H/AIOh/wCDofkAg6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYsAg6H/AIOh/wSJpv9I4fT/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HM7m/wDM5v8A0en/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDK5P8Af5v/AH+b/weEn/+d5fz/oun9/53o - /f+N5vz/ceH7/2jg+v9f3/r/Vd75/0zd+f9C3Pj/Odv4/zDa9/8n2vf/J9r3/yfa9/813Pj/fun6/37p - +v985PX/GIym/wB/m/8Af5v/ALrT/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMvj/wDL4/8EzOP/Qdjq/47p9P/J9/z/0fn+/9H5/v/R+f7/t+72/2m2yP8Vjan/AIOh/wCD - ovAAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICfEACDoesAg6H/AIOh/yGux/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/y/h - 9v8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wCHo/8Af5v/AH+b/3vU7f+Z6P3/k+f8/43n/P9r4fr/Xt/6/1Te+f9L3fn/Qdz4/zjb - 9/8v2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9p5fn/fun6/37p+v9KuM7/AH+b/wB/m/8AmLL/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8EzuX/VN3u/6vv9v+98vj/vfL4/8b0 - +v/R9/z/0fn+/9H5/v/R+f7/xu70/7nd5f9escT/AoSi/wCDoeoAgKMkAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWjGQCDopYAg6GYAISgdACB - o0UAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCDof8Ag6H/AYWi/0LZ - 7f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5vr/AdXt/wDT7P8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AJiy/wB/m/8Af5v/QrHL/43m - /P+J5vz/g+b7/33l+/9l4fr/S935/0Dc+P832/f/Ltr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Xf - +P9+6fr/fun6/3bd7/8Ggp3/AH+b/wCAnP8AxNz/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Bzeb/Rtvt/6fu9v+98vn/oOz1/3fd6/9Sytz/RcDT/0u80P9jx9f/fNXk/6vs9f/Q+f7/wunv/73f - 5/+Qydb/D4qn/wCEodsAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKG+AIOh/wCDof8Ag6H/AIOh/wCDofsAg6G1AIGjRQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAmZkFAIOh4gCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj8/wfW - 7v8A1O3/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR - 6f8A0en/ANHp/wDR6f8Ascv/AH+b/wB/m/8Af5v/euD4/37l+/955fv/c+T6/3Dk+f9l4/n/Rt74/y3a - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/Kdr3/3fo+v9+6fr/fun6/zOmvf8Af5v/AH+b/wCi - vP8A0Oj/ANDo/wDQ6P8A0Oj/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/L9jr/5nr9f+i7PX/W9Dg/xunwP8Ah6P/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/xeQrP9zz97/td/o/73f5/+e0Nv/E4yo/wCCoLIA//8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoeYAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOhowCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6HeAIOh/wCD - of8Ag6H/Os7k/0/p/P9P6fz/T+n8/0/p/P8c3PL/ANTt/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDN5v8Agp3/AH+b/wB/ - m/8MiqX/Rb/Y/2vi+f9q4/n/ZeP5/2Hj+f9d4/n/TeH4/zDc9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/VuL5/37p+v9+6fr/Z9Ll/wB/m/8Af5v/AIOf/wDN5f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/G9Xq/4Ho - 9P+J5O//NbbM/wGHo/8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8Xkaz/fsDQ/73f5/+f0Nz/BYWj/wCDoHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhzACJ - nQ0AAAAAAAAAAAAAAAAAAAAAAIShXwCDof8Ag6H/AIOh/wCDof8OlbH/Tuj7/0/p/P9P6fz/O+T4/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wCeuf8Af5v/AH+b/wB/m/8Af5v/EZKu/0TL5P9a4vn/VuL5/1bi - +f9b4/n/XeP5/0ng+P8r2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8z3Pf/fun6/37p+v9+6Pn/HZWu/wB/ - m/8Af5v/AK7I/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDR6f8A0en/FNXr/2rk8v972uj/Jqa9/wCBnv8AgZ7/AIyp/wCatf8Apb//AKrF/wCr - xv8AqsT/AKO+/wCYtP8AhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8BgZ7/XK7C/7ze5v9XrcH/AIOh/ACB - oUEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCD - of8Ag6H/ALbR/wCbuP8Ag6L/AIOh/wCDof8Ag6H/AIOhmAAAAAAAAAAAAAAAAACAnwgAg6HiAIOh/wCD - of8Ag6H/AIOh/wCDof8xwdn/T+n8/0zo/P8D1u3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wC0 - zv8AiKP/AH+b/wB/m/8Af5v/AH+b/xafu/9B1O3/VOL5/1ji+f9c4/n/X+T5/17j+f9B3vj/KNr3/yfa - 9/8n2vf/J9r3/yfa9/9m5fn/fun6/37p+v9Sxdv/AH+b/wB/m/8Ai6b/ANLr/wDT7P8A0+z/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Fdbt/2ff7v9uz9//Hpix/wCJ - pv8Aor3/ALnS/wDM5f8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8Aw9v/EqrE/waI - pf8AgZ7/AIGe/wCBnv8AgZ7/QKG3/7DZ4v8Qi6f/AIOg6ACAnRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoZAAg6H/AIOh/wCEov8A1O3/ANTt/wDF3v8Aiqf/AIOh/wCD - of8Ag6H7AIOiIQAAAAAAAAAAAIOgcQCDof8Ag6H/AIOh/wOTsP8Ag6H/AIOh/wiNqv9L5Pj/Htzy/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDQ6f8AqML/AIKe/wB/m/8Af5v/AH+b/wGB - nf8jrsj/T9z0/1nj+f9c4/n/YOT5/2Tk+f9c4/n/ON34/yfa9/8n2vf/J9r3/0Lf+P996fr/fun6/3rl - 9/8Kh6P/AH+b/wB/m/8AudT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/JNnv/1nU5v9SvM//D5ex/wCow/8Awdv/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8C0+v/O+D0/y691P8Giab/AIGe/wCBnv8AgZ7/Tqi8/3G6 - y/8Ag6H/AIOhwwCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKigwCD - of8Ag6H/AIOh/wDS6/8A1O3/ANTt/wC30v8Ag6H/AIOh/wCDof8AgqGNAAAAAACAvwQAg6HhAIOh/wCD - of8Aiaf/KdTq/wOHpf8Ag6H/AIOh/yCzzP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8C1O3/AtTt/wLU7f8Dy+T/AqC6/wCAnP8Af5v/AH+b/wB/m/8Fh6P/MLzV/1ji9/9e4/n/YeT5/2Xl - +f9o5fn/VuL5/y/b9/8n2vf/Ot34/3ro+v9+6fr/fun6/zy2zv8Af5v/AH+b/wCUr/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//PtTo/0zE1v84tcv/Aq/J/wDJ4v8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT - 7P8I1u7/SOb5/0rh9f8hq8T/AYKf/wCBnv8AgZ7/Wq7B/yGTrf8Ag6H/AIOihgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KGAIOh/wCDof8Ag6H/ANLq/wDU7f8A1O3/ANPs/wCP - rP8Ag6H/AIOh/wCDodEAAAAAAIKgVgCDof8Ag6H/AIOh/wqyzf9N6fz/J7bP/wCDof8Ag6H/AIek/wDS - 6v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8I1e7/B8Xe/wKV - sP8Af5v/AH+b/wB/m/8Af5v/DZGs/0DK4v9f5Pn/YuT5/2bl+f9q5fn/beb6/3Hn+v905/r/d+j6/3zp - +v9+6fr/Z9zw/wB/m/8Af5v/AH+b/wDD3f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BMri/wa9 - 1v8Av9r/ANDp/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8T2fD/Ten8/0/p/P85y+L/Boil/wCB - nv8AgZ7/TKe8/wCDof8Ag6H9AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oZAAg6H/AIOh/wCDov8A1O3/ANTt/wDU7f8A1O3/AKfD/wCDof8Ag6H/AIOh/QCOqgkAg6HHAIOh/wCD - of8AhaP/Md70/0/p/P9M5fj/CY+r/wCDof8Aiqf/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW - 7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8N1u7/Dtbu/wq60/8Ci6f/AH+b/wB/m/8Af5v/AH+b/xqf - uv9Q1u3/ZOT5/2fl+f9r5vr/bub6/3Ln+v915/r/eOj6/1je9P8XqMT/AH+b/wB/m/8Af5v/AZ65/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8h3fP/T+n8/0/p/P9F3fD/EJex/wCBnv8OiaP/JpWv/wCDof8Ag6HmAIaeFQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhqACDof8Ag6H/AIyq/wDU7f8A1O3/ANTt/wDU - 7f8AqcX/AIOh/wCDof8Ag6H/AIOiQgCDof8Ag6H/AIOh/weow/9N6Pz/T+n8/0/p/P9C3PD/A6/L/wDH - 4f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wPV - 7f8E1e3/BtXt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8P1+7/ENfu/xDX7v8Q1+7/Edfu/xLX - 7v8S1+7/Etfu/xPV7P8Kr8n/AYSg/wB/m/8Af5v/AH+b/wKAnf8pr8j/Xt/0/2jl+f9s5vr/cOb6/2Lk - +P8oudP/AoWg/wB/m/8Af5v/AH+b/wCAnf8DpsD/BNXt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8x4fb/T+n8/0/p - /P9M5fj/FJ23/wCBnv8Yjqn/A4Si/wCDof8AgqK0AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6HQAIOh/wCDof8AmbX/ANTt/wDU7f8A1O3/ANTt/wCfu/8Ag6H/AIOh/wCDoe0Ag6GQAIOh/wCD - of8Ag6H/JtXt/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wTV7f8G1e3/B9Xt/wnW7v8K1u7/DNbu/w3W7v8O1u7/ENfu/xDX - 7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xXY7v8W2O//Ftjv/xfY7/8X2O//F9jv/xfY7/8V0ej/CqS+/wCA - nf8Af5v/AH+b/wB/m/8Gh6H/O77W/2Lj+P85yOH/B46q/wB/m/8Af5v/AH+b/wB/m/8Dl7L/Ccjh/wnW - 7v8I1e7/B9Xt/wXV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8/5fn/T+n8/0/p/P9M5vn/EZ23/wCCoP8EhaL/AIOh/wCD - of8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCCoPwAgqD/AIOh/wCpxf8A1O3/ANTt/wDU - 7f8A1O3/AJCt/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGRrv9F5/r/T+n8/0/p/P9M6Pv/Bdbu/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/A9Xt/wTV7f8H1e3/CNXu/wrW - 7v8M1u7/Ddbu/w7W7v8Q1+7/Edfu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xjY7/8Z2O//Gtjv/xrY - 7/8b2e//G9nv/xvZ7/8c2e//HNnv/xzZ7/8b2e//F8ri/wiYsv8Af5v/AH+b/wB/m/8Af5v/AYCb/wB/ - m/8Af5v/AH+b/wB/m/8DjKj/Db7X/xDX7v8P1+7/Dtbu/wzW7v8L1u7/Cdbu/wfV7f8F1e3/BNXt/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX - 7/9L6Pv/T+n8/0/p/P9N5/r/Cpm1/wCCof8Ag6H/AIOh/wCDoewAhp4VAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKBZAIKg/wCBnv8AgJ7/ALvU/wDU7f8A1O3/ANTt/wDU7f8AhqT/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/F77X/0/p/P9P6fz/T+n8/zHh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8B1O3/A9Xt/wXV7f8H1e3/Cdbu/wvW7v8M1u7/Dtbu/xDX7v8R1+7/E9fu/xTX7v8V2O7/F9jv/xjY - 7/8a2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z7/8f2e//H9nv/x/Z7/8f2e//INnv/yDZ7/8g2e//INnv/yDZ - 7/8g2e//H9nv/xbA2P8Fjqj/AH+b/wB/m/8Af5v/AH+b/wB/m/8ChaD/DbDJ/xfW7f8W2O//Fdju/xPX - 7v8S1+7/ENfu/w7W7v8N1u7/C9bu/wnW7v8H1e3/BdXt/wTV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x7d8/9P6fz/T+n8/0/p/P9I5Pf/Ao+q/wCD - of8Ag6H/AIOh/wCEoZsAAAAAAAAAAAAAAAAAAAAAAAAAAACCorQAg6H/AIKg/wCDof8AyuP/AMzk/wDO - 5v8A0uv/ANTt/wCcuP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof813vT/T+n8/0/p/P9P6fz/Edjw/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xDX - 7v8T1+7/FNfu/xXY7v8X2O//Gdjv/xrY7/8b2e//HNnv/x7Z7/8f2e//INnv/yHa7/8h2u//Itrv/yPa - 7/8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yLZ7f8UtMz/A4ai/wB/ - m/8BgJ3/DaS+/xzS6P8c2e//HNnv/xrY7/8Z2O//GNjv/xbY7/8U1+7/E9fu/xDX7v8P1+7/Ddbu/wvW - 7v8J1u7/B9Xt/wXV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p/P8r1uv/AIil/wCDof8Ag6H/AIOh/gCEnzgAAAAAAAAAAAAA - AAAAhKUfAIOh+wCDof8Ag6H/AJy5/wDU7f8A0uv/AM/n/wDL4/8Ay+P/AMfg/wCcuf8AhKL/AIOh/wCD - of8Ag6H/BZy4/03p/P9P6fz/T+n8/0Hl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV - 7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xHX7v8T1+7/Fdju/xbY7/8Y2O//Gtjv/xvZ7/8d2e//H9nv/yDZ - 7/8h2u//Itrv/yTa8P8k2vD/Jdrw/yba8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb - 8P8p2/D/Kdvw/ynb8P8o2/D/J9vw/yba8P8k1Ov/E6/H/x7L4v8k2vD/Itrv/yHa7/8f2e//H9nv/x3Z - 7/8b2e//Gtjv/xjY7/8W2O//Fdju/xPX7v8R1+7/D9fu/w3W7v8L1u7/Cdbu/wfV7f8E1e3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/C9fv/03p+/9P6fz/T+n8/03p - /P8IuNP/AIOh/wCDof8Ag6H/AIShuAAAAAAAAAAAAAAAAACDoZIAg6H/AIOh/wCDof8Au9X/ANTt/wDU - 7f8A1O3/ANTt/wDT7P8Azeb/AMvj/wDJ4P8Av9n/AK3J/wCsx/8a1+7/T+n8/0/p/P9P6fz/Jd70/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wPV7f8F1e3/CNXu/wrW7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY - 7/8Z2O//Gtjv/xzZ7/8e2e//H9nv/yHa7/8j2u//JNrw/yXa8P8m2vD/KNvw/ynb8P8q2/D/K9vw/yvb - 8P8s2/D/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb - 8P8p2/D/Kdvw/yjb8P8m2vD/Jdrw/yTa8P8i2u//Idrv/x/Z7/8e2e//HNnv/xrY7/8Y2O//F9jv/xXY - 7v8T1+7/ENfu/w7W7v8M1u7/Ctbu/wfV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Kd/0/0/p/P9P6fz/T+n8/yvg9f8Amrj/AIOh/wCDof8Ag6H+AIakKgAA - AAAAgKYUAIOh8wCDof8Ag6H/AI6q/wDS6v8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDP - 5/8A0Oj/ANLr/zLh9v9L4/f/Os7k/z3S6P8LyOD/AMni/wDO5v8A0er/ANTt/wLU7f8E1e3/B9Xt/wnW - 7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY7/8Z2O//G9nv/xzZ7/8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb - 8P8p2/D/Ktvw/yvb8P8s2/D/Ldzw/y7c8P8v3PD/MNzw/zDc8P8x3PD/Mtzx/zLc8f8y3PH/Mtzx/zLc - 8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/L9zw/y3c8P8t3PD/LNvw/yvb8P8p2/D/KNvw/yba - 8P8l2vD/JNrw/yHa7/8g2e//Htnv/xzZ7/8a2O//GNjv/xbY7/8U1+7/Etfu/xDX7v8N1u7/C9bu/wnW - 7v8G1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8G1e7/S+j7/0/p - /P9P6fz/R+f6/wHG3/8Ag6H/AIOh/wCDof8Ag6CEAAAAAACEoHwAg6H/AIOh/wCDof8Ascv/ANLq/wDQ - 6P8Azub/ANDo/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/RuX3/wqPrP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIim/wGMqf8Bj63/A5Sx/wSZtf8Fm7j/B5+6/wmlwf8LqcT/DazG/w+x - yv8Sts//FbrS/xe91f8aw9v/Hsbe/yLO5P8q2/D/K9vw/y3c8P8u3PD/MNzw/zDc8P8y3PH/Mtzx/zPd - 8f813fH/Nd3x/zXd8f823fH/Nt3x/zfd8f833fH/N93x/zfd8f833fH/Nt3x/zbd8f813fH/Nd3x/zTd - 8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//INnv/x7Z - 7/8c2e//Gtjv/xjY7/8V2O7/E9fu/xDX7v8O1u7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8s4PX/T+n8/0/p/P9P6fz/FNrw/wCZtf8Ag6H/AIOh/wCD - odYAgJ8QAIOh7ACDof8Ag6H/AIqn/wDS6/8A1O3/ANPs/wDS6v8Az+f/AM3l/wDM5P8Az+f/ANPs/wDU - 7f8A1O3/ANTt/xDZ8P9F3PD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6f - uf8w3PD/Mdzw/zLc8f803fH/Nd3x/zbd8f833fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/O97x/zve - 8f883vH/O97x/zve8f873vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zDc - 8P8v3PD/Ldzw/yvb8P8p2/D/KNvw/yba8P8k2vD/Idrv/x/Z7/8d2e//G9nv/xnY7/8W2O//FNfu/xHX - 7v8P1+7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y - 7/9P6fz/T+n8/0/p/P8t4PX/ALLO/wCDof8Ag6H/AIOh/wCDoIwAg6H/AIOh/wCDof8Arcj/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDQ6P8Azeb/AM7m/wDR6f8A1O3/It30/0/p/P8hr8j/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zHY7f813fH/N93x/zjd8f853vH/Ot7x/zze - 8f883vH/Pd7x/z7e8f8+3vH/P9/x/z/f8f9A3/L/QN/y/0Df8v9A3/L/P9/x/z/f8f8+3vH/Pt7x/z7e - 8f883vH/PN7x/zve8f853vH/ON3x/zfd8f823fH/NN3x/zLc8f8x3PD/L9zw/y3c8P8r2/D/Kdvw/yfb - 8P8l2vD/I9rv/yHa7/8f2e//HNnv/xrY7/8X2O//Fdju/xLX7v8P1+7/Ddbu/wrW7v8H1e3/BNXt/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0rn+/9P6fz/T+n8/z/k+f8Awdv/AIOh/wCD - of8Ag6H/AIOh+QCDof8Ag6H/AIuo/wDP6f8A0+z/ANLr/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDT7P8y4fb/T+n8/0/p/P8yw9r/AIak/wCDof8Ag6H/AIOh/xGZtP8/1On/QNbr/z3S - 6P86zuT/Ncfe/zHC2v8uvtb/KrnR/yazzf8jr8n/H6vF/xqlv/8Xobv/FJy4/xCYs/8Lkq//CI6r/wCD - of8arsf/ON3x/zne8f873vH/PN7x/z7e8f8+3vH/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9D3/L/RN/y/0Tf - 8v9E3/L/RN/y/0Tf8v9E3/L/Q9/y/0Pf8v9D3/L/Qt/y/0Hf8v9A3/L/P9/x/z7e8f883vH/O97x/zne - 8f843fH/N93x/zXd8f8z3fH/Mdzw/y/c8P8t3PD/K9vw/ynb8P8m2vD/JNrw/yHa7/8f2e//HNnv/xrY - 7/8Y2O//Fdju/xPX7v8Q1+7/Ddbu/wrW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/O+T4/0/p/P9P6fz/Ten8/wLL5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AtdD/ANTt/wDT - 7P8A0ur/ANHp/wDP5/8Az+f/AM/n/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/0Hm+v9P6fz/T+n8/0vo - +/8BxuD/AI+s/wCDof8Ag6H/AIOh/wqQrf8/1Or/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9O6fz/Ot/y/zre8f883vH/Pt7x/z/f8f9B3/L/Qt/y/0Pf - 8v9E3/L/ReDy/0bg8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9J4PL/SeDy/0jg8v9I4PL/SODy/0fg - 8v9G4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f863vH/Od7x/zfd8f813fH/Mtzx/zDc - 8P8v3PD/ON3x/0Df8v9F4PL/SODy/0bg8v9C3/H/Od7x/y3b8P8f2fD/Fdju/xPX7v8Q1+7/Ddbu/wrW - 7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p/P9P6fz/B8/o/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AJWy/wDT7P8A1O3/ANTt/wDT7P8A0uv/ANLq/wDR6f8Az+f/AM3m/wDM - 5P8AzOT/AM3m/wDR6f8C1O3/Tuj8/0/p/P9P6fz/POT5/wDU7f8Az+n/AJ26/wCDof8Ag6H/AIOh/wKF - pP8vwNf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vn - +v883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9G4PL/SODy/0ng8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h - 8v9N4fL/TeHy/03h8v9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9G4PL/ReDy/0Pf - 8v9C3/L/QN/y/z7e8f883vH/Ot7x/zrd8f9M4fP/X+T0/23m9f9u5vT/bOb0/2rm9P9o5vT/Z+X0/2Xl - 9P9i5PT/YeT0/1/k9P9T4vP/O97x/yDZ7/8Q1+7/Ddbu/wnW7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P8Mzeb/AIOh/wCDof8Ag6H/AIOh/wCHpf8AyeL/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLq/wvU6/9P6Pv/T+n8/0/p - /P8x4fb/ANTt/wDU7f8A1O3/AbDL/wCEo/8Ag6H/AIOh/wCDof8dqML/TOX4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+T3/0Hf8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg - 8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi8/9R4vP/UuLz/1Li8/9S4vP/UuLz/1Li8/9S4vP/UeLz/1Dh - 8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9J4PL/SODy/0Xg8v9E3/L/Qt/y/0Lf8v9b4/P/cuf1/3To - 9f9z5/X/cef1/3Dn9f9u5vT/bOb0/2rm9P9p5vT/Z+X0/2Xl9P9j5fT/YeT0/1/k9P9d5PT/W+Tz/0Xg - 8v8f2u//DNbu/wnW7v8G1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MOH2/0/p/P9P6fz/T+n8/wvG - 3/8Ag6H/AIOh/wCDof8Ag6H/ALfS/wDT7P8A0ur/AM/n/wDN5v8AzOT/AMvj/wDK4v8AyuL/AMnh/wDJ - 4f8AyeH/AMnh/wDK4v8Ay+P/E9Tp/0/o+/9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8C1O3/EMPc/wOM - qf8Ag6H/AIOh/wCDof8OlLD/Q9nu/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9G4vX/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Dh8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj - 8/9W4/P/VuPz/1bj8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h - 8v9L4fL/SuDy/0jg8v9X4/P/def1/3rp9v946fX/d+j1/3Xo9f9z6PX/aeL0/1/X9P9Y0PT/Vsv0/1TI - 9P9SzfT/VNLz/1jb8/9f5PP/YOT0/17k9P9d5PT/W+Tz/1nj8/873vH/Etfu/wjV7v8F1e3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/BrXQ/wCDof8Ag6H/AIOh/wCpxf8A1O3/ANPs/wDR - 6f8Azub/AM3l/wDL4/8AyuL/AMri/wDK4v8AyuL/AMzk/wDN5f8Azub/ANHp/wDT7P8c2/L/T+n8/0/p - /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wLU7f8Z2O//GtHo/wiZtf8Ag6H/AIOh/wCDof8Eiab/Ncfe/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8/9I4PL/SuDy/03h8v9O4fP/UOHz/1Li - 8/9T4vP/VOLz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj - 8/9Z4/P/WePz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UeLz/0/h8/9Q4fL/bOf0/4Dp9v9/6fb/fOn2/3vp - 9v9x5PX/Xsv0/1Gz8v9SovP/UqLz/1Kh8/9SofP/UqHz/1Kh8/9SofP/UqHz/06n8v9MvfP/UdTz/1zj - 9P9c5PT/WuPz/1jj8/9O4fP/Htnv/wfV7f8E1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k+f9P6fz/T+n8/0zo - +/8Anbj/AIOh/wCDof8Ag6HpANTt/wDU7f8A0+z/ANPs/wDT7P8A1O3/ANTt/wDU7f8AutT/AIOh/wCq - xf8AtM//AL/a/wDK5P8A0+z/ANTt/xzb8v9P6fz/T+n8/0/p/P8d3PL/ANTt/wDU7f8A1O3/A9Xt/xvZ - 7/8f2e//Idnu/xGsx/8Ag6H/AIOh/wCDof8Ag6H/I7DK/03n+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9N5/v/SuDy/03h8v9P4fP/UOHz/1Li8/9U4vP/VuPz/1fj8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k - 9P9f5PT/X+T0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj - 8/9V4vP/WOPz/3np9v+B6vb/gOr2/3/q9v925fX/Wsby/06j8f9Pn/H/T5/w/0+f8P9Pn/D/T5/w/0+f - 8P9PnvD/T57w/0+e8P9PnvD/Tp7w/06e8P9OnvD/Rq/w/0rU8v9Y4/T/V+Lz/1Ti8/9R4vP/Jtvw/wbV - 7f8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Suf7/0/p/P9P6fz/P+T5/wCHpP8Ag6H/AIOh/wCCoa4A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ALPN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFo/8Am7f/G9Tr/0/p - /P9P6fz/T+n8/yHd8/8A1O3/ANTt/wDU7f8C1O3/Htnv/yHa7/8k2vD/J9vw/x7C2P8Diab/AIOh/wCD - of8Ag6H/Epu2/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/07l9/9O4fP/UOHz/1Li8/9U4vP/VuPz/1nj - 8/9a4/P/W+Pz/17k9P9e5PT/YOT0/2Hk9P9i5fT/YuX0/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/ZOX0/2Pl - 9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9c5PP/W+Pz/13k8/986fb/gur2/4Hq9v9/6vb/atj0/02l - 7v9MnO7/TJzu/0yc7v9MnO7/Tp3t/2il4/97rd3/hrDZ/4uy1v9/rdr/c6ne/1ig6f9Lm+3/S5vt/0ub - 7f9Lm+3/S5vt/0O57v9P4PL/UeLz/0/i8/9N4fP/Jdrw/wXV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wzY8P9P6fz/T+n8/0/p - /P8y2O7/AIOh/wCDof8Ag6H/AIKhZADS6v8A0ur/ANHp/wDR6f8A0en/ANHp/wC40v8Ag6L/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Xssz/T+n8/0/p/P9P6fz/It70/wDU7f8A1O3/ANTt/wDU - 7f8h2u//JNrw/yfb8P8q2/D/Ldzw/yrR5v8KlbH/AIOh/wCDof8Ag6H/B4up/zvP5f9P6fz/T+n8/0/p - /P9P6fz/T+T2/1Li8/9U4vP/VuPz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9k5fT/ZeX0/2bl - 9P9n5fT/Z+X0/2jm9P9o5vT/aeb0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuX0/2Dk - 9P9f5PT/fen2/4Pq9v+B6vb/f+r2/2PK8v9Kmuz/SZns/0mZ7P9Jmez/YaLk/5W10f+2wsf/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vMTE/6i8zP9+rNn/TJrq/0iY6/9HmOr/R5jq/0Op7P9J2vH/SuHy/0ng - 8v9G4PL/Gtju/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/H9zz/0/p/P9P6fz/T+n8/yK+1v8Ag6H/AIOh/wCDofwAhaMZAMzk/wDM - 5P8AzeX/AM3m/wDQ6P8Avtj/AIWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xWl - wP9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANTt/yDZ7/8m2vD/Ktvw/y3c8P8w3PD/M93x/zTa - 7v8XqMH/AIOh/wCDof8Ag6H/AYSi/ym4z/9P6fz/T+n8/0/p/P9T4vT/VeLz/1jj8/9a4/P/XOTz/17k - 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9s5vX/beb1/23m9f9t5vX/beb1/2zm - 9f9s5vX/bOb1/2vm9f9q5vT/aOb0/2fl9P9l5fT/ZOX0/3bo9f+E6vb/gur2/4Dq9v9iwvD/Rpbp/0aW - 6f9Glun/UJnm/5a00P+8xMT/vcTE/77Fxf+/xcX/v8bG/8DHx//Bx8f/wcjI/8LIyP/Dycn/w8rK/8TK - yv+4xs3/cafd/0SV6P9Elej/RJXo/0Sf6v9F2PH/ReDy/0Lf8v893/L/C9bt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p - /P9P6fz/Dpez/wCDof8Ag6H/AIShuAAAAAAA1O3/ANTt/wDU7f8A1O3/AL7Y/wCIpf8Ag6H/AIOh/wCD - of8Ag6KkAImdDQCFoS4AhKFRAIOh/wCDof8Ag6H/Ep65/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU - 7f8A1O3/Htnv/ynb8P8s2/D/MNzw/zLc8f823fH/Od7x/zze8f8ovdP/A4ek/wCDof8Ag6H/AIOh/xeh - vP9J4vX/UOj7/1bj8/9Z4/P/W+Pz/17k9P9g5PT/YuX0/2Xl9P9m5fT/aOb0/2rm9P9s5vX/beb1/27n - 9f9v5/X/cOf1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/b+f1/27n9f9s5vX/a+b1/2rm - 9P9v5/X/her2/4Pq9v+B6vb/acvt/0OU5/9Dk+b/Q5Pm/1uc4P+xwcr/wcjI/8LIyP/Cycn/w8rK/8PK - yv/CyMj/wsjI/8HIyP/Dycn/xcvL/8jOzv/Jzs7/yc/P/8rQ0P/K0ND/krbW/0KS5P9BkuX/QZLl/0Gh - 6P9A3PH/Pt7x/zve8f8n2/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AtXt/0rn+/9P6fz/T+n8/0nl+P8BhKL/AIOh/wCDof8AhKBbAAAAAADU - 7f8A1O3/ANTt/wC0z/8AhKL/AIOh/wCDof8Ag6H/AIOhvQCAqgYAAAAAAAAAAAAAAAAAgqGNAIOh/wCD - of8Kj6z/T+n8/0/p/P8+1On/Dp25/wCTsP8AxN//ANTt/wDU7f8a2O//K9vw/y7c8P8y3PH/Nd3x/zjd - 8f883vH/Pt7x/0Lf8v85z+P/CpGt/wCDof8Ag6H/AIOh/wqQrf9B0ub/WePz/1zk8/9f5PT/YeT0/2Tl - 9P9m5fT/aOb0/2rm9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f926PX/duj1/3bo - 9f926PX/dej1/3To9f9z5/X/cuf1/3Hn9f9v5/X/buf1/4Dp9v+F6/b/gur2/3XZ8/8xbar/QJHk/0CR - 5P9Vm+H/u8fO/8bMzP/Hzc3/x83N/8XLy//Ax8f/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/v8XF/8XL - y//O09P/z9TU/9DV1f/Q1dX/nb3Z/z6P4v8+j+L/Po/i/zyw6P863vH/ON3x/zLd8P8M1u7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p - /P9P6fz/M8je/wCDof8Ag6H/AIOh6wCOqgkAAAAAANTt/wDT7P8AqMT/AIOh/wCDof8Ag6H/AIOh/wCE - obwAgJkKAAAAAAAAAAAAAAAAAAAAAACDoX0Ag6H/AIOh/wGEov9K5Pj/I6/J/wGEov8Ag6H/AIOh/wCi - vv8A1O3/ANTt/xXY7v8t3PD/Mdzw/zTd8f833fH/O97x/z7e8f9B3/L/ReDy/0jg8v9G2+7/GaG8/wCD - of8Ag6H/AIOh/wKFpP9N1Ob/YOT0/2Ll9P9l5fT/Z+X0/2rm9P9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo - 9f936PX/eOj1/3no9v966fb/e+n2/3vp9v976fb/e+n2/3rp9v966fb/eOj1/3jo9f926PX/dej1/3Pn - 9f936PX/huv3/4Tq9/984vj/SKnr/yVViP86iNj/SZTf/7nI0f/K0ND/y9HR/8vR0f/Dysr/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/7/Gxv/P1NT/1dra/9ba2v/W29v/irTd/zqM - 4P86jOD/Oo3g/zXQ7v8z3PH/K9zw/x7Z7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y3g9f9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8AgqKDAAAAAAAA - AAAAx+H/AJe0/wCDof8Ag6H/AIOh/wCDof8AhKGdAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCD - of8Ag6H/AIOh/wmQrP8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/Dtbu/zDc8P8z3fH/N93x/zne - 8f893vH/Qd/y/0Pf8v9H4PL/SuDy/03h8v9R4vP/L7jO/wGFov8Ag6H/AIOh/yyvx/9i5fT/ZeX0/2jm - 9P9r5vX/beb1/3Dn9f9y5/X/dOj1/3bo9f946PX/eun2/3vp9v996fb/fen2/37p9v9/6fb/f+n2/3/p - 9v9/6fb/f+n2/37p9v996fb/fOn2/3vp9v946PX/d+j1/4Hq9v+G6vb/hOr2/2HF+P89rfn/JFiM/y9y - uP+du9b/z9TU/9DV1f/Q1dX/w8rK/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+5ta7/tKGO/7KZ - g/+0o5H/urm0/77Fxf/U2dn/29/f/9zg4P/a3+D/Vpnc/zeJ3f83id3/NaLi/y/c8P8n2+//I9rv/wXV - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/SOf6/0/p - /P9P6fz/Tef6/wWJp/8Ag6H/AIOh+QCFoxkAAAAAAAAAAACEov8Ag6H/AIOh/wCDof8Ag6H+AIOgeQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/DZOw/zvP - 5v8W2vH/ANTt/wDU7f8E1e3/Mtzx/zXd8f853vH/PN7x/z/f8f9D3/L/RuDy/0ng8v9N4fL/UOHz/1Pi - 8/9W4/P/QMjb/wCDof8MkKz/TtHj/2bl9P9p5vT/bOb1/2/n9f9x5/X/c+f1/3bo9f946PX/eun2/3zp - 9v9+6fb/f+n2/4Hq9v+C6vb/g+r2/4Tq9v+E6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v+A6vb/fun2/33p - 9v986fb/h+v2/4br9v+D6vb/R7L5/z6v/P8rdbH/PGiT/9PY2f/U2dn/1dra/8nPz/+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+6ubP/q4Bd/6RlNP+kZTT/pGU0/6RlNP+kZjb/ropr/77Bvv/d4OD/4eTk/+Ll - 5f+8z+H/NIbb/zSG2/8zhtr/KdPu/yHa7/8d2e//Dtbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXa8f9P6fz/T+n8/0/p/P83yuD/AIOh/wCDof8Ag6KhAAAAAAAA - AAAAAAAAAIOh/wCDof8Ag6H/AIOg6ACDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF - oHMAg6H/AIOh/wCDof8Ag6H/AoWj/ye1zv9N5/n/T+n8/0Tm+f8C1O3/ANTt/wDU7f8t3PD/N93x/zre - 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9S4vP/VuPz/1nj8/9c5PP/YOT0/2Ll9P9l5fT/aeb0/2zm - 9f9v5/X/cef1/3To9f936PX/eej2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+H6/f/iOv3/4jr - 9/+I6/f/iOv3/4fr9/+H6/f/her2/4Tq9v+C6vb/gOr2/4Lq9v+I6/f/huv3/3jd9/8/r/z/Pq/8/zea - 4v9jc4H/w8bG/9re3v/W2tr/vsTE/73ExP+9xMT/vsXF/8DGxv/ByMj/u7Sp/6dsP/+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/p3JH/8bBuv/m6en/5+np/+fq6v9Sldn/MIPY/zCD2P8ptuX/Gtnv/xfY - 7/8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p - /P9P6fz/T+n8/xulwP8Ag6H/AIOh/wCCnzUAAAAAAAAAAAAAAAAAg6H/AIOh/wCDoasAgKYUAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEnh0AgqK0AIOh/wCDof8Ag6H/AIOh/xGZtf9B1+z/T+n8/0/p - /P9P6fz/T+n8/ybe9f8A1O3/ANTt/yDZ7/853vH/PN7x/0Df8v9D3/L/R+Dy/0rg8v9O4fP/UeLz/1Ti - 8/9Y4/P/W+Pz/17k9P9i5fT/ZeX0/2jm9P9s5vX/b+f1/3Hn9f916PX/eOj1/3vp9v996fb/f+n2/4Lq - 9v+E6vb/huv2/4jr9/+J6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+M7Pf/i+v3/4vr9/+J6/f/iOv3/4br - 9v+E6vb/h+v2/4jr9/+G6/f/XcT2/z6v/P8+r/z/Pq/8/3OLnf+go6P/3+Li/8rQ0P+9xMT/vsXF/8HH - x//Dycn/xcvL/8XIxf+obkL/pGU0/6RlNP+jZTT/fU0o/2Y/IP+FUir/pGU0/6RlNP+kZTT/qHhQ/+Tn - 5v/s7u7/7e/v/52+3v8tgNX/LYDV/yqX3P8T1+7/Etfu/xLX7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7v9L6Pv/T+n8/0/p/P9L5fj/A4el/wCDof8Ag6H/AAAAAAAA - AAAAAAAAAAAAAACDotIAg59IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6JrAIOh8gCD - of8Ag6H/AIOh/wSIpf8tvdT/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wrW7/8A1O3/Edfu/zre - 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Tl9P9n5fT/a+b1/27n - 9f9x5/X/dOj1/3jo9f976fb/fen2/4Dq9v+D6vb/her2/4jr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Hs - 9/+R7Pf/kez3/5Hs9/+Q7Pf/j+z3/47s9/+L6/f/iev3/4fr9/+J6/b/iOv2/4Xq9v9MtPT/Pq/8/z6v - /P8+r/z/WaHS/4mKiv/Y29v/wMbG/7/Gxv/Dycn/xszM/8nOzv/L0ND/vaaR/6RlNP+kZTT/nmEy/ykZ - Df8AAAD/AAAA/wAAAP8+JhT/o2U0/6RlNP+kZTT/zL+y//Lz8//y9PT/0t/q/yp90/8qfdP/KoDT/wzW - 7v8M1u7/DNbu/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gtvy/0/p - /P9P6fz/T+n8/zLF3P8Ag6H/AIOh/wCDoaMAAAAAAAAAAAAAAAAAAAAAAP//AQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6IpAIOhxQCDof8Ag6H/AIOh/wCDof8WoLv/Rdzw/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/NuP3/wDU7f8D1e3/Ot7x/z/f8f9D3/L/R+Dy/0rg8v9O4fP/UuLz/1Xi - 8/9Z4/P/XOTz/2Dk9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3Pn9f936PX/e+n2/33p9v+A6vb/g+r2/4fr - 9/+J6/f/i+v3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/le34/5bt+P+W7fj/le34/5Xt+P+T7ff/kez3/5Ds - 9/+N7Pf/i+v3/4vr9/+I6/b/hev2/0Wv9v8+r/z/Pq/8/z6v/P9Brvn/g5CZ/6qtrf/Axsb/xMrK/8fN - zf/L0ND/ztPT/9DV1f+xhGD/pGU0/6RlNP9SMxr/AAAA/wAAAP8AAAD/AAAA/wAAAP9qQSL/pGU0/6Rl - NP+5lnn/9/j4//j5+f/r8PT/J3rQ/yd60P8netD/Cs/s/wfV7f8H1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/FqC7/wCDof8Ag6H/AIOgcQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAISifgCDofkAg6H/AIOh/wCD - of8Gi6j/MsTb/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/F9vx/wDU - 7f8r2/D/Qd/y/0Tf8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n - 9f9z5/X/duj1/3no9v996fb/gOr2/4Pq9v+H6/f/iev3/4zs9/+P7Pf/ku33/5Tt9/+W7fj/mO74/5nu - +P+a7vj/mu74/5ru+P+a7vj/mO74/5ft+P+V7fj/k+33/5Ds9/+O7Pf/i+v3/4jr9/+F6vb/P6z3/z6v - /P8+r/z/P6/8/0Wy/P9lptL/aGlp/4KFhf/Izs7/zNHR/8/U1P/T19f/1tra/6pzSP+kZTT/pGU0/y0c - Dv8AAAD/AgIC/woKCv8RERH/FRUV/1E4Jf+kZjX/pGU0/7CCXv/8/Pz//f39//z9/f8jd83/I3fN/yN3 - zf8Hxej/AdTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Bdbu/03p - /P9P6fz/T+n8/0jl+P8BhaL/AIOh/wCDof8AhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAISfOACDodMAg6H/AIOh/wCDof8Ag6H/HKfB/0jh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vr/AtTu/xTX7v9C3/L/ReDy/0ng8v9N4fL/UeLz/1Ti - 8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr - 9/+M7Pf/j+z3/5Lt9/+V7fj/mO74/5ru+P+c7vj/ne74/5/v+P+f7/j/n+/4/57v+P+c7vj/m+74/5nu - +P+W7fj/k+33/5Ds9/+O7Pf/iuv3/4fr9/9Arff/Pq/8/z6v/P9Fsfz/SrT8/1C2+/8oLjL/HB0d/8rQ - 0P/Q1dX/1NjY/9fb2//b3t7/rnxU/6RlNP+kZTT/OyQT/xsbG/9GRkb/TExM/1NTU/9ZWVn/lIBx/7mJ - ZP+kZTT/toxr////////////+fv8/yB0y/8gdMv/IHTL/wTJ6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8g3PP/T+n8/0/p/P9P6fz/Lsvi/wCDof8Ag6H/AIOh/ACJ - nQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqDACCopEAg6H9AIOh/wCDof8Ag6H/CY+s/zjM - 4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8n3vX/AdTt/z7e8f9H4PL/SuDy/07h8/9S4vP/VeLz/1nj8/9d5PT/YOT0/2Tl9P9n5fT/a+b1/2/n - 9f9z5/X/duj1/3rp9v996fb/ger2/4Tq9v+I6/f/i+v3/47s9/+S7ff/le34/5ju+P+b7vj/nu/4/6Dv - +P+h7/j/o+/5/6Tw+f+k8Pn/ou/4/6Hv+P+f7/j/nO74/5nu+P+W7fj/k+33/5Ds9/+M7Pf/iev3/0Ov - 9v8+r/z/Q7H8/0mz/P9Ptvz/QpDE/wgOEf8HBwf/e39//9PY2P/Y3Nz/3N/f/9/j4//BoIX/pGU0/6Rl - NP+KVSz/aGZl/4iIiP+Ojo7/lZWV/6Kfnf/Yw7P/28Ov/6RlNP/Ks6D////////////s8fX/HXHI/x1x - yP8dcsj/AdLs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k - +f9P6fz/T+n8/0/p/P8Qn7r/AIOh/wCDof8Ag6HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - olUAg6H/AIOh/wCDof8Ag6H/AIOh/w2TsP82yeD/Os7k/zrO5P86zuT/Oczi/zXH3v81x97/Ncfe/zXH - 3v81x97/NMbd/y/A1/8vwNf/L8DX/y/A1/8vwNf/L7/X/ym50f8FsMr/Gb3W/0jg8v9L4fL/T+Hz/1Pi - 8/9W4/P/WuPz/17k9P9i5fT/ZeX0/2nm9P9s5vX/cOf1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr - 9/+N7Pf/kOz3/5Tt9/+Y7vj/m+74/57v+P+h7/j/pPD5/6bw+f+n8Pn/qPD5/6jw+f+m8Pn/pPD5/6Hv - +P+f7/j/m+74/5ju+P+V7fj/kez3/47s9/+K6/f/SrT0/0Cv/P9Hs/z/TrX8/1O09v8RIy//OXCV/wIC - A/8WFxf/0dbW/9vf3//g4+P/5Ofn/9vTyv+lZjb/pGU0/6RlNP/AppL/19LO/9jV0v/h3dr/8Onj//Xu - 6f/m1Mb/pms9/+ro5P///////////8jY5/8absb/Gm7G/xd7yv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1/D/T+n8/0/p/P9P6fz/QuP3/wCDof8Ag6H/AIOh/wCD - onMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JlAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0zh8v9P4fP/VOLz/1fj8/9b4/P/XuT0/2Ll9P9m5fT/aub0/23m - 9f9x5/X/dej1/3jo9f986fb/gOr2/4Tq9v+H6/f/i+v3/47s9/+S7ff/lu34/5ru+P+d7vj/oe/4/6Tw - +f+n8Pn/qvH5/6zx+f+t8fn/rPH5/6rx+f+n8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+T7ff/j+z3/4vr - 9/9gyfj/RLH8/0u0/P9St/z/KFVz/zlxlv9aqd7/ChAU/xYXF/9eYGD/3uHh/+Ll5f/n6ur/7O7u/8io - jv+kZTT/pGU0/610SP/w5d3/////////////////9vDr/7SBWf/Drpv//v7+////////////daTT/xZr - w/8Wa8P/DpfU/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf - 9P9P6fz/T+n8/0/p/P8lyOD/AIOh/wCDof8Ag6H/AIafKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oasAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8YsMn/TeHy/1Dh - 8/9U4vP/WOPz/1vj8/9f5PT/Y+X0/2fl9P9q5vT/buf1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr - 9/+L6/f/kOz3/5Pt9/+X7fj/mu74/5/v+P+i7/j/pvD5/6nw+f+t8fn/sPL5/7Hy+f+w8vn/rPH5/6nw - +f+l8Pn/oe/4/57v+P+a7vj/l+34/5Pt9/+P7Pf/i+v3/3ne+P9Hsvz/T7b8/0yj3f9UquP/ZL/8/z5x - kv8nQ1b/KjpF/xITE/+Tlpb/5Ofn/+rs7P/v8PD/8/X1/9O3of+maTr/pGU0/6VnN/+/knD/zKiM/8GX - df+rckf/x7Wl//Hy8v////////////f5+v8bbMH/E2jA/xNowP8Ft+H/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/Ref6/0/p/P9P6fz/Tun8/wmcuf8Ag6H/AIOh/wCD - odwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISjOgCDof8Ag6H/AIOh/wCDoe4Ag6HhAISi3QCE - ot0AhKLdAISi3QCEot0Ag6HRAIOhzACDocwAg6HMAIOh7wCDof8Ag6H/AISi/w+cuP8Vnrn/FZ65/xWe - uf8Gnbn/AJ66/wCeuv8Anrr/AKnF/wzS6/9M4fL/UeLz/1Ti8/9Y4/P/W+Pz/2Dk9P9j5fT/Z+X0/2vm - 9f9v5/X/cuf1/3bo9f966fb/fen2/4Hq9v+E6vb/iev3/4zs9/+Q7Pf/lO33/5ju+P+b7vj/n+/4/6Pv - +f+m8Pn/qvH5/67x+f+y8vr/tPL6/7Dy+f+t8fn/qfD5/6bw+f+h7/j/nu/4/5ru+P+X7fj/k+33/4/s - 9/+L6/f/h+v3/0u0+P9Rt/z/Wbr8/2G9/P9nv/r/Ficz/12cx/8vTF//DQ8R/wsMDP++v7//6+3t//Dx - 8f/19vb/+/v7//Hp4//KqI3/t4di/7F9VP+2iGT/xKaO/9nW0v/o6ur/////////////////l7rb/xBl - vv8QZb7/EG/C/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xTZ - 8f9P6fz/T+n8/0/p/P863/T/AIOh/wCDof8Ag6H/AIKhhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJNAIOh/wCDof8Ag6H/GMXe/0/p/P9P6fz/T+n8/zfj9/8A1O3/ANTt/wDU7f8A1O3/ANTt/zHc - 8P9R4vP/VOLz/1jj8/9c5PP/YOT0/2Pl9P9n5fT/a+b1/2/n9f9y5/X/duj1/3rp9v996fb/ger2/4Tq - 9v+J6/f/jOz3/5Ds9/+T7ff/mO74/5vu+P+f7/j/ou/4/6bw+f+p8Pn/rPH5/6/y+f+w8vn/rvH5/6vx - +f+o8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+S7ff/juz3/4vr9/+H6/f/Zs34/1S4/P9cu/z/ZL/8/0By - lP81W3X/esj9/ypCUv8THCL/AAAA/xscHP/W2Nj/7/Dw//T19f/4+Pj/+fn5//b39//x8/P/7O7u/+fq - 6v/i5eX/6evr/////////////////+zx9f8SZrz/DWO7/wxiu/8Motj/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p/P9P6fz/T+n8/x291v8Ag6H/AIOh/wCD - of0AgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAvwQAg6HnAIOh/wCDof8BlrP/Q+X5/0/p - /P9P6fz/Tun8/wzY8P8A1O3/ANTt/wDU7f8A1O3/Ctbu/0/h8/9U4vP/WOPz/1vj8/9g5PT/Y+X0/2fl - 9P9q5vT/b+f1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr9/+L6/f/j+z3/5Pt9/+W7fj/mu74/53u - +P+h7/j/pPD5/6bw+f+p8Pn/q/H5/6vx+f+q8fn/qPD5/6Xw+f+i7/j/n+/4/5zu+P+Y7vj/le34/5Hs - 9/+O7Pf/iev3/4br9v9/5vf/Vrj5/168/P9XotT/PWqJ/3fG/f9+yv3/EBge/xsoMP8FBgj/BAQE/yco - KP/b3d3/8PHx//Lz8//z9PT/8fPz/+7w8P/q7Oz/5+rq//L09P/////////////////2+Pr/MXnC/wlf - uP8JX7j/DHDA/wbS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV - 7v9P6fz/T+n8/0/p/P9L6Pv/A4uo/wCDof8Ag6H/AIKhtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAGZmCgByjeEAg6H/AIOh/wCDof8f0Oj/T+n8/0/p/P9P6fz/NOL3/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Kdvw/1Ti8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9u5/X/cef1/3Xo9f946PX/fOn2/4Dq - 9v+E6vb/h+v3/4vr9/+O7Pf/kez3/5Xt+P+Y7vj/m+74/5/v+P+h7/j/pPD5/6Xw+f+m8Pn/pvD5/6bw - +f+k8Pn/ou/4/5/v+P+c7vj/me74/5bt+P+T7ff/j+z3/4vr9/+I6/f/hOr2/4Hq9v9t1fj/X738/2Gy - 6v9wxP3/ecf9/2ijy/8YJCz/IC44/xgiKf8oNj//AAAA/yoqKv/P0dH/7vDw/+7w8P/v8fH/8PLy//b2 - 9v/+/v7/////////////////9ff4/0uJxv8GXbb/Bl22/wZet/8VueH/AdTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Kt/0/0/p/P9P6fz/T+n8/zHT6f8Ag6H/AIOh/wCD - of8AhKFRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVgAYAWm+1AF91/wCDof8Ag6H/AIOh/wOa - t/9J6Pv/T+n8/0/p/P9P6fz/Etnw/wDU7f8A1O3/ANTt/wDU7f8D1e3/SODy/1fj8/9b4/P/XuT0/2Ll - 9P9l5fT/aub0/23m9f9x5/X/dOj1/3jo9f976fb/f+n2/4Lq9v+G6/b/iev3/4zs9/+Q7Pf/k+33/5bt - +P+Z7vj/m+74/57v+P+f7/j/oe/4/6Lv+P+i7/j/oe/4/6Dv+P+e7/j/nO74/5nu+P+W7fj/k+33/5Ds - 9/+N7Pf/iev3/4fr9/+D6vb/f+n2/3zp9v9mx/n/aMH8/3HE/f96yP3/Mk9i/2+myv8SGiD/L0FM/1V0 - iP8AAAD/AgMD/w8PD/+BgYH/+fn5/////////////////////////////////+Ho7/8ncLz/A1q0/wNa - s/8DWrP/EqXX/xDX7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW - 7/9M6Pv/T+n8/0/p/P9O6Pz/DZm1/wCDof8Ag6H/AIOh5ACAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVVUDAFtwqQBab/8AWm//AHWQ/wCDof8Ag6H/AIOh/yXT6v9P6fz/T+n8/0/p/P8+5fn/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Qv9j/O8Xa/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n9f9z5/X/duj1/3rp - 9v996fb/gOr2/4Tq9v+H6/f/iuv3/47s9/+Q7Pf/k+33/5Xt+P+Y7vj/mu74/5zu+P+c7vj/ne74/53u - +P+c7vj/nO74/5ru+P+Y7vj/lu34/5Pt9/+Q7Pf/juz3/4vr9/+H6/f/hOr2/4Hq9v996fb/eun2/3Xl - 9v9nxfn/ccT9/2CeyP9ek7j/hMby/wEBAf9kiqT/f6/O/wAAAP8lNkL/AAAA/wQEBP8LCwv/SkpK/5CQ - kP+6urr/3t7e/9LV1v9rjrH/A1iw/wBXsf8AV7H/AFmy/xGd0v8c2e//CNXu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ld/1/0/p/P9P6fz/T+n8/zLT6f8Ag6H/AIOh/wCD - of8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBZbp0AWm//AFpv/wBab/8XiqH/AYSi/wCD - of8Ag6H/Ape0/0bn+v9P6fz/T+n8/0/p/P8b2/L/ANTt/wDU7f8A1O3/AK/K/wCDof8Ag6H/N7/U/1zk - 8/9g5PT/Y+X0/2fl9P9q5vT/buf1/3Hn9f916PX/eOj1/3vp9v9/6fb/gur2/4Xq9v+I6/f/i+v3/47s - 9/+Q7Pf/k+33/5Tt9/+W7fj/mO74/5ju+P+Z7vj/me74/5ju+P+Y7vj/lu34/5Tt9/+S7ff/kOz3/43s - 9/+L6/f/h+v3/4Tq9v+C6vb/fun2/3vp9v946PX/dOj1/3Dk9v9rw/H/ZafU/4DK/f9spMn/FiEo/5HO - 9/+Rzfb/AAAA/1N9mf8AAAD/BQcI/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ADhz/ADl0/wNg - tv8Ytd7/H9nv/xrY7/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y - 8P9O6Pz/T+n8/0/p/P9O6Pz/C5az/wCDof8Ag6H/AIOg6ACLogsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnCQAFpv/wBab/8AWm//GIWZ/03m+f8cp8H/AIOh/wCDof8Ag6H/G8rj/0/p/P9P6fz/T+n8/0fm - +v8G1e7/ANTt/wDE3v8AhaP/AIOh/wCDof8mtc3/W+Pz/1/k9P9i5fT/ZeX0/2nm9P9s5vX/b+f1/3Pn - 9f926PX/eej2/3zp9v9/6fb/gur2/4Xq9v+I6/f/iuv3/4zs9/+O7Pf/kOz3/5Lt9/+T7ff/lO33/5Tt - 9/+U7ff/lO33/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iev3/4fr9/+E6vb/gur2/3/p9v986fb/eOj1/3bo - 9f9y5/X/b+f1/2zk9f9w0vr/fcn9/0Zth/9wp83/jtD9/47Q/f8DBAX/aJ7D/xIdJP8AAAD/BQkL/wUK - Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Cj1F/x7I2/8d2e//D9fu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/QOX5/0/p/P9P6fz/T+n8/y7N5P8Ag6H/AIOh/wCD - of8AhKJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/xR/k/9M5Pb/T+n8/0LY - 7f8BhKL/AIOh/wCDof8Ai6n/PuT4/0/p/P9P6fz/T+n8/zTi9/8A0uv/AJCt/wCDof8Ag6H/CpCt/zze - 9P9U4/T/XeT0/2Dk9P9k5fT/Z+X0/2rm9P9u5/X/cef1/3Pn9f936PX/eun2/33p9v9/6fb/gur2/4Tq - 9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/kOz3/5Ds9/+P7Pf/juz3/47s9/+M7Pf/iuv3/4jr - 9/+G6/b/hOr2/4Hq9v9/6fb/fOn2/3no9v926PX/c+f1/3Dn9f9s5vX/aub0/2bl9P9p2/b/YqLI/4PM - /f+Hzf3/fb/r/wAAAP9ys9//LEhb/wAAAP8lQ1b/CREX/y1bev8AAQH/AAAA/wQLEP8AAQH/AAAA/wAA - AP8AAAD/BB4h/xi6zP8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yXe - 9P9P6fz/T+n8/0/p/P9H5fn/BYqn/wCDof8Ag6H/AIOh5ACOqgkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb - b3MAWm//AFpv/wBab/8Pdor/OcHV/yqluf8XhZj/BWN3/wBzjf8Ag6H/AIOh/wCDof8Qssz/Tuj8/0/p - /P9P6fz/T+n8/wypxP8Ag6H/AIOh/wGEov880Ob/T+n8/0rn+v9X5fX/XuT0/2Ll9P9l5fT/aOb0/2vm - 9f9v5/X/cef1/3To9f936PX/eun2/33p9v9/6fb/ger2/4Pq9v+F6vb/h+v3/4nr9/+J6/f/iuv3/4vr - 9/+L6/f/i+v3/4vr9/+K6/f/iev3/4jr9/+G6/b/hOr2/4Lq9v+A6vb/fen2/3vp9v946PX/duj1/3Pn - 9f9w5/X/beb1/2rm9P9n5fT/Y+X0/2Dk9P9d4/T/aNn3/3jO+/9WiKv/ERwj/3rI/f84XXf/BAgK/1CN - t/8VFRX/OXSb/ytojv8LCwv/ChMV/yCXpf8OTFP/AAEB/wAAAP8AAAD/Ahga/wC90/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8S2fD/Ten8/0/p/P9P6fz/T+n8/xqwyf8Ag6H/AIOh/wCD - of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29lAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AF1z/wB/nP8Ag6H/AIOh/wCDof8o1ev/T+n8/0/p/P86zeT/AIOh/wCDof8Ag6H/JbPM/0/p - /P9P6fz/T+n8/0/o/P9X5vj/YOT0/2Ll9P9m5fT/aeb0/2zm9f9v5/X/cef1/3To9f926PX/eej2/3vp - 9v996fb/f+n2/4Lq9v+D6vb/hOr2/4Xq9v+G6/b/h+v3/4fr9/+H6/f/h+v3/4br9v+E6vb/hOr2/4Lq - 9v+A6vb/fun2/3zp9v966fb/eOj1/3Xo9f9z5/X/cOf1/23m9f9q5vT/Z+X0/2Tl9P9h5PT/XuT0/1vj - 8/9X4/P/VOLz/xg+RP84hZf/XdT3/ypjdP8TMDj/S8/u/zI3OP8yYWf/Nd3x/y19hv8kJCT/Ij1B/yjQ - 5P8hr8D/Fzo+/xEREf8ODg7/CC0y/wDM5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0bn - +v9P6fz/T+n8/0/p/P832O3/AIOh/wCDof8Ag6H/AIShvgD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvVQBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AGqC2ACDof8Ag6H/AIOh/wKR - rv8/5Pn/SeL1/wiOq/8Ag6H/AIOh/w+Wsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9U5/n/X+T1/2Pl - 9P9m5fT/aeb0/2zm9f9v5/X/cef1/3Pn9f926PX/eOj1/3rp9v986fb/fen2/3/p9v+A6vb/ger2/4Lq - 9v+C6vb/gur2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/fen2/3zp9v976fb/eOj1/3bo9f906PX/cef1/2/n - 9f9s5vX/aub0/2fl9P9k5fT/YeT0/17k9P9b4/P/WOPz/1Xi8/8+rLn/IV5l/0vh8v9I4PL/GVNZ/yV/ - iv8+3vH/SnJ3/0ldX/803fH/Mdvv/z1hZf89PT3/MXuE/yLa7/8f1On/IXiD/ysrK/8nJyf/FWhy/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f844/j/T+n8/0/p/P9P6fz/RuX5/waNq/8Ag6H/AIOh/wCD - ofcAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/bAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv9QBa - b70AWm5/AFlwQgBmZgoAAAAAAIOgrwCDof8Ag6H/AIOh/wyqxf8apcD/AIOh/wCDof8ChqT/Qtjt/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9S6Pr/XeX1/2Pl9P9m5fT/aeb0/2vm9f9u5/X/cOf1/3Ln - 9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3vp - 9v956Pb/eOj1/3bo9f906PX/cuf1/3Dn9f9u5/X/bOb1/2nm9P9n5fT/ZOX0/2Hk9P9e5PT/W+Pz/1jj - 8/9V4vP/UuLz/zmir/9M4fL/SODy/0Xg8v8QNzz/O9Dh/zze8f9VnKX/ZW9w/zLc8f8u3PD/Nr/P/1pa - Wv9WWFn/KMTX/xvZ7/8D1e3/HJ6t/0NHSP9AQUH/EKy//wDU7f8A1O3/ANTt/wDU7f8A1O3/LuD2/0/p - /P9P6fz/T+n8/07o/P8TpL//AIOh/wCDof8Ag6H/AISgfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b7wAWm//AFpv/wBab/8AWm+1AFlveABYbjoAZmYFAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh7ACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y291P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9Q6fz/Wub3/2Pl9P9l5fT/aOb0/2rm9P9s5vX/b+f1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo - 9f946PX/eej2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3Xo9f9z5/X/cuf1/3Hn9f9v5/X/bOb1/2rm - 9P9o5vT/ZeX0/2Pl9P9g5PT/XuT0/1vj8/9Y4/P/VeLz/1Li8/9P4fP/TeHy/0ng8v9G4PL/O8TV/yN6 - hf883vH/Od7x/1a8yP96kJL/MNzw/yzb8P8p2/D/ZIyR/3Nzc/9Mnab/CNXu/wDU7f8A1O3/Iqq6/11e - Xv9LbXH/AdPs/wDU7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/JMDZ/wCDof8Ag6H/AIOh/wCD - ocgAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBbcFQAXHAyAFVVAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh/ACDof8Ag6H/AIOh/wCDof8Vnrn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Vuf4/2Hl9P9l5fT/Z+X0/2nm - 9P9r5vX/bOb1/27n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/dej1/3To9f906PX/c+f1/3Pn - 9f9y5/X/cef1/2/n9f9u5/X/bOb1/2rm9P9o5vT/Z+X0/2Tl9P9i5fT/YOT0/13k9P9b4/P/WOPz/1Xi - 8/9S4vP/T+Hz/03h8v9K4PL/RuDy/0Pf8v8unqz/PNvu/zre8f833fH/SM/f/4GvtP8t3PD/Ktvw/yba - 8P9TvMj/j4+P/3eYnP8A1O3/ANTt/wDU7f8A1O3/Oam2/3Z2dv8nssL/ANTt/wDU7f8h3fT/T+n8/0/p - /P9P6fz/T+n8/y7S6P8Ag6H/AIOh/wCDof8Ag6HsAICiHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg599AIOh/wCDof8Ag6H/BYqn/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/TuP3/zm+1P8mqMH/QcLW/2fl9P9p5vT/aub0/2zm9f9s5vX/buf1/2/n - 9f9v5/X/cOf1/3Dn9f9w5/X/cOf1/3Dn9f9v5/X/b+f1/27n9f9s5vX/a+b1/2rm9P9o5vT/ZuX0/2Xl - 9P9i5fT/YOT0/17k9P9c5PP/WePz/1fj8/9U4vP/UuLz/0/h8/9M4fL/SeDy/0bg8v9D3/L/QNzu/zvT - 5f863vH/N93x/zTd8f821ej/W6Or/yvb8P8n2/D/JNrw/zfL3P+RkZH/jZmb/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/WKy2/3ebn/8B0+z/JN70/0/p/P9P6fz/T+n8/0/p/P832/D/AYSj/wCDof8Ag6H/AIOh/gCF - oUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEonYAg6H/AIOh/wCDof80x93/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tq/yCsxv8EiKb/AIOh/wCD - of8BhKL/W93u/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2vm9f9r5vX/bOb1/2zm9f9s5vX/a+b1/2rm - 9P9q5vT/aeb0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/17k9P9d5PT/W+Pz/1jj8/9W4/P/VOLz/1Hi - 8/9O4fP/TOHy/0ng8v9G4PL/Q9/y/0Hf8v8+3vH/O97x/zfd8f813fH/Mtzx/y7c8P89qrf/KNvw/yXa - 8P8h2u//F9br/21xcv9tenz/ANTt/wDU7f8A1O3/ANTt/wDU7f8G0en/fpuf/0jJ2f9P6fz/T+n8/0/p - /P9P6fz/PN/0/wOKqP8Ag6H/AIOh/wCDof8Ag6GCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhaEuAIOh+ACDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9L5Pf/L8DX/xCYs/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weKp/9c4vH/YOT0/2Ll9P9j5fT/ZOX0/2Xl - 9P9i4fD/Wtnq/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Ll9P9h5PT/YOT0/17k - 9P9c5PP/W+Pz/1nj8/9W4/P/VOLz/1Li8/9Q4fP/TeHy/0vh8v9I4PL/ReDy/0Pf8v9B3/L/Pt7x/zve - 8f833fH/Nd3x/zLc8f8v3PD/K9vw/yvL3v8m2vD/Itrv/xPX7v8B1O3/Rlxe/0JrcP8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wLU7f9Axtb/aJuh/0/p/P9P6fz/T+n8/zrg9P8Di6n/AIOh/wCDof8Ag6H/AIOipAD/ - /wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDodMAg6H/AIOh/wmPrP9K4vb/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1On/H6vF/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ - p/8MqMP/RNPm/1vj8/9c5PP/XuT0/1/k9P9g5PT/W9/x/wyPq/8Ag6H/NLfN/2Ll9P9i5fT/YuX0/2Ll - 9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k9P9b4/P/WuPz/1nj8/9W4/P/VOLz/1Pi8/9R4vP/T+Hz/0zh - 8v9K4PL/SODy/0Xg8v9C3/L/QN/y/z3e8f863vH/N93x/zXd8f8y3PH/L9zw/yzb8P8p2/D/Jtrw/yHa - 7/8L1u7/ANTt/wDU7f8jTVL/HHN+/wDU7f8A1O3/ANTt/wDU7f8G1e7/POT5/0/p/P9RipH/T9/w/0/p - /P843vT/Ao6r/wCDof8Ag6H/AIOh/wCDobkAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGNAIOh/wCDof8BhKL/O8/l/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9v8vv9f/D5ey/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCXs/8At9H/ANLr/wDU7f8F1e3/Mdzw/1jj8/9Z4/P/W+Pz/1vj - 8/9Dydz/AIOh/wCDof8SlbH/XuT0/17k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1fj - 8/9W4/P/VOLz/1Li8/9R4vP/T+Hz/03h8v9K4PL/SODy/0bg8v9D3/L/Qd/y/z/f8f883vH/Od7x/zfd - 8f813fH/Mtzx/y/c8P8s2/D/Kdvw/yXa8P8X2O//BdXt/wDU7f8A1O3/ANTt/wg6QP8Emqv/ANTt/wDU - 7f8A1O3/Etnw/0Xn+v9P6fz/T+n8/0rM2/9Fo6//MNnv/wKKqP8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGhQQCDof4Ag6H/AIOh/ySxyv9P6fz/T+n8/0/p - /P9P6fz/T+n8/z7U6f8fqsT/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqo/wWpxP8Ax+H/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Dtbu/zve8f9W4/P/V+Pz/z7H2/8Ag6H/AIOh/w6Vsf9U5/f/WOT0/1nj - 8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9Q4fP/TuHz/03h8v9K4PL/SeDy/0fg - 8v9F4PL/Qt/y/0Df8v8+3vH/O97x/zne8f823fH/NN3x/zHc8P8u3PD/K9vw/yjb8P8V2O7/A9Xt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AEZO/wDN5f8A1O3/AtTt/yjf9P8yw9v/IK3G/0LZ7f9P6fz/Tuj8/x2L - m/8AhaP/AIOh/wCDof8Ag6H/AIOiygCGoRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH - pREAg6HiAIOh/wCDof8OlbH/Tef5/0/p/P9P6fz/SuP2/y6+1v8PlrH/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/E5y3/zPF2/9N5/n/TOj7/yTe9P8B1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W - 7v823fH/O8fb/wCDof8Ag6H/DpWx/0/p/P9P6Pv/UuX4/1Pj9P9U4vP/VOLz/1Pi8/9S4vP/UeLz/0/h - 8/9P4fP/TeHy/0zh8v9K4PL/SODy/0fg8v9F4PL/Q9/y/0Hf8v8+3vH/PN7x/zre8f833fH/Nd3x/zLc - 8f8w3PD/Ldzw/yvb8P8b2e//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Afoz/ANTt/xPZ - 8P9C5vr/SeL1/wCDof8Ag6H/AIOh/0jn+v8VwNn/AGZ+/wCBnv8Ag6H/AIOh/wCEobwAgKoMAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhpQCDof8Ag6H/AoWk/0HX7P9P6fz/PtPo/x6q - xP8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xe50v9M6Pv/T+n8/0/p/P9P6fz/T+n8/z7l - +f8N1/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Gvtf/AIOh/wCDof8OlbH/T+n8/0/p - /P9P6fz/T+n7/0/l+P9P4fT/TuHz/03h8v9N4fL/S+Hy/0rg8v9J4PL/SODy/0bg8v9E3/L/Q9/y/0Hf - 8v8+3vH/Pd7x/zve8f853vH/Nt3x/zTd8f8y3PH/L9zw/y3c8P8h2u//Cdbu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/weyx/8y4fb/T+n8/0/p/P9D2u7/AIOh/wCDof8BhaL/B6fC/wCD - of8AgqD/AHWP/wCDof8Ag6GrAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - olgAg6H/AIOh/wCDof8ntc7/Lr3V/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4AgqLrAIOh/wCD - of8Ag6H/AIOh/w6nwf9C5fj/T+n8/0/p/P9P6fz/T+n8/07o/P8v4fb/CNfv/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wC91v8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o+/9M4/f/SeDy/0jg - 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Df8v8+3vH/PN7x/zve8f853vH/N93x/zXd8f8y3PH/MNzw/y7c - 8P8f2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v8q3/X/TOj7/0/p - /P9P6fz/T+n8/03n+v8BhaL/AIOh/wCDof8Ag6H/AIOh/wCDof8Af5z/AIKgjwCqqgMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wKFpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoekAgqGLAIahJgCqqgMAgqCJAIOh/gCDof8Ag6H/AIOh/wKNqv8q0un/Tuj8/0/p - /P9P6fz/T+n8/0/p/P9M6Pv/K+D1/wbV7v8A1O3/ANTt/wDU7f8A1O3/AL3W/wCDof8Ag6H/DpWx/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/SOP2/0Pf8v9B3/L/QN/y/z/f8f8+3vH/PN7x/zre - 8f853vH/N93x/zXd8f8y3PH/K9vw/x7Z7/8R1+7/BdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wfW7/8o3/T/Suf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCD - of8Ag6H/AIOh8wCCoFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD - oM0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDob0AhKFXAJKSBwAAAAAAAAAAAAAAAAAA - AAAAd5ZcAH+c/wCDof8Ag6H/AIOh/wCDof8QqsX/QeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+j7/y7g - 9f8O1+//ANTt/wDU7f8Avdb/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/R+f6/yfe8/8S0un/CanE/wqvyf8S1uz/Etfu/w7W7v8M1u7/BtXt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y7/8x4fb/Ten8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDodYAhp8oAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFsAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCC - oIkAgKMkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabn8AXnP/AHeS/wCDof8Ag6H/AIOh/wCD - of8ChqT/J8ff/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P895Pn/Hdzz/wK91/8Ag6H/AIOh/w6V - sf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKeuP8Ag6H/AIOh/wC5 - 0/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/B9bv/yPd - 9P8/5Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8jsMr/AIOh/wCD - of8Ag6HYAICqDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - ocMAg6H/AIOh/wCDof8Ag6H+AIOhuwCEolUAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgIACAFpw4ABab/8AW3D/GZiw/wOGpP8Ag6H/AIOh/wCDof8Ag6H/BpGu/yzP5f9M6Pv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Ncvg/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8uvtb/AIOh/wCDof8Ag6H/AL7Z/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8O2O//JN70/zvk+P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun8/03o - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDofwAg6H/AIOhmACDoCMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabkoAWm//AFpv/wJdcv9I3fD/RNrv/xeh - vP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmSr/8vz+X/TOj7/0/p/P9P6fz/T+n8/0/p/P84y+H/AIOh/wCD - of8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wOHpf8Ag6H/AIOh/wCd - uv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1u7/FNrw/yTe9P814vf/R+b6/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/z7T6f8ltM3/Qdrv/0Di9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD - of8Ag6H/AIOh4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAImdDQCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvrQBab/8AWm//Gomd/0/p/P9P6fz/T+n8/znM4v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCD - of8Fi6n/Ir/Z/0Lj9v9P6fz/T+n8/zjL4f8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07o+/8SmrX/AIOh/wCDof8EjKn/Jdju/yfe9f8s4PX/MOH2/zbi9/8+5fn/Sej7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/A4el/wCDof8Ag6H/Qdfs/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G3vH/AIOh/wCDof8Ag6H/AICqDAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVahgAWm/7AFpv/wBab/85wtX/T+n8/0/p - /P9P6fz/T+n8/03n+v8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xOivP8wz+X/Mcje/wCD - of8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KrnR/wCDof8Ag6H/AIOh/zfK - 4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9D5fn/KdXq/w2uyf8Ag6L/AIOh/wCDof8Jj6v/Rt3y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8Dh6X/AIOh/wCDof8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFlveABab/8AWm//CmyB/07o+/9P6fz/T+n8/0/p/P9P6fz/T+n8/znA1P8IaX7/AGqE/wB+ - mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0HX7P8ChaT/AIOh/wCDof8eqsT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/SOf6/zbf9P8gy+P/CqnF/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8OlbH/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAWm/dAFpv/wBab/8ppLj/T+n8/0/p - /P9P6fz/T+n8/0zj9v8gk6j/AFtw/wBab/8AWm//AFpv/wBrgtIAgp/MAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/r/D5ax/wCDof8Ag6H/Co+s/0vj - 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/PuP4/zPc8f8p0uj/GsHa/wqlwf8Aiaj/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbT/TOX4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/GqW//wCDof8Ag6H/AIOiewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtuQwBab/8AWm//AV1x/0fa7f9P6fz/T+n8/0/p/P89yNz/C26D/wBab/8AWm//AFpv/wBa - b/YAW3BwAAAAAQAAAAAAg58lAISifgCCodcAg6H/AIOh/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/ya0zf8Ag6H/AIOh/wCDof8Knbn/EKzF/xCuyP8Sr8j/EavG/wymwP8InLn/A5Sy/wCK - p/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HMAIShegCD - oYoAg6H/AIOh/wCDof8Yor3/Tuj7/0/p/P9P6fz/T+n8/0/p/P8ltM3/AIOh/wCDof8Ag6GiAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+oAFpv/wBab/8YhZr/T+n8/0/p - /P9N5fj/JZ2x/wFccf8AWm//AFpv/wBab/8AWm/LAFdwKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oTEAg6GrAIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+n/AYSi/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKg/wB6lv8AeZOgAICfIAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8eq8T/T+n8/0/p - /P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDocYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFlzFABab/kAWm//AFpv/ze/0v9P6fz/QM/i/w90if8AWm//AFpv/wBab/8AWm/7AFpwgABA - gAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/w6Vsf9P6fz/T+n8/0/p - /P9P6fz/TOb5/wySr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP8Xobz/I67H/wBke/8AXHH/AFpv/gBddBYAAAAAAAAAAAAA - AAAAAAAAAKqqAwCDoK8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/PdHn/wCDof8Ag6H/AIOh7QAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29zAFpv/wBab/8Jan//Teb5/yqm - uv8CXnP/AFpv/wBab/8AWm//AFpv1wBYcTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIWgSwCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCDof8Ag6H+AIKhxgCD - ocAAg6HMAISh2wCDoeUAg6HjAISh2wB+m+YAepX/AHiT/wZ+mP8ot8//M8Xc/z7T6P9I4PT/T+n8/0/p - /P8xssb/AFpv/wBab/8AWm/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqBgCDob0Ag6H/AIOh/wCD - of8uv9b/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAQBab9cAWm//AFpv/xyNof8Se4//AFpv/wBab/8AWm//AFpv/gBacJAAVXEJAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaBLAIOh/wCDof8OlbH/T+n8/0/p - /P9P6fz/O8/l/wCDof8Ag6H/AIOh/wCCooMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdtIwBa - b/4AWm//AFpv/0DN4f9P6fz/T+n8/0/p/P9P6fz/T+n8/x2Po/8AWm//AFpv/wBabpQAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDotIAg6H/AIOh/wCDof8zxdv/T+n8/0/p/P8Giqj/AIOh/wCD - of8AgqE5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXHE9AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab+EAWXBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCoj8Ag6H/AIOh/wyTsP9P6fz/T+n8/0vj9/8Jj6z/AIOh/wCDof8Ag6HQAICqBgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvwQBab/8AWm//Ipis/0/p/P9P6fz/T+n8/0/p - /P9P6fz/CWqA/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCE - odsAg6H/AIOh/wKFo/87z+X/T+n8/xKatf8Ag6H/AIOh/wCEol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABbb6MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXCgAFV3DwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/0/p - /P9P6fz/HqrE/wCDof8Ag6H/AIOh+ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnBgAFpv/wBab/8GZHr/Teb5/0/p/P9P6fz/T+n8/0TW6f8AWm//AFpv/wBab/4AWXMUAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICjJACDoeoAg6H/AIOh/wWJp/8/1On/HajC/wCD - of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWmkRAFpv/wBab/8AWm//AFpv/wBa - b/8AWm/pAFpwUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Jj6z/T+n8/zfK4P8Ag6H/AIOh/wCDof8Ag6F3AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVagwAWm/zAFpv/wBab/81us7/T+n8/0/p - /P9P6fz/MLLF/wBab/8AWm//AFpv1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKiNwCDofMAg6H/AIOh/weLqf8eqsP/AIOh/wCDof8Ag6GqAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABacGkAWm//AFpv/wBab/8AWm//AFlvsQBZbxcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wmP - rP9J4fX/B42q/wCDof8Ag6H/AIOhxQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABab58AWm//AFpv/xeFmP9P6fz/T+n8/0/p/P8djqP/AFpv/wBab/8AWm6UAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiQgCDofkAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDodEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/wBb - cGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/xqkv/8Ag6H/AIOh/wCDofQAgKMkAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AV1x/0fb - 7v9P6fz/T+n8/wlqgP8AWm//AFpv/wBbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISgWwCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh9QD//wEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAVW0VAFpupABab6UAWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOiawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAABAFpv3ABab/8AWm//KqW5/0/p/P9E1un/AFpv/wBab/8AWm/+AFlzFAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCD - of8Ag6H/AIOh/wCDof8Ag6H/AISeHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA//8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBa - b/8McIX/T+n8/zCyxf8AWm//AFpv/wBZb9MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACD - of8Ag6H/AIOh/wCDof8Ag6HvAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABebx4AWm/9AFpv/wBab/89yNz/HY6i/wBab/8AWm//AFpulAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAA//8BAIOhowCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Ag6H/AIOh/wCCoF4AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b7oAWm//AFpv/x+Sp/8JaoD/AFpv/wBab/8AW3BUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiygCDof8Ag6H/AIOgRgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - ojwAg6H/AIOh/wCDof8Ag6GtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvWgBab/8AWm//AFpv/wBab/8AWm//AFpv/gBZ - cxQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgKoGAIOhZwCDo1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOjJwCDof8Ag6H/AIOh/wCAohYAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVXEJAFpv8ABab/8AWm//AFpv/wBab/8AWm/SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIShcgCDoKcAhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+XAFpv/wBab/8AWm//AFpv/wBa - b5MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////4///4H///////////////+H - //+B///8f///////////g///gf//+H///////////4H//wH///B///////////+A//8B///gf/////// - ////wH/+AP//wH///////////8A//gD//4D////////////AH/AAA/8A////////////wA4AAAA+AP// - ///////8f8AAAAAAAAH//////////B/AAAAAAAAB//////////wP4AAAAAAAAf/////////+A+AAAAAA - AAH//////////gDAAAAAAAAB//////////8AAAAAAAAAAP//////////AAAAAAAAAAA//////////4AA - AAAAAAAAH//////5//+AAAAAAAAAAAf/////8H//wAAAAAAAAAAD//////Af/4AAAAAAAAAAAP/////4 - B/8AAAAAAAAAAAB/////+AH+AAAAAAAAAAAAP/////wA/AAAAAAAAAAAAB/////+ADgAAAAAAAAAAAAP - /////gAAAAAAAAAAAAAAB/////8AAAAAAAAAAAAAAAP/////AAAAAAAAAAAAAAAB/////4AAAAAAAAAA - AAAAAP////+AAAAAAAAAAAAAAAB/////wAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAB/////gAAAA - AAAAAAAAAAAP//n/8AAAAAAAAAAAAAAAB//wH/AAAAAAAAAAAAAAAAP/8AfwAAAAAAAAAAAAAAAD//AD - 8AAAAAAAAAAAAAAAAf/wAeAAAAAAAAAAAAAAAAD/8AHgAAAAAAAAAAAAAAAAf/AAwAAAAAAAAAAAAAAA - AD/wAMAAAAAAAAAAAAAAAAA/8ACAAAAAAAAAAAAAAAAAH/AAgAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAA - AAAAAAAP8AAAAAAAAAAAAAAAAAAAB/AAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAD4AAAAAAA - AAAAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAIAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAA - AAAAAAAAAAAAAQA4AAAAAAAAAAAAAAAAAAMAeAAAAAAAAAAAAAAAAAADAPwAAAAAAAAAAAAAAAAAAwH8 - AAAAAAAAAAAAAAAAAAcH/AAAAAAAAAAAAAAAAAAHD/wAAAAAAAAAAAAAAAAADx/wAAAAAAAAAAAAAAAA - AA9/4AAAAAAAAAAAAAAAAAAP/4AAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAB/8AAAAAAAAAAAA - AAAAAAAf8AAAAAAAAAAAAAAAAAAAH+AAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAA/gAAAAAAA - AAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///+AAAAAAAAAAAAAAAAAf/// - gAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAA - Af//+AAAAAAAAAAAAAAAAAP///AAAAAAAAAAAAAAAAAD///wAAAAAAAAAAAAAAAAB///4AAAAAAAAAAA - AAAAAAf//8AAAAAAAAAAAAAAAAAP//+APAAAAAAAAAAAAAAAH///g/4AAAAAAAAAAAAAAB//////AAAA - AAAAAAAAAAA//////4AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAH//////AAAAAAAAAAAAAAD///// - /gAAAAAAAAAAAAAB//////wAAAAAAAAAAAAAA//////8AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAP - //////AAAAAAAAAAAAAAH//////wAAAAAAAAAAAAAD//////4AGAAAAAAAAAAAD//////8AP4AAAAAAA - AAAB///////AP+AAAAAAAAAAA///////gf/AAAAAAAAAAAP//////8f/wAAAAAAAAAAD/////////4AA - AAAAAAAAA/////////+AAAAAAAAAAAP/////////gAAAAAAAAAAD/////////wAAAAAAAAAAA/////// - //8AB8AAAAAAgAH////////+AA/wAAAAA8AB/////////gAf+AAAAAfgAf////////4Af/gAAAAH8AH/ - ///////8AP/4AP8AB/gB/////////AP/+AH/AA/8Af////////gH//gD/4AP/gD////////4H//4B/+A - D/8A////////+D//+Af/gA//gP////////D///gP/8Af/8D////////5///4H//AH//g//////////// - +B//4B//4P////////////g//+Af//D////////////4f//gP//4////////////+H//8D////////// - //////j///A////////////////9///wP///////////////////+H///////ygAAAAwAAAAYAAAAAEA - IAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKHfAIOh/wCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAACXXSnAFpv/wBcb44AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAgp81AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6CMAIOh/wOHpMQAgJ8IAAAAAAAA - AAAAAAAAAAAAAABVcRIBXHH6G4uf/wRhdssAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAoajoACD - of8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYBoqo+QuT - sPcDh6S3AKqqAwAAAAAAgIACAIakKgBrhZYId475I6zF/wFshvsAgp9aAIOiQgCAphQAAAAAAAAAAACJ - nQ0Dh6S/AoWj/gCDof8AgJ0aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJcb3UAW2+BAAAAAQAA - AAAAgp8tAYSh/Ubd8f8Jj6z4AoWj0QCGpLcAhaL0AIOh/wCDof8AhKL/Aouo/wCHpf8AhKL+AIOh/wCD - of8AhaTgAIOhqQKGpOIPl7P4KrnR/wOIptwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb - cMgAWm//A15zyQBbbSoAjo4JAYSh/UHX7P9F3PD/Boyo/w2fu/4p0Ob/M9jw/yfA1/8cqsP/Qub5/z/k - +f875Pj/Md/0/yXU6v8Qssv/Aoik/xiivf9M5vn/GaS+/ACFoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABecRsAWm//DnOH/QFkefwAgJzmA4ek/zTG3f9M6Pr/RN7x/0Xc8P8kts7/BoSf/zSe - tf8Yi6T/FrzT/yHS6P8q1ur/Ndzv/0bk9/8Mk6//I7DK/03o+/9O6Pv/BIim/wCEou0AhKFXAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKgMwCD - oZAAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAADXnaiC4We/AGEov8UqcP/FaK9/ye1zv9B4/n/HtHo/wiW - sf8smrP/qd/w/8jw/v98xNb/BJSv/wDA1/8AwNf/AMDX/wDA1/8Kxt3/ItPo/0Di9/9B2O3/A4el/xu6 - 0/4AgqH/AISirACAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOiUgCDof8Ag6H/AISgbgCAqgYAAAAAAAAAAACImQ8Ag6DHAoem/yzS5/9P6fz/LMjf/w+8 - 1f8AxNv/BZiy/z6mvv/A7f7/tev+/6no/v+u5vX/Doai/wC50P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Syt//LtHm/0/p/P8+4PX/DZq3+wCFo+UAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOHpcMBhKP/CI6r9wKGo+gAgqJgAIiiHgCFpNcFkq76PeH2/03p - /P8i2/L/AMjg/wDF3P8AtMz/CIah/7Dm+f+v6v7/oub+/4nj/P+b6vz/SbHG/wCdt/8AxNv/AMTb/wDE - 2/8Aw9r/AMPa/wDD2v8Aw9r/AMPa/xPN5P9B4/b/TOj7/yC40v0AhaL2AISiTQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCny0BhKL8P9Tp/y6+1v8Giqj4Aoek+QWS - r/xA4/j/Sej7/xHV7f8AyeH/AMjg/wDI4P8Bn7r/RKrD/7js/v+g5f3/h+P8/23g+/9r4/r/edrs/wOF - oP8AxNv/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG3f8DyN//Ldrv/0/p/P8ryuD/AISi/ACC - oFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChKOgFJy3+E/p - /P9M5fj/KrnR/wWJp/8Xo73/DdTs/wDM5P8AzOT/AMvj/wDL4/8GmbT/YrvT/67q/f+E4/z/a+D6/1Le - +f862/j/d+j6/yeft/8AqsP/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/T - 6f9O5/v/Ls7k/wCDof0AhKFXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgJ8YAYWh+DjL4f9P6fz/T+n8/0ni9v8Nx9//AM7m/wDO5v8Azub/AM7m/wDO5v8En7n/UbPN/5no - /f9p4Pr/T935/zbb9/8n2vf/VuL5/17O4v8Bi6b/AMzk/wDN5f8AzeX/AM3l/wDN5f8AzOT/AMzk/wDM - 5P8AzOT/AMzk/yPT6P995fL/r/T9/4/j7/8nl7D8AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfSACD - of8Ag6KhAISjOgAAAAAAAAAAAoWijgqQrf9O5/v/T+n8/yPd8/8A0ur/ANLq/wDS6v8A0ur/ANHp/wDR - 6f8AsMn/CYej/3jg9v9r4/r/QN34/yfa9/8n2vf/Mtz3/33o+f8Qjqj/ALfQ/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8W0uj/VNbn/1HE1f8rqb//Ip63/1C6zf+R1+P/Sae99wCAoyQAAAAAAAAAAAAA - AAAAAAAAAIKhagCDof8AlLH7AIOh/QCDokoAAAAAAISiugOIpv8uvtb/QOX5/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Azef/BJSw/waHo/8yu9T/VuH3/07h+P8w3Pf/J9r3/2bl+f9GvdT/ApSv/wDS - 6/8A0uv/ANLr/wDS6v8A0ur/ENTp/zDF2v8lts3/AKjD/wC2z/8At8//AK3H/w6ZtP8Bgp//W7XI/iaW - r9gAkpIHAAAAAAAAAAAAAAAAAIKgVgCDof8A1O3/AKK9/gCFo9oAhKE2AIOh/x250v8Gi6n/BtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wO50/8Ci6f/DJGr/0HK4/9h5Pn/TuH4/2rl - +f985PX/AoCc/wDD3f8A1O3/ANTt/wTR6P8byuH/FcLb/wDO5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV - 7v82z+X/CpCt/zKdtf0AhaKWAAAAAAAAAAAAAAAAAIOiZQCIpfoA1O3/ALnU/wCDof8AhKGeCZq3+03p - /P8Nqsb/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/BtXt/wnW7v8M1u7/Dtbu/xDX7v8R1ez/Cq/J/wKF - oP8an7n/W9nu/2jh9f8orsf/AH+b/wO2z/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8P2PD/SOP2/xGduP8KiKT/AISiVQAAAAAAAAAAAIShmwCSsPkA0+z/ALHM/wCD - of8Ag6H/Kc/l/0fn+v8B1e3/ANTt/wDU7f8A1O3/BNXt/wnW7v8O1u7/Edfu/xXY7v8Y2O//Gtjv/xzZ - 7/8d2e//Htnv/xzS6P8MpL//AYCb/wKAnP8Im7X/EMvi/xDX7v8M1u7/B9Xt/wLU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Id30/03n+v8No77+AISi7wCAnxAAjqoJAIWi8ACu - yf8A0en/AMbf/wCfu/4Aj6z8R+b6/yrf9f8A1O3/ANTt/wPV7f8J1u7/Dtbu/xTX7v8Y2O//Hdnv/yHa - 7/8k2vD/Jtrw/ynb8P8p2/D/Ktvw/yrb8P8p2/D/Iczi/xvB2f8i2u//H9nv/xrY7/8V2O7/ENfu/wvW - 7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk+f853vP/AIqm+gCC - oYUAg6FnAIWj/ADL5P8A0en/ANPs/wDT7P8P1+//FZ65/wKLqP8Cob3/AqTA/waqxf8Krsj/DrLL/xO2 - z/8XvNT/Icvi/yzb8P8w3PD/Mtzx/zXd8f813fH/Nt3x/zbd8f813fH/Mtzx/zDc8P8t3PD/Kdvw/yXa - 8P8f2e//Gtjv/xTX7v8O1u7/B9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXZ - 8f9P6fz/DbjS/wCFo+oAhaLuAKbB/gDT7P8A0+z/ANLq/wDS6/8i3fT/GKO9/wCDof8ChaP/Ia7H/x2p - w/8ZpL//FJ66/xGatv8OlrL/C5Wx/zfd8f873vH/Pt7x/0Hf8v9B3/L/Qt/y/0Lf8v9B3/L/Pt7x/zze - 8f843fH/NN3x/y/c8P8p2/D/JNrw/x3Z7/8X2O//ENfu/wjV7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f9N6fz/IdPq/wCDof8Aiqf6ANDo/wDT7P8A0ur/ANHp/wDS6v8u3/X/Qeb6/wC+ - 2P8Diqj/GaS//0vj9/9P6fz/T+n8/0/p/P9P6fz/P9/y/0Pf8v9H4PL/SuDy/03h8v9O4fP/TuHz/07h - 8/9M4fL/SuDy/0fg8v9D3/L/P97x/0/i8/9c4/T/ZOX0/2Lk9P9X4/P/Qd/y/yDa7/8H1e3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9E5vr/Ktft/wCDof8Awdv/ANHp/wDN5v8AyOD/AMzk/wDP - 5/864/f/OOP4/wDU7f8OzeX/CJaz/wuRrv9B2O3/T+n8/0/p/P9M5/r/SeDy/07h8/9S4vP/VuPz/1nj - 8/9a4/P/WuPz/1nj8/9Y4/P/VeLz/1Li8/9W4/P/duj1/3Th9v9gyPT/Xrby/1qw8f9ZufL/Vczz/1ni - 9P883vH/Cdbt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9L6Pv/JM3k/wCEovcA0ur/ANLq/wC3 - 0v8Ag6H/AISi/gCHpf074ff/M+L3/wDU7f8T1+7/Jtru/xSpw/8Dh6X/MsTb/0/p/P9O5ff/U+Lz/1nj - 8/9e5PT/YeT0/2Tl9P9m5fT/ZuX0/2Xl9P9j5fT/YOT0/2Ll8/9/6vb/Z8vy/06d7f9tqeH/krTT/5m3 - 0P+Ksdb/WaPn/06j7P9K1fH/Od7x/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wnW7/9P6fz/GLPM/ACE - orIA0uv/ALvV/wCEov0Ag6CvAIKiWACDof832u//LtTp/wDN5/8U1+7/Ltzw/zfd8f8qwNb/A4il/yCs - xv9S4/P/XeT0/2Pl9P9p5vT/beb1/3Dn9f9y5/X/cuf1/3Hn9f9v5/X/a+b1/33p9v9lxen/SZfl/6S7 - zv/Dycn/wcjI/8LIyP/Fy8v/ys/Q/5C22f9Dl+T/Otfw/xrZ7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c - 8/9O6Pz/BYuo/QCEoFkAp8P/AISi/gCEoo8A//8BAAAAAACFo/AVpsL/AYWi/we91v8P1+7/Nd3x/z3e - 8f9G4PL/Q9Pm/w6Trv8xts3/ZuX0/23m9f9z5/X/eOj1/3zp9v9+6fb/fun2/33p9v966fb/fOn1/3ne - 9/80gL//qsDS/8vQ0P++xcX/vcTE/7m1rf+0oY7/vbeu/9jc3P+Br9v/OKTi/yTa7/8C1O3/ANTt/wDU - 7f8A1O3/ANTt/zji+P882u//AISj6wCAqgYAhaPxAIShXwAAAAAAAAAAAIGjRQKHo/oNlLH/P9Tq/zfj - 9/8H1e3/Od7x/0Pf8v9M4fL/VeLz/1zj8v9l4/L/b+f1/3bo9f996fb/g+r2/4fr9/+K6/f/iuv3/4nr - 9/+E6vb/hur2/1S27v86mt7/wcTE/8DHx//Ax8f/vLCj/6JlNv+IVzH/omU2/8Gwof/a4uj/MILX/xvQ - 7P8H1e3/ANTt/wDU7f8A1O3/B9bv/03o/P8htMz8AIKikQAAAAAAgJwSAAAAAACAnBIChaSlAYWj/Cm5 - 0f5N5/r/T+n8/0/p/P8X2/H/M93x/0jg8v9R4vP/W+Pz/2Tl9P9t5vX/duj1/37p9v+H6/f/juz3/5Pt - 9/+W7fj/lu34/5Pt9/+O7Pf/iev3/0Ko6v8+r/z/gpek/8TKyv/O09P/p3lU/0suGP8AAAD/QSgV/6Nw - SP/4+fn/O4fU/xG95f8C1O3/ANTt/wDU7f8A1O3/Id30/03o/P8Gi6j/AIOgRgAAAAAAAAAAAIKiWAKG - o+8RmbX4Q9nu/0/p/P9P6fz/T+n8/0/p/P9B4fX/INXt/0vh8v9U4vP/XuT0/2jm9P9x5/X/e+n2/4Tq - 9v+O7Pf/lu34/53u+P+i7/j/ou/4/53u+P+W7fj/juz3/0Go6v9Is/z/MlVt/3Z5ef/a3t7/sIZk/3RS - Of+DgoL/uKqf/7GIZ///////LX3M/wm65P8A1O3/ANTt/wDU7f8A1O3/PuX5/zfc8P8Ag6L8AIiZDwAA - AAAAgqJgAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYel/0nf8P9W4/P/YeT0/2vm - 9f916PX/f+n2/4nr9/+S7ff/nO74/6bw+f+t8fn/rfH5/6Tw+f+a7vj/kez3/1C38P9LpuP/PnWa/yw2 - Pf/Lzs7/5NzV/6lzSP/Xu6b/1Lmk/9zSyf/n7fH/Fm3D/wHO6v8A1O3/ANTt/wDU7f8L1/D/T+n8/xu5 - 0/4AhaLDAAAAAAAAAAAAgJkKAIWgSwCCoDMAgqAzAIKgMwCCoDMAhKHJC6XB/0Tb7/8PzOX/AMzm/yvb - 8P9X4/P/YeT0/2zm9f926PX/f+n2/4nr9/+T7ff/nO74/6bw+f+u8fn/rPH5/6Tw+f+a7vj/kOz3/3rg - 9v9auPn/Tomx/zJNXv8qLTD/4+Tk//b19P/j29T/6erp//39/f9imc3/EZTQ/wDU7f8A1O3/ANTt/wDU - 7f8q3/X/S+f7/wOJp/sAg6JjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8BaoTdAISi/zfh - 9v834/f/ANTt/wXV7f9O4fP/YOT0/2rm9P9z5/X/fen2/4fr9/+P7Pf/mO74/5/v+P+i7/j/oe/4/5zu - +P+V7fj/i+v3/4Lq9v9cve3/b7zw/0Zof/8xQUz/JSkr/6usrP/4+Pj/+vv8/3qn0v8PcL3/Cc/r/wDU - 7f8A1O3/ANTt/wfW7v9L6Pv/LdHn/wCFo/IAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgNf - dMYPdor4Dpe0/w2wzP9O6Pz/Ftrx/wC81v8EjKr/XuT0/2fl9P9w5/X/eOj1/4Hq9v+J6/f/j+z3/5Tt - 9/+X7fj/le34/5Lt9/+M7Pf/hOr2/33p9v905/X/Xrfk/1+Rs/9mk7L/KD1M/w8VG/8AAQH/AQUJ/wc2 - Vf8axuH/AdTt/wDU7f8A1O3/ANTt/y/h9v9K5/v/BpGv+QCEoXoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVYAGA11zyABab/8glKn+C3qR9wCEof8s2u//QNzx/wKKqP8tvdT/Q+T3/2Hl9P9q5vT/cuf1/3rp - 9v+A6vb/her2/4nr9/+L6/f/iev3/4fr9/+C6vb/fOn2/3Xo9f9t5vX/ZeX0/2DZ8f8+d5P/PG2M/y1T - a/8jUmv/ECsz/wkzOP8EOD//AM7m/wDU7f8A1O3/Fdnx/07o/P8ixt3+AIaj5ACAmQoAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBbcP0DX3WvAFlubQCBnq4Dk7D4FKC7/xagu/9P6fz/T+n8/0rm - +v9g5fX/aub0/3Hn9f926PX/e+n2/33p9v9/6fb/fen2/3vp9v936PX/cuf1/2zm9f9l5fT/XeT0/1Xi - 8/9Bvsz/Lpaj/0fBz/9BtMP/R4GI/yq0xP8jjJj/HoKO/wDU7f8L1/D/Sej7/zbc8f8Ag6H9AIOgRgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXMUAFhxNAAAAAAAAAAAAAAAAACApA4AhKLsBImm/0ff - 8/9P6fz/T+n8/0/p/P9N6Pv/StTm/1bV5/9s5vX/b+f1/3Hn9f9y5/X/cef1/2/n9f9s5vX/Z+X0/2Ll - 9P9b4/P/VOLz/03h8v9F4PL/OM3f/zvW6P9Dydn/RMnZ/1mzvv8A1O3/RKy4/xnN4/9E5vr/QeP4/wOO - q/kAhKKRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG - oSYBhKP5Ncfe/0/p/P9P6fz/S+T4/y/A1/8Nl7P/AYWk/zC6z/9h5PT/YuTy/0jJ2/9m5fT/ZeX0/2Tl - 9P9g5PT/XeT0/1jj8/9S4vP/TOHy/0Xg8v893vH/Nt3x/y3c8P8l0OX/D8bc/yiPm/8A1O3/Edfu/1TB - z/9B5Pn/CJez+gCFo8IAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIC/BAKIpM8cqMH7T+n8/z/U6v8eqsT/BIil/wqRrv8GsMz/AM3n/wbV7f8z3fH/Qszg/wWJ - p/9X5fX/WePz/1jj8/9V4vP/UuLz/03h8v9I4PL/Qt/y/zze8f813fH/LNvw/xbY7/8E1e3/ANDo/wGa - q/8j2PD/Ncrg/zW/0f8Fkq76AISj0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISigQSJp/squtL/DZSw9gGEovwChqP5Cpm1/kTk+P9M6Pv/Jd70/wPV - 7v8A1O3/BcLb/wSIp/9P6fz/Tuf6/03j9P9J4PL/RuDy/0Lf8v893vH/N93x/y/c8P8a2O//BdXt/wDU - 7f8A1O3/Fdnx/zzX6/85zeP/Aoek/wKIpfwAgZ7FAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCDof8Dh6ThAIOicwCAphQAgKEmAH2a/ASK - qP8sz+X/Tuj8/0fm+v8k3vT/BcPc/wOIp/9P6fz/T+n8/0/p/P9A5fn/Fr3V/xLK4v8Q1+7/Ctbu/wHU - 7f8A1O3/A9Xu/xrb8v8+5fn/T+n8/0nm+v9F3PD/AIOh/wCDopYAmZkFAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh+gCCoj8A//8BAAAAAAAA - AAAAW3BUBWR6+Ci3z/8DiKX/DZu3/zLU6v9M6Pv/O9Hn/wSJp/9P6fz/T+n8/0/p/P87z+X/AIOh/wi/ - 2f8L1/D/Ftrx/yPd9P814vf/TOj7/0fe8v8uyeD/R+H1/0/p/P9O6Pv/AYWi/QCCoDMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAADX3bBJZ2x/k/p/P9C1ur/DYmj/QCCoP8Gjar9GLDK/gSIp/9P6fz/T+n8/0vk - 9/8Kj6z/KbjP/0/p/P9P6fz/T+n8/0rn+/874fX/I8vj/wiat/0BhKL/N8rg/0/p/P9P6fz/CpCt9QCE - oFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbCEAW3D9RNXo/zvF2f8IaXz4AmB00ABogDYAg6BpAISj0gOH - pf9P6fz/T+n8/x2pw/8Gjqz/GLbQ/xWzzv8Ur8r/C566/wGJpv8Ag6H/AIKg/wCCoKwAgqKPAoWj+z7T - 6P9P6fz/F5+7+ACEon4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcb4UQd4z4I5mt/AFccfsCXHB/AECABAAA - AAAAAAAAAISiPAOHpfpP6fz/N8vh/wGEov0Ag6K3AISjtwCBnMIAdZH+I67H/zDC2f8pq8H/A2B13AAA - AAAAAAAAAoajegOHpfpB1+z/JbPM/wKGpKkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABab/8AWm//Al913QBc - cDIAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfpJ4vX/BYuo+ACEonAAAAAAAAAAAAAAAAADYHXfLq7C/0/p - /P8cjaL7AlxwlgAAAAAAAAAAAAAAAAKEopcGjKn4KLbO/wSIptQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhuOgBa - b/8CXnKWAGBgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfoao735A4ikxQD//wEAAAAAAAAAAAAA - AAAAW252D3SI9k/p/P8HaHz2AFtwVAAAAAAAAAAAAAAAAAD//wEDhqSpAIOh/wCDof8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAGZmBQBccSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8BhKP3AISlHwAA - AAAAAAAAAAAAAAAAAAAAXHAZAVxw/Ti+0/8AWm/+AFlzFAAAAAAAAAAAAAAAAAAAAAAAgKoGAYSj2gCD - of8Ahp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACD - of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA190uwZlef8DYHXcAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIiZDwCDoqEAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICfCACEoFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpwUgBab/8CXHGZAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAD//n8f7/8AAP/+Px/P/wAA//8eH4//AAD/9wAAD/8AAP/jAAAP/wAA//AAAA//AAD78AAAA/8AAPnw - AAAB/wAA+GAAAAD/AAD8AAAAAH8AAPwAAAAAPwAA/gAAAAAfAADOAAAAAA8AAMYAAAAABwAAwgAAAAAD - AADAAAAAAAMAAIAAAAAAAQAAgAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAACAAAAAABAAAYAAAAAAEAAHgAAAAAAQAA4AAAAAADAADAAAAAAAMAAIAAAAAAAwAA/AAAAAAH - AAD8AAAAAAcAAPgAAAAADwAA8AAAAAAPAADhAAAAAB8AAP+AAAAAHwAA/4AAAAA/AAD/AAAAAH8AAP4A - AAAA/wAA/jgAAAH/AAD++AAAA/8AAP/wAAAD/wAA//BgAAP/AAD/4fAA4f8AAP/j8eDh/wAA/+fx8fH/ - AAD///Px+f8AAP//9/H9/wAA////+f//AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDof8Giql2AAAAAAAA - AAAAAAAAAAAAAAVjeegGZXrcAAAAAAAAAAAAAAAAAAAAAAD//wEBhqS4AIKjPQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACY2qrwWJ - pvoGi6daAAAAAACOqgkAb4haFIif8Qhzie4AhJ46AIWmFwAAAAAAgKoGB4ypsgCDof8AhKUfAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXnJyA2F1swBV - gAYFiaZwLsDX/g2UsfYBiqfhAYqo8Amct/oTq8b/D6vF/gehvPEBj6ztAIin6geNqOstu9T5CI2r4AAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABc - bycAWm//B2qA4gCEoM8grsf+RN/y/irC2f8ru9P/JZu0/xaSrP8z2u7/O9/z/0Xl+P8Rnbj/N8vh/zvQ - 5f8GjKrnAIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhKLkAIKiNwAA - AAAAAAAAAAAAAAV1jdIIkq7/Ksri/xijv/8l1Ov/Ep23/3jG2//E7v3/W7bL/wCzzP8AwNf/AMDX/w7I - 3v8m1ev/L8Tb/yTD2v8KlbPxAIakcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASJ - psMBhKP+CI2qwwCFoiwAiad3DJ659UPk+P8p3PH/A8fe/waqwv9rwNf/ser+/5bk/f+Q3vD/CZu2/wDD - 2v8Aw9r/AMPZ/wDD2f8DxNv/ItPp/0bm+P8as8z2AIimqQCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICmFAqRrug+1On/Epu28gqUsPs+3PD/F9ft/wDK4v8AyeH/D5ax/6vl+f+T5P3/beD6/2zk - +f8qpL3/AMDY/wDI3/8AyN//AMfe/wDH3v8Ax97/Csvi/z/h9v8qyd/6AImmtQCqqgMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAABoupfyq60vhP6fz/O9Dn/xHA2f8Azeb/AM3m/wDN5v8QmLP/muX5/2ng - +v9D3Pj/L9v3/17P5P8CpcD/AMzk/wDM5P8AzOT/AMvj/wDL4/8Ay+P/E8/m/3Pp9/902ur8F5SvsAD/ - /wEAAAAAAAAAAAAAAAAAg6H/AIumwQCAoyQAi6ILB42q+0ri9v8x4fb/ANLr/wDS6/8A0ur/ANLq/wio - w/88s8v/XuH3/zXc+P8n2vf/YuP4/xWXsv8AzeX/ANDo/wDQ6P8A0Oj/CNHo/zLM4P87vdH/G5+4/zOm - vP90ydj7Lp+2lwAAAAAAAAAAAAAAAACHpPIAvNb/AImm3ACGpWkQp8L5ILHK/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANLr/wOtyP8LlrH/QMri/1Hh+f9W4vn/TsTa/wGyzf8A1O3/AdLr/xXK4f8YxN3/AMni/wDT - 7P8A0+z/Csvj/xunwf82pbz5AIOkRgAAAAAAgIACAIim8QDU7f8Ah6T1AIin3z/i9/8Lw93/ANTt/wDU - 7f8A1O3/BtXt/wzW7v8Q1+7/E9fu/xPR5/8KpL//G6G7/0jF3P8SlbH/BrHN/wLU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/yvA1/8Kj6ruAIaeFQCEojwAmLTtANLq/wCTsPUMnLj6P+X5/wDU - 7f8A1O3/CNXu/xDX7v8X2O//Hdnv/yHa7/8l2vD/Jtrw/yfb8P8gy+L/EKfB/xvQ5/8Y2O//Etfu/wrW - 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/x3B2/wAiqejAIqnwgC71v8A0ur/ANLr/yDP - 5/8Ag6H/B5az/wqduP8NoLz/EafB/xSrxP8q1er/M93x/zfd8f853vH/Od7x/zfd8f803fH/L9zw/ynb - 8P8h2u//GNjv/w7W7v8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u//QeX6/wCJp+4AlbHwANPs/wDS - 6v8A0uv/N+P4/w650/8LlrL/QNXr/0Tb7/9B1+z/Nszi/z3d7v9F4PL/SeDy/0vh8v9L4fL/SeDy/0Xg - 8v8/3/H/Qd/y/07h8v9M4fP/P9/y/x/a7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f9I5/v/AY6s7QDL - 5f8AzuX/AK3I/wC81v9A5fr/Ctbv/xHI3/8Ml7P/N8vh/0/p/P9K5Pf/T+Hz/1bj8/9b4/P/XeT0/13k - 9P9a4/P/VeLz/2rm9f9v2fX/Y7fu/1+w7f9ete3/Vc/y/znd8f8B1O3/ANTt/wDU7f8A1O3/AdXt/0jn - +v8AiaXvAM/n/wCVsvEAh6TCAIOh/zve8/8Mzuf/G9nv/zDX7P8VoLv/JbTM/1Tk9P9f5PT/Z+X0/2zm - 9f9v5/X/b+f1/2vm9f9y5/X/Z8jr/2io3/+0wsr/wcjI/73HzP93r93/R7np/yDa7/8A1O3/ANTt/wDU - 7f8R2PD/O9zx/wCKp58Aka3wAIimgQAAAAAAiaamD5i0/x2+1/8Z2O//PN7x/0rg8v8vuM//TM/i/27n - 9f936PX/fun2/4Lq9v+A6vb/fOn2/3TZ9P9EisL/y9HS/77Fxf+yo5X/oYBl/8rEvf9tq97/Jszs/wLU - 7f8A1O3/ANTt/yrf9f8lu9P2AIWfMACCoz0AjqoJB4ypkg+WsvRD2u7/Tun8/xfZ8P9E3/L/UuLz/2Dk - 9P9u5/X/e+n2/4br9v+P7Pf/k+33/5Lt9/+L6/f/Vbfq/0ag3/+5vr7/x8W//4pcOP8mFwz/mmxH/8ra - 5/8freD/A9Xt/wDU7f8B1O3/Reb5/wqUsOwAAAAAA4SiTQCDof8Unbj8NMbd/zXH3v81x97/JcPb/zzW - 6v9Y4/P/Z+X0/3Xo9f+E6vb/kez3/57v+P+m8Pn/oe/4/5Xt+P9UuOr/RJzY/0RRWf/b2dX/mm1J/8S9 - t/+7nIP/z93q/xOp3P8A1O3/ANTt/xTa8P873vL/AIqorAAAAAAChKJuA4immAaLp4AGi6eAA4mn8yW6 - 0v8HtND/H8/m/1rj8/9p5vT/eOj1/4fr9/+V7fj/pPD5/7Ly+v+n8Pn/mO74/3ba9v9Xr+n/N1Vo/3l+ - gP/r4Nf/zLWi/+zo5P9bm8//BMXm/wDU7f8A1O3/MuH2/x+80/YAg6BOAAAAAAAAAAAAAAAAAAAAAABc - cSQFepPxHMLZ/zDh9v8C0ur/QNPm/2fl9P926PX/g+r2/5Ds9/+b7vj/oO/4/5vu+P+Q7Pf/hOr2/1q2 - 5f9WgqD/OUxY/01UWf+Xmp3/YI61/xik1f8A1O3/ANTt/wvX8P9F5vn/BI2q5wCAgAIAAAAAAAAAAAAA - AAAAW20cAVxy+iGZrfwDi6j8PuP4/wyqxf8iuNH/W+X1/27n9f966fb/hOr2/4vr9/+O7Pf/i+v3/4Pq - 9v946PX/bOb1/1m73f9Og6T/JkRY/wsfK/8HKC//CIub/wDU7f8A1O3/OeP4/x+91vYAhaRiAAAAAAAA - AAAAAAAAAAAAAAFdccwDYHbfAl52bAB2lGYJmrb1GKK9/0/p/P9K6Pv/W+X2/23m9f916PX/eun2/3vp - 9v956Pb/c+f1/2vm9f9g5PT/VeLz/0HH1/87s8H/Qr7M/z+eqf8ol6T/CMPZ/yrf9f8z2u/+AIuqvwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQiNq/JI4PT/T+n8/z/U6v8bq8X/Ja/H/2Xl - 9P9Z2On/aub0/2fl9P9i5fT/W+Pz/1Pi8/9I4PL/Pt3w/zLa7/8i0OT/LaGu/wDT7P9FxdT/O9/0/wON - qegAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjKm4M8Xa+y291v0NlbH+GqfB/xTG - 3/8D1e3/KNvw/xObtf9T5ff/VOLz/1Li8/9L4fL/Q9/y/zve8f8t3PD/Etfu/wHU7f8JtMn/Kcbe/yrB - 1v8CiqjoAICeIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIWlggCDof8IjqrfBoundwCA - mpwQnbj+O9/0/zvk+P8V2fH/CJi0/0/p/P9K5/r/M9vx/xzL4v8b2e//D9fu/wLU7f8M2PD/L+H2/0nn - +/8outH+AIek4gCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChqNnAIOfSAAA - AAAAAAAACWp+sjG70f8PnLf/HLLL/jfc8f8Rm7b/T+n8/0/p/P8cq8T/FbvU/yLd9P8v4fb/Qeb6/zjX - 7f8ludL9T+n8/zfL4f8HjKmMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABVdw8Lb4LpRtns/xF6j+wEcIqvAIuosgmOq/1P6fz/N8vg/xGfuv4qz+X/J8jg/x26 - 0/8MmLX/AIej6geLquE1yN75Q9nu/wqQrboAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAABWJ5cBB3i/oIaH3hAFxyOgAAAAAAAAAACY+s3Eni9f8JjqvtAIOhdQB+ - m4AKe5TwOc3j/xeHm+sAW3EtAICZCgeMqNU4zOP9CI2q3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm//BmV6tABddAsAAAAAAAAAAAAAAAAJj6rcF6C77QOI - o0sAAAAAAAAAAAlpf8dF1+n/CWp/5wAAAAEAAAAAAIaeFQiNquEBhaP7AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVcRIAAAAAAAAAAAAAAAAAAAAAAAAAAACD - of8IjKupAAAAAAAAAAAAAAAAA19yUxuKnvUJa4DEAAAAAAAAAAAAAAAAAISeHQCDof8AiJkPAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAoOjoQCAqgwAAAAAAAAAAAAAAAAAVYAGAFpv/wRidnUAAAAAAAAAAAAAAAAAAAAAAJmZBQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/fPv//zzz//2AA//8AAP/vAAB/4wAAH/AAAA/4AAAHmAAAA4gA - AAOAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABwAAAAYAAAAGAAAAD8AAAA+AAAAfMAAAH/AAAD/gA - AB/xAAA//wAAP/8AAD//OIY//nnHP//557//++//KAAAABAAAAAgAAAAAQAgAAAAAAAABAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4yp0wuVqhgAcI8ZCnKI5QCAnwgA//8BCI2rtwCG - nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX3UjC2yAwRunw9khtc7vKLfQ+ia91f0nyeDxGarF8Ruo - wegAgJ8IAAAAAAAAAAAAAAAAAAAAAAeNqWsSmLSaAIOoIxmyy/IjyuD/K7bP/5/e8f9AutH/AMHY/w7J - 3f8gyeD/HLrU2gCMsCoAAAAAAAAAAAAAAAAAmZkFIa7H0ia40vgc0ej/AMvj/0++1f9r4Pr/Qcvj/wG/ - 2f8AyeH/AMnh/xbR5v9GzuHtFJuvMwAAAAAAlLCSAJu4qAyducYv0+v/ANTt/wDU7f8VttH/MMfg/03g - +P8ct9H/AdLr/wzM4/8cxt3/G7fP/z+2y/AAh6URAJu5pACuyegmyOHuBNLr/wTV7f8R1+7/Gtjv/xnK - 4v8hts7/ErrU/wfV7f8A1O3/ANTt/wDU7f8i1Oz/C6S8uwCtydwA0uv/F7XQ/xOmwf8mvtb/LMzh/z7e - 8f9C3/L/Pt7x/zbd8f803fH/G9nv/wDU7f8A1O3/Bdbu/yPG3uoAwNr1AJq21ibZ8f8cxd3/L8HZ/1Lj - 9P9h5PT/ZuX0/2Pl9P9wzev/fbvd/2+/4f8o0+7/ANTt/wfW7v8jw9vhAJWyexCatZ8sw9v/J9rw/0vY - 6f9p4vH/g+r2/4rr9/911vL/c6PF/6WYi/+NdWH/a77h/wHU7f8c2/L/EanHpwaKqaQapb/EJLfP+xrG - 3f9h5PT/f+n2/5zu+P+t8fn/e9rz/0Jzk/+mnpb/wbKm/2W42v8A1O3/KNXt+wCJqUEAAAAAEHiNYBGh - uvgbz+b/Ptbp/3jo9f+P7Pf/le34/4Tq9v9XoMP/O1Zo/zVVZv8IvNj/Edjw/x+/19cAAAAAAAAAAApq - gGoFdIs3HrHJ90nj+P81yN3/bOXz/3Hn9f9n5fT/VOLz/zzP4P9GxNP/J8XX/yfQ6PEAkbA6AAAAAAAA - AAAAAAAACpGsrB2pxNMWqcP3HtXs/xi40P9C5Pf/Ntrv/yfb8P8T2O//ItPq/x+40PIAmLZNAAAAAAAA - AAAAAAAAAAAAAACJoycAQIAEJJ6x1ROju+wbrsb2SeL1/x250f4n0+n/IsTa+Sa3z+4ks83FAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAABWZ8axJ6j7oAW20OFJ24jB+qw8wAhJw+Ip+13hqJn5oWmrdHD5ey4QAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVVQMAAAAAAAAAAAqProQPlLMyAAAAABJ7kbQMcodVAAAAAAiK - qGYAqqoDAAAAAAAAAAD7bwAA8A8AANAHAADAAwAAAAEAAAAAAAAAAAAAAAAAAIAAAAAAAQAAwAEAAOAD - AADABwAA8AcAAPSXAAD9vwAA - - - - - AAABAAUAAAAAAAEAIAAoIAQAVgAAAICAAAABACAAKAgBAH4gBAAwMAAAAQAgAKglAACmKAUAICAAAAEA - IACoEAAATk4FABAQAAABACAAaAQAAPZeBQAoAAAAAAEAAAACAAABACAAAAAAAAAABAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDovAAg6H/AIOh/wCDof8Ag6H/AIOhwQCA - mQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbiwAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1sIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AgqK0AICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+TAFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBablgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8IAICeIgCG - oRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JKAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoaYAqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYKAFpv7wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm+OAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJaAIOh5gCD - of8Ag6H5AIOijgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfIACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AISglwD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtwYgBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvxQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6F/AIOh/gCD - of8Ag6H/AIOh/wCDof8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - bskAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/YAQIAEAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6GgAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOhsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiygCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEongAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABY - bTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpuMwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACImQ8AhKG8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oaAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW2+YAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Q9Pn/wRhdv8AWm//AFpv/wBab/8AWm//AFpv/wBa - cGkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoh4Ag6HTAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6JzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJ2AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAgqJaAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVWoMAFpv8wBab/8AWm//AFpv/wBab/8AWm//FYGV/0/p/P8Uf5P/AFpv/wBab/8AWm//AFpv/wBa - b/8AWm+fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoTEAg6HlAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIWgSwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCE - ok0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtuaABab/8AWm//AFpv/wBab/8AWm//AFpv/za7z/9P6fz/JZyx/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv1wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoUkAg6HzAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoiEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H1AIOfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABab88AWm//AFpv/wBab/8AWm//AFpv/whoff9O5vr/T+n8/za8z/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/0AVXcPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoWcAg6H8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoZ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAA//8BAIOh9ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDovAAhKI0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFpv/wBab/8mn7T/T+n8/0/p/P9G2ez/AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpxRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCDoYgAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKFXAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDocsAg6H/AIOh/wCDof8Ag6H/AIOh/wKFo/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh6gCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+eAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Rtnr/0/p/P9P6fz/T+n8/wlq - f/8AWm//AFpv/wBab/8AWm//AFpv/wBab3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDoagAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AICcEgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KhAIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/JrPN/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAYHAQAFpv9QBab/8AWm//AFpv/wBab/8AWm//F4WY/0/p/P9P6fz/T+n8/0/p - /P8Zh5z/AFpv/wBab/8AWm//AFpv/wBab/8AWW+xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICcEgCDoMIAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhyAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdwCDof8Ag6H/AIOh/wCDof8Ag6H/C5Kv/0/p - /P8hrcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhaYXAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFlubQBab/8AWm//AFxx/wBedf8AYXf/AGR7/yeuxP81x97/M8bc/y/A - 1/8vwNf/GJev/wBqg/8AaoP/AGqD/wBqg/8AaIH/AGmA7wCDoUQAhKE2AIShGwCAvwQAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgIwCDodgAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoYIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoUwAg6H/AIOh/wCDof8Ag6H/AIOh/wGE - ov9N5vn/T+n8/xynwv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKEbAIKiPwCDomMAgqGFAIOioQB5lfQAfJj/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIOh5gCD - ocwAgqCyAISglwCEon4Ag6BhAISiNACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiNwCDoekAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x6qxP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqA7AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/QNbs/0/p/P9N5/r/GKK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDn0AAg6FvAIShnQCD - ocwAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIKh1wCDoakAg6B5AIWgSwCAnRoAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShUQCDofYAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/BImm/zrN5P8mtM3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAKqqAwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCDofQAg6H/AIOh/wCD - of8Ag6H/AIOh/zPF3P9P6fz/T+n8/0zl+P8Unbj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - obsAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKgKwCCoWQAhKGdAIOh1gCDof4Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh3ACD - oZoAgqJYAICiFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCDofwAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/CY+r/0HX7P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKirAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAF10FgBbcGIAWm5dAGBwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6HMAIOh/wCD - of8Ag6H/AIOh/wCDof8mtM3/T+n8/0/p/P9P6fz/SuP2/xGZtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKhrgCqqgMAAAAAAICkHACComAAg6KkAIOh5wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof4Ag6LSAIOhkACFoEsAkpIHAAAAAAAAAAAAqqoDAIOhkACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/D5ax/0fe8v9P6fz/SeH1/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - omUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFttRgBab/UAWm//AFpv/wBab+8AW3BrAICAAgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhogCD - of8Ag6H/AIOh/wCDof8Ag6H/GaO9/0/p/P9P6fz/T+n8/0/p/P9I4fT/DpSw/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HQAISh0ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACDoqEAhKFRAIKhsACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/FqC7/0vk9/9P6fz/T+n8/zPF3P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AgKIeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBab+gAWm//AFpv/wBab/8AWm//AFpv/wBab8sAXHAyAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - ongAg6H/AIOh/wCDof8Ag6H/AIOh/wySr/9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8v8Lka7/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/H6vF/07n+/9P6fz/T+n8/0/p/P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCD - of8AgqHXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXbSMAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+wBa - cJAAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJNAIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/Tef5/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wiO - q/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6L/AImn/wCOrP8Ak7D/AJi0/wCZtf8AmbX/AJ26/wCeuv8Anrr/AJ66/wCeuv8Anbn/AJm1/wCV - sv8Akq//AI2q/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0/p/P9P6fz/T+n8/0/p/P9P6fz/CI6r/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFlv5ABbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICeIgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/0HX7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9B1+z/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCLqf8Al7T/AKK+/wCuyf8At9H/AL/a/wTJ - 4v8H0uv/C9fv/w3X7/8Q2fD/FNrw/xXZ8f8V2fH/Fdnx/xHY8P8Q2fD/ENnw/xDZ8P8L1/D/B9bu/wLV - 7f8A1O3/ANTt/wDU7f8A1O3/AM7m/wDG3/8AvNf/ALXQ/wCsyP8AoL3/AJKv/wCEo/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ChaT/M8Xc/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab6gAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFtvtABdbCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACAgAIAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8zxtz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/z7T6f8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhaP/AJWy/wCkwP8Ft9H/EMri/xvZ7/8m3vX/MOH2/zfj+P8+5fn/Ruf6/0zo - +/9P6fz/T+n8/0/p/P9O6Pv/RNru/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/0fn+v9B5vr/OOL4/zDh9v8n3vX/Hdzz/xTZ8f8J1u//ANTt/wDU7f8A0uv/AMXf/wC2 - 0f8AqMP/AJm1/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Fiqf/O8/m/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yu60v8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDofgAjqoJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWG0xAFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/2AFlveABVgAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOgzQCDof8Ag6H/AIOh/wCDof8Ag6H/JrTN/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/O8/l/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ai6j/AaC9/w261P8d0Oj/K9/0/zvk+P9J6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/SN/z/zDA1v8aorv/Boei/wGAnP9E2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8/5fn/MuH2/yXe - 9f8W2vH/BtXu/wDU7f8A1Oz/AMPd/wCuyf8AmbX/AIak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8KkK3/Q9nt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Vnrn/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb7cAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/UAFlvPAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaMAg6H/AIOh/wCDof8Ag6H/AIOh/xmjvv9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83yuD/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSP - rP8n1+7/O+T4/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DV - 6/8pts3/EZaw/wGAnP8Af5v/AH+b/wB/m/8Af5v/Iq3F/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9E5vn/M+L3/yLd9P8Q2PD/AdXt/wDR6f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8RmLT/SOD0/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/AoWk/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AISgdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW0/AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/4AWm+cAF5rEwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Mk6//T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLE2/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8ChaP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I4PP/KrjP/wuO - qf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWGof9L5Pf/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fi9/8Pnrr/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Yor3/TOX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OMvh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b8YAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b+sAW25fAAAAAQAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh2ACDof8Ag6H/AIOh/wCDof8Ag6H/AYWi/03n - +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Lb3V/wCDof8Ag6H/AIOh/wCD - of8Ag6H/D5ay/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+j8/zvP5P8bo73/AoKe/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/LbvS/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8Pl7L/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ir8j/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKv - yf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofEAkrYHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWm5PAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab8EAV3ApAAAAAACEojQAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of9B1+z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puM//AIOh/wCD - of8Ag6H/AoWj/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N8ne/w6SrP8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wuOqf9O6Pv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl+P8ChaP/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wGEov8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8Mk6//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAQBab9UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AGB1+gB6l9UAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/NMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7U - 6f8ltM3/J7XO/0Ta7v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P87zuT/Epew/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/D4eh/xOJov8Af5v/AH+b/wB/m/8Af5v/NcTa/03k - 9/9N5Pf/TeT3/07l+P9O5vn/Tuf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8yxNv/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wOGpP82yN//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9F3fH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOivwCCoTEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm9eAFpv/wBab/8AWm//AFpv/wBab/8AWm//IJSo/x2PpP8AW3D/AFpv/wBa - b/8AWm//AFpv/wBab/8AXHH/AG6I/wCBn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/ya0zv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8f8Xnrf/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wKAnP8kkan/Tqa6/2ezw/9Mpbn/AH+b/wB/m/8Af5v/AH+b/wGQ - qv8Fwtj/BcLY/wXC2P8Jwtn/DcTZ/xHF2f8Wxdr/Gsjd/x7K3/8nzuP/L9Po/znY7P9B3fD/S+L0/03k - 9/9P6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KLfP/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/weLqf8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/L8DX/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIShnQCF - phcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAECABABab+EAWm//AFpv/wBab/8AWm//AFpv/wVjeP9L4vX/O8XZ/wxx - hv8AWm//AFpv/wBab/8AZ3//AH+b/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ZpL7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLB2P8FhqH/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/CoSf/zSZr/94v9D/oNbl/7zp9/+Y0uH/Z7PD/w6GoP8Af5v/AH+b/wB/ - m/8Af5v/ALPM/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8Iwtn/FMXa/yDM4f8s0uf/ONns/0fh8/9O5/r/T+n8/0/p/P9P6fz/T+n8/znM4/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuSr/9E2+//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xmkvv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HtAIShXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW5tAFpv/wBab/8AWm//AFpv/wBab/8AWm//K6e7/0/p - /P9N5vn/KaS3/wNieP8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/DJOw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SN/z/xefuP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8MhaD/SKS5/5HO3v+55/X/yPD+/8jw/v/I8P7/wOv6/2u1xv83m7H/AH+b/wB/ - m/8Af5v/AH+b/wCYsv8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/CsPZ/xrJ3v8p0+f/O9vv/0vk9/9P6fz/IK3G/wCD - of8Ag6H/AIOh/xWeuf9J4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07n+/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhKG4AICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFVxCQBab+sAWm//AFpv/wBab/8AWm//AFpv/who - ff9N5vn/T+n8/zDC2f8EiKb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGFov9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/854/j/G8Xc/wGHov8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wiDnv9Pqr//pNrp/8Xu/f/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v+Ny9v/X6/A/wKA - nP8Af5v/AH+b/wB/m/8Agp7/ALvS/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8CwNf/Esfb/ybS - 5v8uyeD/Lr7W/zvQ5f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P880ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAg6FtAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//K6zA/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCF - o/8Prcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Qdfs/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9K5/v/M+L3/xrZ8P8EzeX/AKzF/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/zqetv+g2Of/x/D+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/t+X0/2ez - w/8kkan/AH+b/wB/m/8Af5v/AH+b/wChuf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wTB2P8Vy+D/Ldbr/0bk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/J7XO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wCIpv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq - qgMAgqCBAIOh5gCDoewAg6CnAIakKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBwEABab/MAWm//AFpv/wBa - b/8AWm//AGd//wGCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wql - wf823/T/Tuj7/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/zTH3f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/zPi9/8W2vH/AtDn/wDH3v8Av9f/AKC6/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/GY2n/4/P4f/F7/3/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw - /v9/wtL/Taa5/wB/m/8Af5v/AH+b/wB/m/8AhqH/AL7W/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/EMje/yjV6v9B4/b/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xijvv8s3fL/B6rG/wCE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh8QCEol0AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqKDAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6KhAIOiIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBa - b/8AXHL/AHSO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5Ku/yfT - 6v9M6Pv/T+n8/0/p/P8PlrL/AIOh/wCDof8Ag6H/AIOh/wCDof8ntc7/T+n8/0/p/P9P6fz/T+n8/0/p - /P895Pn/INzz/wXV7f8Ay+P/AMPZ/wDA1/8AwNf/AJq0/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/S6vB/7vp+P/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw - /v/I8P7/qt3s/2ezw/8Qh6H/AH+b/wB/m/8Af5v/AH+b/wCowf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/w7J3v8q2Oz/R+b5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8vwNf/T+n8/0vo - +/8n1+7/BJi1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOipACA - pA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOh5ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKGTAIWjGQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhsGgBa - b/kAYXf/AHyZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaT/GL/Y/0Tm - +f9P6fz/T+n8/0/p/P9P6fz/HKfB/wCDof8Ag6H/AIOh/wCDof8Ag6H/GqS//0/p/P9P6fz/TOj7/y7g - 9f8M2PD/ANLr/wDJ4f8Awdj/AMDX/wDA1/8AwNf/AKK7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8IhKD/hczg/8bw/v/G8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw - /v/I8P7/yPD+/8Xv/f90u8z/OZyx/wB/m/8Af5v/AH+b/wB/m/8AjKb/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHD - 2f8VzuT/Mt7z/03p+/9P6fz/T+n8/0/p/P9P6fz/DZSw/wCDof8Ag6H/AIOh/wCDof8ChaT/Rt3y/0/p - /P9P6fz/T+n8/0jn+v8hyOD/Aoim/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKLdAIKiNwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAP//AQCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgqGHAICfEAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAbYa/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Gm7j/Nd3z/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKvyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xGZtf9G5vr/Jd70/wXW - 7v8A0uv/AMnh/wDC2f8Awdj/AMHY/wDB2P8Awdj/AKnC/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Ui6b/otzu/8Xv/v/F8P7/xvD+/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8Hu/v+u6f7/oub+/8Du - /v/I8P7/yPD+/8Xw/v/D8P7/mtXl/1+vwP8DgZz/AH+b/wB/m/8Af5v/AH+b/wCwyP8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8Gxdz/JNfs/0Xm+v9P6fz/T+n8/zPF2/8Ag6H/AIOh/wCDof8Ag6H/KbjQ/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8XsMn/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDofsAg6F1AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6GpAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oe8Ag6B5AIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq - qgMAg6GQAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCGpP8Xwtv/SOf6/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puND/AIOh/wCDof8Ag6H/AIOh/wCDof8Lor7/AtTt/wDT - 7P8AyuL/AMPZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/ALXN/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8gk63/reP1/8Pv/v/E7/7/xe/+/8bw/v/G8P7/x/D+/8jw/v/I8P7/xO/+/7Dq/v+i5v7/oub+/6Lm - /v+x6v7/yPD+/8Xw/v/C8P7/wO/+/7ns+v9stsf/JZKq/wB/m/8Af5v/AH+b/wB/m/8Ala//AMHY/wDB - 2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Bw9n/F9Hp/zni9/9P6fz/N8rg/xihvP8Vn7n/Lr7W/07n - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfZ7v8KlLH/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoagAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIafKACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoecAg6FtAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA - nxAAhKG4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSZtf8w3fL/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/w+Xsv8Ag6H/AIOh/wCDof8Ag6H/ALrW/wDM - 5P8Aw9n/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMDX/wCKpf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8dkKv/suX3/8Lv/v/D7/7/xO/+/8Tv/v/F7/7/xvD+/8bw/v/H8P7/uOz+/6Tn/v+i5v7/oub+/6Lm - /v+i5v7/pOb+/8Lv/v/C8P7/v+/+/7zv/f+57v3/i87e/0+nuv8Af5v/AH+b/wB/m/8Af5v/AICc/wC5 - 0f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Awtn/Ds3j/zTg9f9O6Pz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/ye/2P8ChqT/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QCDoCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKGTAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEod8AhKFfAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - nyUAg6HYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/w+20P9E5vn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3vL/D6S//wCJp/8Aj63/ALTO/wDF - 3P8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wCdtv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8QiaT/qOL0/8Hu/v/B7v7/wu/+/8Pv/v/E7/7/xe/+/8Xw/v/C7/7/q+j+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oub+/53l/f+x6/3/v+/+/7zv/f+57v3/tu79/6nm9f9os8T/EYii/wB/m/8Af5v/AH+b/wB/ - m/8Anbf/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awtn/Cs3l/zHf - 9P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qt/z/xCZ - tP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HuAIOfSAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICmFACDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoNUAg6JSAP//AQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - ojcAg6HqAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIuo/x3P5/9M6Pv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P864/j/Ddfw/wDU7f8A0uv/AMnh/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wC40P8AgJz/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Gg5//mNjs/8Du/v/A7v7/we7+/8Lv/v/C7/7/w+/+/8Tv/v+97f7/pef+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oeX9/5zl/f+X5f3/m+b9/7zv/f+57v3/tu79/7Pu/f+x7v3/fcXV/zudsv8Af5v/AH+b/wB/ - m/8Af5v/AIOf/wC/1v8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awtn/DM3l/zfh9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9O6Pv/Kb3V/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAhKJ4AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6F1AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDosoAg6FEAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oUwAg6H0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Apq2/y/d8/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bn+v8a2/L/ANTt/wDU7f8Azub/AMXc/wDE - 2/8AxNv/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8AlbD/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/bcDY/77u/v+/7v7/wO7+/8Hu/v/B7v7/wu/+/8Pv/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oeX9/5zl/f+X5P3/k+T9/47k/P+u7P3/tu79/7Pu/f+w7v3/re39/5rf7v9hsMH/BIGd/wB/ - m/8Af5v/AH+b/wB/m/8Apr7/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Aw9n/EdDm/zzj+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9B3PH/D5ey/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oZoAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDod4Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ob0AhJ84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oGEAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/B7DK/z7l+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDS6/8AyeH/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8Ats//AICb/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/MZ64/7rs/v++7f7/vu7+/7/u/v/A7v7/we7+/8Hu/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43k/P+J4/z/l+f8/7Pu/f+w7v3/re39/6rt/f+n7fz/c77O/yeT - qv8Af5v/AH+b/wB/m/8Af5v/AImk/wDC2f8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8AxNv/GtTq/0fm+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o+/8isMr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIShuACJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqBWAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDobEAhaIsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oWoAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhqT/DMHa/0Xn+v9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOf6/xja8v8A1O3/ANTt/wDQ6P8Ax97/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AJOu/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/CoWh/6Lg9v+87f7/ve3+/77t/v+/7v7/wO7+/8Du/v+87f7/pOb+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43j/P+I4/z/g+P8/4Di/P+t7f3/re39/6rt/f+n7fz/pO38/4zX - 5/9Qp7r/AH+b/wB/m/8Af5v/AH+b/wB/m/8Arsb/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Ex97/Ktvw/03p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbL4f8Fiqf/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HQAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD - ocUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ru9L/Iq/J/wKFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCDoaMAgJ4iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - onMAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/Eszk/0no+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/NeL3/wbV7v8A1O3/ANTt/wDM5P8Axt3/AMbd/wDG - 3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/ALnR/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/1i3z/+67P7/vO3+/73t/v+97f7/vu7+/7/u/v++7v7/puf+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/n+X9/5rl/f+V5P3/keT9/4zj/P+I4/z/g+P8/37i+/954vv/lej8/6rt/f+n7fz/pO38/6Hs - /P+d6/v/bLnJ/xOJov8Af5v/AH+b/wB/m/8Af5v/AJOt/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8MzOT/O+L2/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rd7y/xGZtf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeMAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhJ84AIOh/gCDof8Ag6H/AIOh/wCDof8Ag6H/CY+r/0zl+P9G3fH/HqvE/wGFov8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDopYAhaMZAAAAAAAAAAAAAAAAAAAAAACD - oXUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak6//GNXt/0zo+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6fz/Id3z/wDU7f8A1O3/ANLr/wDJ4f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wCguf8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wyHov+o5fv/uu3+/7zt/v+87f7/ve3+/77t/v++7v7/run+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/n+X9/5rl/f+V5P3/kOT9/4zj/P+H4/z/guL8/33i+/954vv/dOH7/3rj+/+n7fz/pO38/6Hs - /P+e7Pz/m+v7/4HQ4P89nrP/AH+b/wB/m/8Af5v/AH+b/wCAm/8AutL/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDE - 2/8g1er/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/H6vF/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACEojwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoagAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8puM//T+n8/0/p/P9D2e3/GqW//wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9gCCoIkAgJwSAAAAAACD - oW0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/INrx/07o/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/Edjw/wDU7f8A1O3/ANHp/wDI4P8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDG3f8Ah6L/AH+b/wB/ - m/8Af5v/AH+b/wB/m/9Eq8X/tuv+/7rs/v+77f7/vO3+/7zt/v+97f7/uez+/6Lm/v+i5v7/oub+/6Lm - /v+i5v7/nuX9/5nl/f+V5P3/kOT8/4vj/P+H4/z/guL8/33i+/944fv/dOH7/2/h+/9q4Pr/ler8/6Hs - /P+e7Pz/m+v7/5jr+/+R5ff/YrHC/wWCnf8Af5v/AH+b/wB/m/8Af5v/AJu1/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wnK4P863/X/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8tvdX/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIOgRgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgJ8gAIOh9wCDof8Ag6H/AIOh/wCDof8Ag6H/BIil/0jf8/9P6fz/T+n8/0/p - /P8/1Or/FqC6/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wCE - oa4Ag6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AoLz/Jd3z/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P875Pj/B9bu/wDU7f8A1O3/AM/n/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Atc7/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/h9Pr/7ns/v+57P7/uu3+/7vt/v+87f7/ve3+/6vp/v+i5v7/oub+/6Lm - /v+i5v7/nuX9/5nl/f+U5P3/j+T8/4vj/P+G4/z/geL8/33i+/944fv/c+H7/2/h+/9q4Pr/ZeD6/3fk - +/+d7Pz/m+v7/5jr+/+V6/v/k+v7/3bH2P8plKv/AH+b/wB/m/8Af5v/AH+b/wCCnv8AwNj/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AcTb/yHU6f9N6Pr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/znN4/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKFRAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoIkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8fqsT/T+n8/0/p - /P9P6fz/T+n8/0/p/P880Ob/Epu2/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ApsH/Jd70/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u4PX/AdXt/wDU7f8A1O3/AM3l/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AKW+/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/GY+r/63p/v+47P7/uez+/7rs/v+67f7/vO3+/7rt/v+j5v7/oub+/6Lm - /v+i5v7/neX9/5jl/f+U5P3/j+T8/4rj/P+G4/z/geL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/2Df - +v9d3/n/lur7/5jr+/+V6/v/kuv7/4/q+/+F3/D/Uqi7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AKS9/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/Dcrg/0Lh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Qtru/wqQrf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACE - ol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQOAIOh6gCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0HX - 7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83y+H/D5ax/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqMT/Jd71/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A0+z/AMzk/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCU - rv8Af5v/AH+b/wB/m/8Af5v/AH+b/0Oqxf+y6v7/uOz+/7js/v+57P7/uuz+/7vt/v+y6v7/oub+/6Lm - /v+i5v7/neX9/5jl/f+T5P3/juT8/4rj/P+F4/z/gOL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/1/f - +v9a3/n/Vt75/3jl+v+U6/v/kuv7/4/q+/+M6vv/iur7/2/A0f8ViqP/AH+b/wB/m/8Af5v/AH+b/wCH - ov8Axd3/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Ex97/Mdnt/0/o+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9H4PT/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWoAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Vnrn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8zxtz/C5Ku/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqsX/Jt71/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8d3PL/ANTt/wDU7f8A0+z/AMzk/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AhJ//AH+b/wB/m/8Af5v/AH+b/wB/m/9rw9z/tuv+/7fs/v+47P7/uez+/7ns/v+67P7/q+j+/6Lm - /v+h5f3/nOX9/5fl/f+T5P3/juT8/4nj/P+F4/z/gOL8/3vi+/924fv/ceH7/23g+/9o4Pr/ZN/6/1/f - +v9a3/n/Vt75/1He+f9X3/n/kev7/47q+/+M6vv/ier7/4bq+v972On/Pp6z/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AK3H/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8c0eb/S+T3/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9/8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAIOh1gCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/zjM4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8vwNf/CY+r/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CJWy/znN4/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8V2fH/ANTt/wDU7f8A0+z/AMvj/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AxNz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/lNzz/7br/v+37P7/uOz+/7js/v+57P7/uuz+/6Xn - /v+h5f3/nOX9/5fk/f+S5P3/jeT8/4nj/P+E4/z/f+L8/3vi+/924fv/ceH7/23g+v9o4Pr/Y9/6/17f - +v9Z3vn/Vd75/1De+f9L3fn/R934/3vn+v+L6vv/ier7/4bq+v+D6fr/gOj6/2a4yf8Ggp3/AH+b/wB/ - m/8Af5v/AH+b/wCPqv8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/w7K - 4f9E3/L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/xegu/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDoWcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Nk7D/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zm - +f8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/FJ23/z3S - 6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fn+v8P2O//ANTt/wDU7f8A0uv/AMvj/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/ALzU/wB/m/8Af5v/AH+b/wB/m/8Af5v/CIWg/6fn/v+16/7/tuv+/7fs/v+47P7/uOz+/7js - /v+g5f3/m+X9/5bk/f+S5P3/jeP8/4jj/P+E4/z/f+L8/3ri+/914fv/cOH7/2zg+v9n4Pr/Y9/6/17f - +v9Z3vn/VN75/1De+f9L3fn/Rt34/0Hc+P9Y4fn/iOr7/4Xq+v+D6fr/gOn6/37p+v900uL/KpSr/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/ALjR/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//CMnh/zvb7v9O5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/GaO+/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOiYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0vj9/8nts//BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/GKG8/0Xc8P9P6fz/T+n8/0fn+v8M2PD/ANTt/wDU7f8A0uv/AMzk/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wC0zf8Af5v/AH+b/wB/m/8Af5v/AH+b/xuRrP+n5/7/tev+/7br/v+26/7/t+z+/7js - /v+z6v3/m+X9/5bk/f+R5P3/jOP8/4jj/P+D4/z/fuL8/3ri+/914fv/cOH7/2zg+v9n4Pr/Yt/6/13f - +v9Y3vn/VN75/0/d+f9L3fn/Rt34/0Hc+P883Pj/Otv3/3/o+v+C6fr/f+n6/37p+v9+6fr/feX3/1Ws - v/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCYs/8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8CyOD/MNbp/07l+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o - +v8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4Ag6BhAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF - oS4Ag6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Hi6n/S+P3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nh9f8jsMr/Aoak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Jjqz/Rdzw/0fn+v8M2PD/ANTt/wDU7f8A0+z/AMzk/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8As83/AH+b/wB/m/8Af5v/AH+b/wB/m/8wnrj/qOj+/7Tr/v+16/7/tuv+/7fs - /v+16/3/q+n9/5Xk/f+R5P3/jOP8/4fj/P+D4/z/fuL7/3ni+/904fv/cOH7/2vg+v9m4Pr/Yt/6/13f - +v9Y3vn/U975/0/d+f9K3fn/Rd34/0Dc+P883Pj/N9v3/zPb9/9b4vn/f+n6/37p+v9+6fr/fun6/37p - +v9xytv/F4uk/wB/m/8Af5v/AH+b/wB/m/8AgJ3/AMDZ/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8l0uj/TeP1/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Tef6/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACEoFsAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIShnQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8fq8X/AYWi/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xilv/8M1/D/ANTt/wDU7f8A0+z/AM3l/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/ALDK/wB/m/8Af5v/AH+b/wB/m/8Af5v/OaO+/6jo/v+06/7/tev+/7Xr - /v+z6/3/sev9/6fp/f+Q5P3/i+P8/4fj/P+C4vz/feL7/3ni+/904fv/b+H7/2rg+v9m4Pr/Yd/6/1zf - +v9Y3vn/U975/07d+f9K3fj/Rdz4/0Dc+P873Pj/Ntv3/zLb9/8t2vf/NNz3/37p+v9+6fr/fun6/37p - +v9+6fr/e+Hy/0Cgtf8Af5v/AH+b/wB/m/8Af5v/AH+b/wChu/8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/R5v9L4PL/T+j7/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9M5vr/FJ23/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIOgTgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACFoxkAg6HzAIOh/wCDof8Ag6H/AIOh/wCDof8ChqT/Rd3x/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pa7v8bpcD/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ckq//ANTt/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wCxy/8Af5v/AH+b/wB/m/8Af5v/AH+b/zmjvv+o6P7/s+v+/7Tr - /v+x6v3/r+v9/6zq/f+k6f3/i+P8/4bj/P+C4vz/feL7/3jh+/9z4fv/b+H7/2rg+v9l4Pr/Yd/6/1zf - +v9X3vn/Ut75/07d+f9J3fj/RNz4/z/c+P872/j/Ntv3/zLb9/8t2vf/KNr3/yfa9/9n5fn/fun6/37p - +v9+6fr/fun6/37p+v9qwtT/BoKd/wB/m/8Af5v/AH+b/wB/m/8AhJ//AMff/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/GtDm/0ne8P9O5vn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rl+P8RmbT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofUAhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISifgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xumwP9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DW - 6/8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKK+/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8Assv/AH+b/wB/m/8Af5v/AH+b/wB/m/8rmrb/p+f+/7Pr - /v+v6v3/rer9/6rq/f+n6v3/oOj9/4bj/P+B4vz/feL7/3jh+/9z4fv/buD7/2ng+v9l4Pr/YN/6/1vf - +v9X3vn/Ut75/03d+f9J3fj/RNz4/z/c+P862/j/Ndv3/zHb9/8s2vf/J9r3/yfa9/8n2vf/RN/4/37p - +v9+6fr/fun6/37p+v9+6fr/eNvt/yyVrP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCsxf8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8V0Ob/SN3v/03k - 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOP3/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zzR5/8NlLD/AIOh/wCDof8Ag6H/AI2q/wDM5v8A1O3/AM/n/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8AzeX/AM3l/wDN5f8AzeX/ALbP/wB/m/8Af5v/AH+b/wB/m/8Af5v/HZGt/6bn - /v+u6v3/q+r9/6np/f+l6f3/oun9/5/p/f+A4vz/fOL7/3fh+/9y4fv/buD7/2ng+v9k4Pr/YN/6/1vf - +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z/c+P862/j/Ndv3/zHa9/8s2vf/J9r3/yfa9/8n2vf/J9r3/yna - 9/925/r/fun6/37p+v9+6fr/fun6/37p+v9btsj/AH+b/wB/m/8Af5v/AH+b/wB/m/8AjKf/AMvk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xLP - 5f9I3e//TeP1/0/p/P9Q6fz/YOv8/3Ht/f967v3/eu79/3Ht/f9Y4vP/Bomn/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HpAICjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiYACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKb - tv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zjM4/8Lscz/ALTP/wDR6v8A1O3/ANDo/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wC81v8Af5v/AH+b/wB/m/8Af5v/AH+b/w2H - o/+e5v3/qen9/6bp/f+j6f3/oOj9/53p/f+b6P3/gOP7/3fh+/9y4fv/beD7/2jg+v9k4Pr/X9/6/1rf - +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/VeL5/37p+v9+6fr/fun6/37p+v9+6fr/ddXl/xiLpP8Af5v/AH+b/wB/m/8Af5v/AH+b/wC3 - 0P8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8By+P/ONfr/47q9v+28/r/z/n+/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/8Py+f+Cw9L/RKS6/weG - pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6DNAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8c2/L/ANTt/wDU7f8A1O3/ANLq/wDP5/8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Axd3/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/idr0/6To/f+h6f3/nuj9/5vo/f+Y6P3/luj8/4Hj+/9x4fv/beD6/2jg+v9k3/r/X9/6/1rf - +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/zLc9/996fr/fun6/37p+v9+6fr/fun6/37n+P9Epbr/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8AlbD/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/xLQ - 5v9c3u7/qO72/73y+P/C8/n/0Pf8/9H3/P/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5/v/R+f7/yvL4/73f - 5/+u2OH/YLLE/wyJpf8Ag6H/AIOh/wCDof8Ag6H/AIOhyACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuRrv9N5/n/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM3l/wCDn/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/2jH4f+b5/z/nOj8/5ro/f+X5/3/lOj8/5Hn/P+E5fz/bOD6/2fg+v9j3/r/Xt/6/1ne - +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z3c+P842/j/M9v3/y/a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/ZuX5/37p+v9+6fr/fun6/37p+v9+6fr/cMze/wiDnv8Af5v/AH+b/wB/ - m/8Af5v/AICb/wDA2v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/IdPn/3vl - 8f+58fj/vfL4/73y+P+98vj/vfL4/8Pz+f/Q9/z/0ff8/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5 - /v/H7vT/vd/n/73f5/+u2OH/Sqe8/wGDof8Ag6H/AIOh/wCDof8Ag6GxAKqqAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGwAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/K7vS/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8w4fb/ANTt/wDU7f8A1O3/ANPs/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Akqz/AH+b/wB/ - m/8Af5v/AH+b/wB/m/9Is8z/kuX8/5fo/P+U5/3/kef8/47n/P+L5/z/ieb7/3Tj+v9i3/r/Xt/6/1ne - +f9U3vn/T935/0vd+f9G3fj/Qdz4/z3c+P842/f/M9v3/y7a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Lf+P9+6fr/fun6/37p+v9+6fr/fun6/3zk9f8vmK//AH+b/wB/ - m/8Af5v/AH+b/wB/m/8An7n/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8e0+j/feby/7zy - +P+98vj/vfL4/73y+P+98vj/vfL4/73y+P+98vj/xfT6/9H3/P/R9/z/0fn9/9H5/v/R+f7/0fn+/9H5 - /v/R+f7/0fn+/8Lp8P+93+f/vd/n/73f5/+IxdP/E4yo/wCDof8Ag6H/AIOh/wCCopEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKEbAIShTwCBo0UAg6IhAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIahJgCD - ofoAg6H/AIOh/wCDof8Ag6H/AIOh/wWKp/9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5vn/AtTt/wDU7f8A1O3/ANTt/wDR6f8A0en/ANHp/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AKK8/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/IJay/4jk/P+S5vz/kOf8/43m/P+K5vz/h+b7/4Tm+/+C5vv/eOT7/2Dg - +f9U3vn/T935/0rd+f9G3fj/Qdz4/zzc+P832/f/M9v3/y7a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8p2vf/def6/37p+v9+6fr/fun6/37p+v9+6fr/YMDU/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AIKe/wDI4P8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/xPR6P905PH/vPL4/73y - +f+98vn/vfL5/73y+f+98vn/vfL4/7Dw9/+X6/T/g+fy/3Tj8P+G5PH/leby/5/p8/+t7/j/xPj+/9H5 - /v/R+f7/0fn+/9H5/v/Q+f7/v+Tr/73f5/+93+f/vd/n/6rW4P8smLH/AIOh/wCDof8Ag6H/AIOh7AAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGFAIOh+QCDof8Ag6H/AIOh/wCDofkAg6HYAIOhrQCDoHEAhKI0AKqqAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Iq/I/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdfv/wDU7f8A1O3/ANTt/wDS6v8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wC3 - 0P8Af5v/AH+b/wB/m/8Af5v/AH+b/wKBnP912/T/jOb8/4rm/P+I5vz/heX7/4Lm+/9/5fv/feX7/3vl - +v945fr/aOL6/07e+P9F3fj/QNz4/zzc+P832/f/Mtv3/y3a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/1Pi+f9+6fr/fun6/37p+v9+6fr/fun6/3re - 8P8ajKX/AH+b/wB/m/8Af5v/AH+b/wB/m/8AqcP/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/Cc/n/2Lg8P+28fj/vfL5/73y - +f+98vn/vfL5/5jr9f9o3+7/Osne/xayyv8AnLf/AI2p/wCFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wOE - of8YmbH/Ub3Q/4rd6f/C9fv/0fn+/833/f++4Oj/vd/n/73f5/+93+f/ud3l/0Skuv8Ag6H/AIOh/wCD - of8Ag6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKFfAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe8AhKKZAISiNAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIahEwCDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9D2e7/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/FNnw/wDU7f8A1O3/ANTt/wDS6/8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8AzeX/AIKd/wB/m/8Af5v/AH+b/wB/m/8Af5v/RrjT/4Hk+/+F5vz/guX7/3/l+/995fv/euX7/3jk - +v915fr/c+T6/3Dk+v9s5Pr/VuD5/z3c+P822/f/Mtv3/y3a9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8x3Pf/fen6/37p+v9+6fr/fun6/37p - +v9+6fr/SrLG/wB/m/8Af5v/AH+b/wB/m/8Af5v/AIik/wDO5f8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Bzub/Sdzt/6rv9/+98vn/vfL5/73y - +f+Y6/X/V9fo/xi0zP8AlrH/AIOf/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/A4Og/0ezx/+d5e//yPT6/73f5/+93+f/vd/n/73f5/+83ub/VazA/wCD - of8Ag6H/AIOh6QCEoRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HJAISiTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKN0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/GKK9/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LeD1/wDU7f8A1O3/ANTt/wDT7P8A0uv/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wCWsf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/9j0+3/fuT7/33l+/965Pv/eOX7/3Xk - +v9z5Pr/cOT6/27k+f9s4/n/aeT6/2jj+v9f4vn/Qd74/yza9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/2Tk+f9+6fr/fun6/37p - +v9+6fr/fun6/3XX6P8Ig57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Atc7/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8v2Ov/mez1/73z+f+98/n/rO/3/2Pd - 6/8atMz/AI+r/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zCiuf+M2eX/vd/n/73f5/+93+f/vd/n/73f - 5/9YrsH/AIOh/wCDof8Ag6HEAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCodkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhKC6AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKfcACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof87z+X/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/wHU7f8A1O3/ANTt/wDU7f8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8Atc7/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/y2oxP9n3PP/deT6/3Pj - +v9w5Pr/buP6/2vk+f9p4/n/Z+P5/2Tj+v9i4/n/YeP5/17j+f9P4fj/MNz3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9A3vj/fun6/37p - +v9+6fr/fun6/37p+v9+6Pn/NKC3/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJGs/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/xbU6v+A5/P/vfP5/73z+f+T6vT/RMnb/wWZ - tP8AgZ//AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zyovv+o1d//vd/n/73f - 5/+93+f/vd/n/1Gqv/8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAhaFJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIahEwCDoewAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5ey/07o - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/wzX8P8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/AM/p/wCFof8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BoWh/zW1 - 0P9k4Pj/auP6/2jj+f9m4vn/ZOP5/2Hi+f9f4vn/XeL5/1ri+f9a4/n/XOP5/1zj+f9I4Pj/K9v3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/KNr3/3Pn - +v9+6fr/fun6/37p+v9+6fr/fun6/2fM3/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Av9j/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ - 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/CdLp/2Li8f+28vj/vPP5/4Lk8P8pts3/AIml/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/Coej/33A - zv+93+f/vd/n/73f5/+23OT/FY2p/wCDof8Ag6H/AISiVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhmgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCopEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8Apb//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/C4yo/zvD3P9e4fn/YeL5/17i+f9c4vn/WuL5/1ji+f9W4vn/WOL5/1rj+f9c4/n/XuP5/1zj - +f9A3vj/KNr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/9R4fn/fun6/37p+v9+6fr/fun6/37p+v995ff/HZCq/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJu2/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8D0un/Sd7v/6rw+P+68/n/edzp/yCowP8AgqD/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCFov8AhqP/AIyo/wCIpv8AhKH/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/UKm9/7nd5f+93+f/vd/n/3m+zv8Ag6H/AIOh/wCDofIAg6MnAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAICjJAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoCMAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/CI6r/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/AMnj/wCLpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/EZay/0DO6P9X4fj/V+H5/1Xh+f9U4vn/VuL5/1fi+f9Z4/n/W+P5/1zj - +f9e4/n/YOT5/1ji+f843fj/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/L9v3/3vo+v9+6fr/fun6/37p+v9+6fr/fun6/1G+0/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCA - nf8AyOH/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wHS6v8/3e//oO73/7fx+P9v0uH/GJqz/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIKg/wCTrv8AoLv/AKzG/wC40v8Axd3/AM7m/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDP - 5/8AxN3/ALrS/wCqxf8Am7X/AIek/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8wmrD/s9rj/73f5/+83ub/KJaw/wCDof8Ag6H/AIOh1ACAmQoAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCi - vv8Aj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodAAgJkKAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ots7/T+n8/0/p/P9P6fz/T+n8/0/o/P8L1u//ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0uv/AK3I/wCEoP8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJz/F6S//0DX8P9R4fn/UuH5/1Ti+f9W4vn/V+L5/1nj - +f9c4/n/XeP5/1/k+f9h5Pn/YuT5/1Hh+f8v2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/9h5Pn/fen6/37p+v9+6fr/fun6/37p+v954fL/CoWg/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AKfB/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/zbc7v+Y7ff/s+31/2bH1/8Sjqn/AIGe/wCBnv8AgZ7/AISg/wCX - sv8AqsT/AL3X/wDQ6P8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDQ6P8Awdr/AKnD/wSPqv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ySTrP+r1uD/vd/n/5LK1/8Bg6H/AIOh/wCDof8Ag6KkAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShNgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCM - qf8A1O3/ANTt/wDI4f8Ap8P/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOghAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaIsAIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/A4el/0ff8/9P6fz/T+n8/0/p/P8s4PX/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Azeb/AKK+/wCA - nf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Cg6D/H7LN/0jc9f9T4vn/VeL5/1bi - +f9Y4vn/WuP5/1zj+f9e4/n/YOT5/2Hk+f9j5Pn/Y+T5/0ng+P8q2/f/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/Pt74/3zp+v9+6fr/fun6/37p+v9+6fr/fun6/zuvxv8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wCGov8A0On/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8C0uv/QN3w/5js9v+r5+//WrnM/wyHo/8AgZ7/AIGe/wCJpv8AoLz/ALnT/wDP - 6P8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/KMrh/x6owv8Cg6H/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/G46o/6zX4P+93+f/QKK5/wCDof8Ag6H/AIOh/wCC - oWoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGoSYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ah6b/ANTt/wDU7f8A1O3/ANTt/wDL5P8AmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofUAgKQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKirACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8eq8T/T+n8/0/p/P9I5/v/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8AxuD/AJey/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Fiqb/K7/a/1Hg - +f9W4vn/V+L5/1nj+f9b4/n/XOP5/17j+f9g5Pn/YuT5/2Tk+f9m5fn/YeT5/0De+P8o2vf/J9r3/yfa - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yja9/9w5vr/fen6/37p+v9+6fr/fun6/37p+v9s1+r/AYCb/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/ALLM/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wfU7f9N4PL/o+v0/57d5/9Jqb7/BYOg/wCBnv8AjKj/AKfC/wDC3P8A0+z/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wbT7P9O5/r/R93x/yay - y/8EhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8hkav/r9ji/6bU3v8GhqP/AIOh/wCD - of8Ag6H5AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wCeuv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKhiwAAAAAAAAAAAAAAAAAAAAAAAAAAAIakKgCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ak7D/A4uo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0DW7P9P6fz/ENjw/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8AvNb/AI2o/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8MlLD/OMzm/1Xi+f9X4vn/WeP5/1vj+f9d4/n/X+T5/2Hk+f9i5Pn/ZOT5/2bl+f9n5fn/W+P5/zbd - +P8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/TuH4/3vo+v996fr/fun6/37p+v9+6fr/fuj6/yKb - tP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCNqP8A0+3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/I9nv/23l8/+r6PD/esnX/zGZsf8BgZ7/AI6r/wCpxP8Aw93/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Etfu/0zn - +v9O5/r/Rt3w/yCrxP8Bg6D/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ymWrv+23OT/Wq/C/wCD - of8Ag6H/AIOh/wCDoeEAhqETAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAImdDQCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8A0On/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ev/AI2q/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoe8AjqoJAAAAAAAAAAAAAAAAAAAAAACDoqEAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/B8Ld/y2/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/NeL3/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AdTt/wHU7f8C1O3/AtTt/wLT6/8Bscr/AIai/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wCAm/8Xor3/Rdfv/1ji+f9a4/n/XOP5/13j+f9g5Pn/YeT5/2Pk+f9l5fn/Z+X5/2jl - +f9q5fn/U+L5/y7b9/8n2vf/J9r3/yfa9/8n2vf/J9r3/y/b9/966Pr/fOn6/33p+v9+6fr/fun6/37p - +v9Yyt//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/ALzW/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W - 7v9M4fL/lOXw/5DX4/9RrsH/DYej/wCSrf8ArMf/AMfg/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8e3PL/T+j7/0/o+/9P6Pv/QdXr/xGXsv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/PaC2/7Pa - 4/8SjKj/AIOh/wCDof8Ag6H/AIKitAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AMzm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC30v8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOiUgAAAAAAAAAAAAAAAACAoh4Ag6H7AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AJe0/y/h9v9O5/v/DZOw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wK91/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU - 7f8C1O3/AtTt/wPV7f8D1e3/BNXt/wTV7f8E1e3/BNXt/wTQ6P8CpsD/AIGd/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGDn/8isMv/UN71/1vj+f9c4/n/XuP5/2Dk+f9h5Pn/ZOT5/2bl - +f9n5fn/aeX5/2vm+v9p5fn/SuD4/yra9/8n2vf/J9r3/zLc9/9g5Pn/eOj6/3vo+v996fr/fun6/37p - +v9+6fr/fOb4/w6Lpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wCXsv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//VOLz/3Tf - 7v900N//QKm//w2MqP8AnLj/ALTP/wDM5v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yzg9f9P6fz/T+j7/0/o+/9O5/r/Lr3U/wOFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv9cr8L/dLvM/wCDof8Ag6H/AIOh/wCDof8Ag6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDN5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/AI2q/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoJ8AAAAAAAAAAAAAAAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wrD3v9O6Pz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak7D/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/A9Xt/wTV - 7f8E1e3/BNXt/wXV7f8F1e3/BtXt/wbV7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wbJ4v8Cm7X/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWIpP8wv9n/V+H4/1zj+f9f5Pn/YeT5/2Lk - +f9k5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9m5fn/Qd74/03h+P9y5/r/duf6/3fo+v956Pr/e+j6/33p - +v9+6fr/fun6/37p+v9CvNP/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJv/AMbg/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDR6v8Xwdn/IbPM/wme - uf8Aob3/ALTO/wDF3/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1e3/OeP4/0/p/P9P6fz/T+n8/0/p/P9C2ez/D5Sv/wCBnv8AgZ7/AIGe/wCB - nv8AgZ7/AYGe/37Az/8kla7/AIOh/wCDof8Ag6H/AIOh+gCCoTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCs - yP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LSAAAAAAAAAAAAgKoMAIOh7wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCTsP8v4fb/T+n8/0/p/P9P6fz/FZ+5/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDL - 5f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wXV - 7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wjV7v8I1e7/Cdbu/wnW7v8J1u7/Cdbu/wnW7v8J1u7/Ctbu/wfA - 2f8CkKv/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wyTrv8/y+X/XOP5/1/k - +f9h5Pn/Y+T5/2Xl+f9n5fn/aOX5/2rl+f9s5vr/beb6/2/m+v9y5/r/c+f6/3Xn+v936Pr/eOj6/3ro - +v986fr/fen6/37p+v9+6fr/Wdzy/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCkvv8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT6/8AyeL/ANDp/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXW7v9E5vn/T+n8/0/p/P9P6fz/T+n8/03m+f8hrcb/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8MiKT/dbzM/wCDof8Ag6H/AIOh/wCDof8Ag6HgAICcEgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA - pBwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8AyOL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCSkgcAAAAAAIKiaACDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Jv9n/Ten8/0/p/P9P6fz/T+n8/0HX7P8BhKL/AIOh/wCDof8Ag6H/AIOh/wCD - of8AzOb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV - 7f8I1e7/CNXu/wnW7v8J1u7/Cdbu/wnW7v8K1u7/Ctbu/wvW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/wzW - 7v8M1u7/DNXt/we1z/8BiKP/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/xae - uP9M1ez/YOT5/2Hk+f9j5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9u5vr/cOb6/3Ln+v9z5/r/def6/3fo - +v946Pr/euj6/33p+v9z5/r/Qd32/xWxzv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ag5//AM/o/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/zDA - 1/8Cg6D/AIGe/wCBnv8AgZ7/AIGe/yqXr/84nrb/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6MnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIem/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wCHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAACCoNUAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Aj6v/LuD1/0/p/P9P6fz/T+n8/0/p/P9P6fz/D5ey/wCDof8Ag6H/AIOh/wCD - of8AmbX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AdTt/wLU7f8C1O3/A9Xt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW - 7v8J1u7/Ctbu/wrW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/w3W7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W - 7v8O1u7/Dtbu/w7W7v8P1+7/DtPq/weqw/8Bg57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AYCd/yatyP9Y3fT/YuT5/2Tk+f9m5fn/Z+X5/2nl+f9r5vr/bOb6/27m+v9x5/r/cuf6/3Tn - +v925/r/d+j6/3fo+v9R4fn/IMHd/waMqP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGu - yP8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vL/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/PNDm/weJpv8AgZ7/AIGe/wCBnv8AgZ//PqG4/wSFov8Ag6H/AIOh/wCDof8Ag6H/AIKgZgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKiPwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCQrf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Ah6b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKfLQCEoD4Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/CLjR/03p+/9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8jsMr/AYak/wCE - ov8An7v/AM7m/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU - 7f8C1O3/AtTt/wPV7f8E1e3/BNXt/wXV7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8J1u7/Ctbu/wvW - 7v8M1u7/DNbu/wzW7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W7v8P1+7/D9fu/xDX7v8Q1+7/ENfu/xDX - 7v8Q1+7/ENfu/xDX7v8R1+7/Edfu/xHX7v8R1+7/D83k/waeuP8AgJv/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/BIah/zS71f9g4vj/ZeX5/2fl+f9o5fn/auX5/2zm+v9t5vr/b+b6/3Hn - +v9z5/r/def6/1/k+f8szun/C5m1/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8BlrD/BNXt/wPV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yrf9f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9D2e7/Co6q/wCBnv8AgZ7/AIGe/wKDof8smLH/AIOh/wCDof8Ag6H/AIOh/wCD - ofQAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACComgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AnLj/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A0uv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoRsAhKCfAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIek/ybb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/POT5/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU - 7f8D1e3/BNXt/wTV7f8F1e3/BtXt/wfV7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/wzW - 7v8N1u7/Dtbu/w7W7v8O1u7/D9fu/w/X7v8Q1+7/ENfu/xDX7v8Q1+7/Edfu/xHX7v8S1+7/Etfu/xPX - 7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/D8Tc/wSSrf8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpCr/0TJ4f9l5fn/Z+X5/2nl+f9r5vr/bOb6/27m - +v9w5vr/Z+X5/zvY8v8QpcL/AYCd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGE - oP8Ersj/B9Pr/wbV7f8E1e3/BNXt/wPV7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/O+T4/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fe8v8Nkq7/AIGe/wCBnv8AgZ7/FI2o/wqIpf8Ag6H/AIOh/wCD - of8Ag6H/AIOhywCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqGNAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKnF/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AMTe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgKQOAIOh8wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKlwf9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ - 8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV - 7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8J1u7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W - 7v8O1u7/D9fu/xDX7v8Q1+7/ENfu/xHX7v8R1+7/Etfu/xPX7v8T1+7/E9fu/xPX7v8U1+7/FNfu/xTX - 7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8W2O//Ftjv/xbY7/8W1+//DrnS/wKJ - pf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/G523/1TV7P9n5fn/aeX5/2vm - +v9q5fn/St/3/xmz0P8ChaD/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wSj - vv8Iz+f/Cdbu/wjV7v8H1e3/B9Xt/wbV7f8F1e3/BNXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW - 7/9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/w+VsP8AgZ7/AIGe/wCCn/8Oiqb/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhuQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC10P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCzzv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HDAIKgXgCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8d0en/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7l - +f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wTV - 7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8K1u7/C9bu/wzW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX - 7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xTX7v8V2O7/Fdju/xXY7v8V2O7/Ftjv/xbY - 7/8X2O//F9jv/xfY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY - 7/8X1Oz/DK3H/wGDn/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8BgJz/Kq3F/2De - 8/9W4vn/JsPe/waOqv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BJey/wvI - 4f8M1u7/DNbu/wvW7v8K1u7/Cdbu/wnW7v8I1e7/B9Xt/wfV7f8G1e3/BNXt/wTV7f8D1e3/AtTt/wHU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/E9nw/03o/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/DJOv/wCBnv8AgZ7/AYOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCCoy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAJmZBQCDoPMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Axd7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOijgCE - obwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aka//PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8b3PL/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wPV7f8E1e3/BdXt/wbV - 7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8O1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX - 7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8W2O//F9jv/xfY7/8Y2O//GNjv/xjY - 7/8Y2O//Gdjv/xnY7/8Z2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY - 7/8a2O//Gtjv/xrY7/8Xz+f/CqG7/wCAnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8CgJz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ci6f/C7vT/xDX - 7v8P1+7/Dtbu/w7W7v8N1u7/DNbu/wzW7v8L1u7/Ctbu/wnW7v8J1u7/B9Xt/wfV7f8G1e3/BdXt/wTV - 7f8D1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8m3vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jh9P8Ika3/AIGe/wCB - oP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HLAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAgqD/AIGf/wCBn/8AgqD/AIOh/wCDof8AiKX/ANTs/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJi0/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D7rU/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9G5/r/AdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wTV7f8F1e3/B9Xt/wfV - 7f8I1e7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W7v8P1+7/ENfu/xDX7v8R1+7/Etfu/xPX - 7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xnY7/8a2O//Gtjv/xrY - 7/8a2O//G9nv/xvZ7/8b2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ - 7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8Xx9//B5ex/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGFoP8Lr8n/E9Xs/xPX - 7v8S1+7/Edfu/xDX7v8Q1+7/D9fu/w7W7v8O1u7/Ddbu/wzW7v8M1u7/Ctbu/wnW7v8J1u7/CNXu/wfV - 7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AdXt/z3k+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R9/0/wKN - qf8AgZ7/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGFAIKg/wCBn/8AgZ7/AICe/wCAnv8AgZ//AJu4/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCNqv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi/yzd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Jt71/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BNXt/wXV7f8H1e3/B9Xt/wjV - 7v8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX7v8Q1+7/Edfu/xLX7v8T1+7/E9fu/xTX - 7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8b2e//HNnv/xzZ - 7/8c2e//HNnv/x3Z7/8d2e//Htnv/x7Z7/8e2e//Htnv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z - 7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x7Z7/8WwNj/BY6o/wB/m/8Af5v/AH+b/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wqkvv8V0ej/Ftjv/xXY - 7v8V2O7/FNfu/xPX7v8T1+7/Etfu/xHX7v8Q1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8M1u7/C9bu/wnW - 7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1+//S+j7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P801uz/AIWi/wCCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H0AICfIAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOh4ACCoP8AgZ//AIGe/wCAnv8AgJ3/AH+d/wCs - xv8A0Oj/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AhaP/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGbtv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/wbV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wTV7f8E1e3/BdXt/wfV7f8H1e3/Cdbu/wnW - 7v8K1u7/DNbu/wzW7v8N1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8U1+7/Fdju/xXY - 7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Hdnv/x7Z - 7/8f2e//H9nv/x/Z7/8f2e//H9nv/yDZ7/8g2e//INnv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa - 7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8g2e//INnv/x/Y7f8TtMz/Aoai/wB/ - m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/B5Ww/xXH4P8Z2O//Gdjv/xjY - 7/8Y2O//F9jv/xbY7/8V2O7/Fdju/xTX7v8T1+7/E9fu/xLX7v8Q1+7/ENfu/w/X7v8O1u7/Dtbu/wzW - 7v8M1u7/C9bu/wrW7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xfJ4f8Agp//AIOg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/AIOh/wCCoP8AgZ//AICe/wB/ - nf8AwNj/AM3l/wDM5P8Azeb/ANLq/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJOv/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ywtv/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zLh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Cdbu/wrW - 7v8M1u7/DNbu/w3W7v8O1u7/D9fu/xDX7v8R1+7/Etfu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Ftjv/xjY - 7/8Y2O//GNjv/xnY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/x3Z7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ - 7/8g2e//Idrv/yHa7/8h2u//Idrv/yLa7/8i2u//Itrv/yPa7/8j2u//I9rv/yPa7/8j2u//JNrw/yTa - 8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/I9rv/yPa7/8j2u//I9rv/yLa7/8i2u//Itrv/x/U - 6v8PqMH/AYGd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8EjKf/FLzU/xzZ7/8c2e//G9nv/xvZ - 7/8a2O//Gtjv/xnY7/8Y2O//GNjv/xfY7/8W2O//Fdju/xXY7v8T1+7/E9fu/xLX7v8R1+7/ENfu/w/X - 7v8O1u7/Dtbu/w3W7v8M1u7/C9bu/wnW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wTV7f8C1O3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/OOP4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9I5/r/A7bQ/wCDoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKhOQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ai6n/ANHp/wDO5v8AzOT/AMvj/wDJ4f8AyuL/AM/n/wDT7P8A1O3/ANTt/wC10P8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/Nd/1/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8U2fH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8J1u7/Ctbu/wzW - 7v8M1u7/Dtbu/w7W7v8P1+7/ENfu/xHX7v8S1+7/E9fu/xTX7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY - 7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa - 7/8i2u//Itrv/yPa7/8j2u//JNrw/yTa8P8k2vD/JNrw/yTa8P8l2vD/Jdrw/yXa8P8m2vD/Jtrw/yba - 8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8l2vD/Jdrw/yXa8P8l2vD/JNrw/yTa - 8P8k2vD/JNrw/x7N4/8LnLb/AH+b/wB/m/8Af5v/AH+b/wKFoP8SsMn/H9ft/x/Z7/8f2e//Htnv/x3Z - 7/8c2e//HNnv/xzZ7/8b2e//Gtjv/xrY7/8Z2O//GNjv/xjY7/8W2O//Fdju/xXY7v8U1+7/E9fu/xLX - 7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/C9bu/wnW7v8J1u7/B9Xt/wfV7f8F1e3/BNXt/wPV - 7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX7/9N6fv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yrf9f8Anrn/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ocUA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgwAg6HvAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AKjE/wDU7f8A1O3/ANLr/wDQ6P8AzOT/AMjg/wDI3/8AyN//AMzk/wDS6v8A1O3/AKzI/wCE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5q3/0vo+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9G5/r/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BdXt/wbV7f8H1e3/CNXu/wnW7v8K1u7/DNbu/wzW - 7v8O1u7/Dtbu/w/X7v8Q1+7/Edfu/xPX7v8T1+7/FNfu/xXY7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY - 7/8a2O//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yLa7/8i2u//I9rv/yTa - 8P8k2vD/JNrw/yXa8P8l2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yfb8P8o2/D/KNvw/yjb - 8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yfb8P8n2/D/J9vw/yba - 8P8m2vD/Jtrw/yba8P8m2vD/Jdrw/xzD2/8Hkav/AICc/w6iu/8g0+j/Itrv/yLa7/8h2u//Idrv/yDZ - 7/8f2e//H9nv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8Z2O//GNjv/xjY7/8X2O//Ftjv/xXY - 7v8U1+7/E9fu/xPX7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wbV - 7f8F1e3/BNXt/wLU7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/KN/0/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/Bdbu/wCMqf8Ag6D/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOiUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFqAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wDF3v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8Az+f/AMvj/wDH3v8Axdz/AMjg/wDP - 5/8AxN//AJ67/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISj/xjP5/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Kd/0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BtXt/wfV7f8I1e7/Cdbu/wrW7v8M1u7/DNbu/w7W - 7v8O1u7/D9fu/xDX7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xnY7/8a2O//Gtjv/xvZ - 7/8c2e//HNnv/x7Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa - 8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yjb8P8o2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb8P8p2/D/Ktvw/yrb - 8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb - 8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8n2/D/Jtnv/yHO5f8m2vD/Jdrw/yTa8P8k2vD/JNrw/yPa - 7/8i2u//Idrv/yHa7/8g2e//INnv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8a2O//Gdjv/xjY - 7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xPX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8L1u7/Ctbu/wnW - 7v8I1e7/B9Xt/wXV7f8E1e3/A9Xt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU - 7v9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8AwNv/AIOg/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoccAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKg1QCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCRr/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANDo/wDL - 4/8Axdz/AMXc/wDM5P8A0On/AMHb/wCyzv8ApcH/AIOh/wCDof8Ag6H/AImn/wC10P8y4fb/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wzX8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W - 7v8P1+7/ENfu/xHX7v8T1+7/E9fu/xTX7v8V2O7/Ftjv/xjY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/xzZ - 7/8d2e//Htnv/x/Z7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa8P8m2vD/Jtrw/yfb - 8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/ynb8P8q2/D/Ktvw/yvb8P8r2/D/K9vw/yvb8P8r2/D/LNvw/yzb - 8P8s2/D/LNvw/yzb8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/LNvw/yzb8P8s2/D/LNvw/yvb - 8P8r2/D/K9vw/yvb8P8r2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8m2vD/Jtrw/yba - 8P8l2vD/JNrw/yTa8P8j2u//Itrv/yHa7/8h2u//INnv/x/Z7/8f2e//Htnv/x3Z7/8c2e//HNnv/xrY - 7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW - 7v8L1u7/Cdbu/wnW7v8H1e3/BtXt/wXV7f8E1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J6Pv/A9Xu/wCeuv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AISfOAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKg/wCDof8Ar8v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDQ6P8AyuL/AMTb/wDI3/8A0Oj/ANTt/wDU7f8AyOL/AMTe/wDQ6f8B1e3/Suf7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Dl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8G1e3/B9Xt/wjV7v8J1u7/C9bu/wzW7v8N1u7/Dtbu/w/X - 7v8Q1+7/Edfu/xLX7v8T1+7/FNfu/xXY7v8W2O//GNjv/xjY7/8Z2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z - 7/8f2e//H9nv/yDZ7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/Jtrw/yba8P8n2/D/KNvw/ynb - 8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb8P8s2/D/LNvw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ltzw/y7c - 8P8u3PD/Ltzw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y7c8P8u3PD/Ltzw/y7c - 8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/ynb - 8P8o2/D/J9vw/yba8P8m2vD/Jdrw/yTa8P8k2vD/I9rv/yLa7/8h2u//Idrv/yDZ7/8f2e//H9nv/x3Z - 7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8Q1+7/ENfu/w7W - 7v8O1u7/DNbu/wzW7v8K1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f9B5fr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yLd9P8AyuP/AISi/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAACDobsAg6H/AIOh/wCD - of8Ag6H/AIOh/wCCoP8AhqP/AM3m/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ur/AMri/wDI4P8AzeX/ANTt/wDU7f8A1O3/Etnw/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8n3vX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8K1u7/DNbu/wzW7v8O1u7/Dtbu/xDX - 7v8R1+7/Etfu/xPX7v8U1+7/Fdju/xbY7/8Y2O//GNjv/xrY7/8a2O//G9nv/xzZ7/8d2e//Htnv/x/Z - 7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/yrb - 8P8r2/D/K9vw/yzb8P8s2/D/Ldzw/y3c8P8t3PD/Ltzw/y7c8P8v3PD/L9zw/zDc8P8w3PD/MNzw/zDc - 8P8w3PD/MNzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/MNzw/zDc - 8P8w3PD/MNzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/K9vw/yvb - 8P8r2/D/Ktvw/ynb8P8p2/D/KNvw/yfb8P8m2vD/Jtrw/yXa8P8k2vD/JNrw/yPa7/8i2u//Idrv/yDZ - 7/8f2e//H9nv/x7Z7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Fdju/xXY7v8T1+7/E9fu/xHX - 7v8Q1+7/D9fu/w7W7v8N1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/AdTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5Pn/ANTt/wCe - u/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H4AICkDgAAAAAAAAAAAAAAAACCojcAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqD/AKW//wDS6v8A0en/ANHp/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDN5v8A0Oj/ANPs/yfe - 9f9P6fz/T+n8/zvP5v8ms83/JbPM/yq50f8uvdX/CbfR/wC51P8Avdj/AMDb/wDE3/8AyeL/AM3n/wDQ - 6f8A1O3/AdTt/wLU7f8D1e3/BNXt/wbV7f8H1e3/CNXu/wnW7v8L1u7/DNbu/w3W7v8O1u7/D9fu/xDX - 7v8S1+7/E9fu/xTX7v8V2O7/Ftjv/xfY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/x3Z7/8e2e//H9nv/yDZ - 7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb - 8P8s2/D/Ldzw/y3c8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zDc8P8w3PD/Mdzw/zHc8P8y3PH/Mtzx/zLc - 8f8y3PH/Mtzx/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd - 8f8y3PH/Mtzx/zLc8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c - 8P8t3PD/Ldzw/yzb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/yjb8P8n2/D/Jtrw/yba8P8l2vD/JNrw/yPa - 7/8i2u//Idrv/yHa7/8f2e//H9nv/x7Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xjY7/8W2O//Fdju/xTX - 7v8T1+7/Etfu/xHX7v8Q1+7/Dtbu/w7W7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8F1e3/BNXt/wLU - 7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f9E5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wvX - 8P8AxN7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAAAAAAg6GzAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIKg/wDG4f8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDQ6P8A0ur/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f895Pn/T+n8/yKvyf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCIpf8Ai6j/AY6s/wGUsP8Cl7T/A5q3/wOeuv8Fo77/BabD/weqxf8IsMr/CbPN/wq2 - 0f8Nu9X/Dr/Z/xDE3P8SyOD/FMzk/xbR6P8Y1Ov/Gtjv/xvZ7/8c2e//Hdnv/x/Z7/8f2e//INnv/yHa - 7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/Kdvw/ynb8P8q2/D/K9vw/yvb8P8s2/D/Ldzw/y3c - 8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8y3PH/M93x/zPd8f803fH/NN3x/zXd - 8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd - 8f813fH/Nd3x/zXd8f813fH/Nd3x/zTd8f803fH/M93x/zPd8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc - 8P8w3PD/L9zw/y/c8P8u3PD/Ldzw/y3c8P8s2/D/K9vw/yvb8P8q2/D/Kdvw/ynb8P8o2/D/Jtrw/yba - 8P8l2vD/JNrw/yTa8P8i2u//Idrv/yHa7/8f2e//H9nv/x3Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xfY - 7/8W2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wXV - 7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8m3vX/ANTt/wCXtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GoAAAAAAAAAAAAgqExAIOh/gCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCbuP8A0+z/ANLq/wDR6f8A0Oj/AM/n/wDP5/8Azub/AM3m/wDN - 5v8Az+f/ANLr/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8E1e7/Tuj8/0DW6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8CiKX/A4uo/wSPrP8GlLH/CJi0/wqc - t/8MoLv/DqXA/xCoxP8SrMb/FbLL/xe1zv8ZudL/H8Xd/yva7/8r2/D/LNvw/y3c8P8t3PD/Ltzw/y/c - 8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zXd8f813fH/Nt3x/zbd - 8f833fH/N93x/zfd8f833fH/N93x/zfd8f833fH/ON3x/zjd8f843fH/ON3x/zjd8f843fH/ON3x/zfd - 8f833fH/N93x/zfd8f833fH/N93x/zfd8f823fH/Nt3x/zXd8f813fH/Nd3x/zXd8f803fH/M93x/zPd - 8f8y3PH/Mtzx/zHc8P8x3PD/MNzw/zDc8P8v3PD/Ltzw/y3c8P8t3PD/LNvw/yvb8P8r2/D/Kdvw/ynb - 8P8o2/D/J9vw/yba8P8l2vD/JNrw/yTa8P8i2u//Idrv/yDZ7/8f2e//H9nv/x3Z7/8c2e//G9nv/xrY - 7/8Z2O//GNjv/xfY7/8V2O7/FNfu/xPX7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8L1u7/Cdbu/wjV - 7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v9M6Pv/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/QOX6/wDU7f8AtdD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wAAAAAAAAAAAIOgrwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aw93/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN - 5v8AzeX/AMzk/wDL4/8AzeX/ANDo/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/GNrx/0/p/P8wwdj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fjqr/Isfd/y7c8P8v3PD/MNzw/zDc - 8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f833fH/ON3x/zjd - 8f843fH/Od7x/zne8f853vH/Od7x/zne8f853vH/Ot7x/zre8f863vH/Ot7x/zre8f863vH/Ot7x/zre - 8f863vH/Od7x/zne8f853vH/Od7x/zne8f853vH/ON3x/zjd8f843fH/N93x/zfd8f833fH/Nt3x/zXd - 8f813fH/Nd3x/zTd8f8z3fH/M93x/zLc8f8y3PH/Mdzw/zDc8P8w3PD/L9zw/y7c8P8t3PD/LNvw/yvb - 8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ7/8f2e//Htnv/x3Z - 7/8c2e//Gtjv/xrY7/8Y2O//F9jv/xbY7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8O1u7/DNbu/wvW - 7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/NeL3/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8H1u7/AM7m/wCDov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AAAAAAIKiPwCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A0uv/ANHp/wDO5v8AzOT/AMri/wDK4v8AyeH/AM3l/wDR6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yzg9f9P6fz/PdLo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSLqP8v2+7/Mdzw/zLc - 8f8y3PH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Od7x/zre - 8f863vH/O97x/zve8f873vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze - 8f883vH/PN7x/zze8f883vH/PN7x/zze8f873vH/O97x/zve8f863vH/Ot7x/zne8f853vH/Od7x/zjd - 8f843fH/N93x/zfd8f823fH/Nd3x/zXd8f813fH/NN3x/zPd8f8y3PH/Mtzx/zHc8P8w3PD/L9zw/y7c - 8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ - 7/8f2e//Hdnv/xzZ7/8b2e//Gtjv/xnY7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/D9fu/w7W - 7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xrb8v9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/GNrx/wDU7f8AlrP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICAAgCD - oc4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AMDb/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0en/AM7m/wDL4/8AyOD/AMnh/wDN5v8A0uv/ANTt/wDU - 7f8A1O3/ANTt/wDU7f895Pn/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/KMrh/zPd - 8f803fH/Nd3x/zXd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/PN7x/zze - 8f883vH/PN7x/z3e8f893vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e - 8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z3e8f893vH/PN7x/zze8f883vH/PN7x/zve - 8f863vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/N93x/zbd8f813fH/Nd3x/zTd8f8y3PH/Mtzx/zHc - 8P8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa - 7/8h2u//INnv/x/Z7/8e2e//Hdnv/xzZ7/8a2O//Gtjv/xjY7/8X2O//Fdju/xXY7v8T1+7/Etfu/xDX - 7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8A1O3/AKfD/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC - oWIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDS6v8Azub/AMvj/wDM - 5P8A0Oj/ANTt/wDU7f8C1O7/Ten8/0/p/P9P6fz/Tef5/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y/T - 6f813fH/Nt3x/zfd8f833fH/ON3x/zne8f853vH/Ot7x/zve8f873vH/PN7x/zze8f893vH/Pd7x/z7e - 8f8+3vH/Pt7x/z/f8f8/3/H/P9/x/0Df8v9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf - 8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9A3/L/QN/y/0Df8v8/3/H/P9/x/z7e8f8+3vH/Pt7x/z7e - 8f893vH/PN7x/zze8f883vH/O97x/zre8f853vH/Od7x/zjd8f833fH/N93x/zbd8f813fH/Nd3x/zTd - 8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/Jtrw/yba - 8P8k2vD/I9rv/yLa7/8h2u//INnv/x/Z7/8d2e//HNnv/xvZ7/8a2O//GNjv/xjY7/8W2O//Fdju/xPX - 7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k - +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P834/j/ANTt/wC10f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wDE3v8A1O3/ANPs/wDS6/8A0+z/ANPs/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANLr/wDR6f8A0+z/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/M8Xb/wOHpf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP82yd//O8/m/zjM4v81x97/MMHY/y291P8quND/JbPM/yGu - x/8eqsT/GqW//xafuv8Tm7b/D5ey/wuRrv8HjKn/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKi - vP833fH/N93x/zjd8f853vH/Od7x/zre8f873vH/PN7x/zze8f893vH/Pt7x/z7e8f8+3vH/P9/x/z/f - 8f9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Lf8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf - 8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Lf8v9C3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Df - 8v9A3/L/P9/x/z7e8f8+3vH/Pt7x/z3e8f883vH/PN7x/zve8f863vH/Od7x/zne8f843fH/N93x/zfd - 8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yvb8P8r2/D/Kdvw/ynb - 8P8n2/D/Jtrw/yXa8P8k2vD/I9rv/yHa7/8g2e//H9nv/x7Z7/8c2e//G9nv/xrY7/8Z2O//GNjv/xbY - 7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8t4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+f6/wDU7f8AwNr/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wChvf8A1O3/ANTt/wDT7P8A0uv/ANLr/wDS - 6v8A0en/ANHp/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B2O3/AZCt/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Boqo/znM4/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/0fe8v9E2u//P9Tq/zvP - 5v84zOL/Ncfe/zDB2P8tvdT/KrjQ/yWzzP8hrsf/HqrE/xqlv/8Wn7r/Epu2/w+Xsv8Ag6H/CJCt/xuw - yf812e7/Od7x/zne8f863vH/O97x/zze8f883vH/Pd7x/z7e8f8+3vH/P9/x/0Df8v9A3/L/Qd/y/0Hf - 8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9E3/L/RN/y/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg - 8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9E3/L/RN/y/0Tf8v9D3/L/Q9/y/0Pf - 8v9C3/L/Qt/y/0Hf8v9B3/L/Qd/y/0Df8v8/3/H/Pt7x/z7e8f893vH/PN7x/zze8f873vH/Ot7x/zne - 8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y7c8P8t3PD/LNvw/yvb - 8P8q2/D/Kdvw/yjb8P8m2vD/Jtrw/yTa8P8j2u//Itrv/yHa7/8f2e//Htnv/x3Z7/8c2e//Gtjv/xnY - 7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV - 7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/H9zz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8G1e7/AMHb/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCHpP8AzOX/ANTt/wDU7f8A0+z/ANLr/wDS - 6/8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDP5/8A0Oj/ANLq/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8w4fb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sej7/wDQ - 6f8Anrr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/J7XO/07o+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PeDz/zjd - 8f853vH/Od7x/zve8f883vH/PN7x/z3e8f8+3vH/Pt7x/z/f8f9A3/L/Qd/y/0Hf8v9C3/L/Q9/y/0Pf - 8v9D3/L/RN/y/0Tf8v9F4PL/ReDy/0Xg8v9G4PL/RuDy/0bg8v9H4PL/R+Dy/0fg8v9I4PL/SODy/0jg - 8v9I4PL/SODy/0jg8v9I4PL/SODy/0jg8v9I4PL/R+Dy/0fg8v9H4PL/R+Dy/0bg8v9G4PL/ReDy/0Xg - 8v9F4PL/RN/y/0Tf8v9D3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9A3/L/P9/x/z7e8f8+3vH/Pd7x/zze - 8f883vH/O97x/zne8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y7c - 8P8t3PD/LNvw/yvb8P8p2/D/Kdvw/yfb8P8m2vD/KNvw/yrb8P8r2/D/J9vv/yPa7/8f2e//Hdnv/xzZ - 7/8a2O//Gdjv/xjY7/8X2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV - 7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DNjw/wDG3/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ArMj/ANTt/wDU7f8A1O3/ANPs/wDS - 6/8A0ur/ANLq/wDR6f8A0Oj/ANDo/wDP5/8Azub/AM3m/wDN5v8AzeX/AM3m/wDP5/8A0ur/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zvj - +f8A1O3/ANTt/wCxzP8AhKP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Wn7r/SOH0/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/zre - 8f863vH/O97x/zze8f883vH/Pt7x/z7e8f8/3/H/QN/y/0Hf8v9B3/L/Qt/y/0Pf8v9D3/L/RN/y/0Xg - 8v9F4PL/ReDy/0bg8v9H4PL/R+Dy/0jg8v9I4PL/SODy/0jg8v9J4PL/SeDy/0ng8v9K4PL/SuDy/0rg - 8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9J4PL/SeDy/0ng8v9I4PL/SODy/0jg - 8v9I4PL/R+Dy/0fg8v9G4PL/ReDy/0Xg8v9F4PL/RN/y/0Pf8v9D3/L/Qt/y/0Hf8v9B3/L/QN/y/z/f - 8f8+3vH/Pt7x/zze8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8z3fH/Mtzx/zHc - 8P8w3PD/Nd3w/0Lf8f9L4PL/VOLz/13k9P9k5fT/aOb0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuT0/1zj - 8/9T4vP/SODy/zze8f8w3PH/Htnv/xfY7/8V2O7/FNfu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW - 7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xHY8P8Axt//AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/ANLr/wDU7f8A1O3/ANTt/wDU - 7f8A0+z/ANPs/wDS6/8A0ur/ANHp/wDQ6P8Az+f/AM7m/wDN5v8Azeb/AM3l/wDM5P8Ay+P/AMvj/wDL - 4/8AzeX/AM/n/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8u4PX/ANTt/wDU7f8A1O3/AMLc/wCLqf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmP - rP8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nm - +f873vH/PN7x/z3e8f8+3vH/Pt7x/0Df8v9B3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Tf8v9F4PL/ReDy/0bg - 8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9K4PL/SuDy/0rg8v9K4PL/S+Hy/0vh8v9M4fL/TOHy/0zh - 8v9M4fL/TOHy/0zh8v9N4fL/TeHy/0zh8v9M4fL/TOHy/0zh8v9M4fL/TOHy/0vh8v9L4fL/SuDy/0rg - 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Lf - 8v9B3/L/Qd/y/z/f8f8+3vH/Pt7x/z3e8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nt3x/0Pg - 8v9V4vP/ZeX0/27m9P9t5vT/bOb0/2zm9P9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9l5fT/ZeX0/2Pl - 9P9i5PT/YuT0/2Dk9P9g5PT/X+T0/17k9P9R4vP/P9/y/ybb7/8V1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W - 7v8M1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/D9jv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8W2vH/AMLc/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/AMXe/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDN - 5v8AzOT/AMri/wDI4P8AyOD/AMnh/wDN5f8A0Oj/ANPs/wfW7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wDU7f8Azub/AJm1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AoWj/y691f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9F4/f/Pd7x/z7e8f8/3/H/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9E3/L/ReDy/0Xg8v9G4PL/R+Dy/0jg - 8v9I4PL/SeDy/0rg8v9K4PL/SuDy/0vh8v9M4fL/TOHy/03h8v9N4fL/TeHy/03h8v9N4fL/TuHz/07h - 8/9O4fP/TuHz/0/h8/9P4fP/T+Hz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TuHz/07h8/9N4fL/TeHy/03h - 8v9N4fL/TeHy/0zh8v9L4fL/S+Hy/0rg8v9K4PL/SeDy/0ng8v9I4PL/SODy/0fg8v9G4PL/ReDy/0Xg - 8v9E3/L/Q9/y/0Lf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/z3e8f883vH/O97x/zne8f9C3/L/V+Lz/2zm - 9f9x5/X/cef1/3Dn9f9v5vX/bub0/27m9P9s5vT/bOb0/2vm9P9q5vT/aeb0/2nm9P9n5vT/Z+X0/2Xl - 9P9l5fT/Y+X0/2Lk9P9i5PT/YeT0/2Dk9P9f5PT/XuT0/13k9P9c5PT/U+Lz/zrd8f8d2e//Edfu/xDX - 7v8O1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wvX7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/G9zy/wC6 - 1P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AK7J/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN5f8Q0+n/TuX4/0/o+/9P6fz/T+n8/0/p - /P9P6fz/T+n8/xja8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7P8Aq8b/AISi/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/G6XA/0vk+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/QuHz/z/f8f9A3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Xg8v9F4PL/RuDy/0fg8v9I4PL/SODy/0ng - 8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h8v9N4fL/TuHz/07h8/9P4fP/T+Hz/0/h8/9P4fP/UOHz/1Dh - 8/9Q4fP/UOHz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UOHz/1Dh8/9Q4fP/T+Hz/0/h - 8/9P4fP/T+Hz/0/h8/9O4fP/TeHy/03h8v9N4fL/TOHy/0vh8v9L4fL/SuDy/0rg8v9J4PL/SODy/0jg - 8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/0rg8v9l5fT/dej1/3To - 9f9z5/X/c+f1/3Ln9f9x5/X/cef1/2/m9f9u5vT/bub0/23m9P9s5vT/a+b0/2rm9P9p5vT/aeb0/2fm - 9P9n5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Lk9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk9P9b5PP/WuPz/0Tf - 8v8h2u//D9fu/w7W7v8M1u7/C9bu/wnW7v8I1e7/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8N1/D/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xja - 8v8Asc3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8P2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wO91v8Biaf/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Mk7D/Qtnt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Tun8/0Df8v9B3/L/Qt/y/0Pf8v9E3/L/ReDy/0Xg8v9H4PL/SODy/0jg8v9J4PL/SuDy/0rg - 8v9L4fL/TOHy/03h8v9N4fL/TuHz/0/h8/9P4fP/T+Hz/1Dh8/9Q4fP/UeLz/1Li8/9S4vP/UuLz/1Li - 8/9S4vP/UuLz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9S4vP/UuLz/1Li - 8/9S4vP/UuLz/1Hi8/9R4vP/UOHz/0/h8/9P4fP/T+Hz/07h8/9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg - 8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/UeLz/2vm9P946PX/d+j1/3bo - 9f916PX/dej1/3To9f9z5/X/c+f1/3Hn9f9x5/X/cOf1/2/m9f9u5vT/bub0/2zm9P9s5vT/aub0/2nm - 9P9p5vT/Z+b0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuT0/2Hk9P9g5PT/X+T0/17k9P9d5PT/W+T0/1vk - 8/9Z4/P/WePz/0fg8v8j2u//Dtbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8T2fD/AKjE/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AImo/wDK5P8A1O3/ANPs/wDS - 6v8A0en/ANHp/wDP5/8Az+f/AM/n/wDO5v8Azub/AM7m/wDO5v8Azub/AM/n/wDP5/8A0Oj/ANHp/wDS - 6v8A0uv/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yLe9P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/BtXu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8E1e3/Es3l/wSU - sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSIpf8zxdz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0zn+v9C3/L/Q9/y/0Tf8v9F4PL/RuDy/0fg8v9I4PL/SeDy/0rg8v9K4PL/S+Hy/0zh - 8v9N4fL/TeHy/07h8/9P4fP/T+Hz/1Dh8/9R4vP/UuLz/1Li8/9S4vP/U+Lz/1Pi8/9U4vP/VOLz/1Ti - 8/9U4vP/VOLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VOLz/1Ti - 8/9U4vP/VOLz/1Ti8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Hi8/9Q4fP/T+Hz/0/h8/9P4fP/TuHz/03h - 8v9N4fL/S+Hy/0rg8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg8v9M4PP/aeb1/3rp9v966fb/eun2/3jp - 9f946PX/d+j1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn9f9w5/X/b+b1/23m9P9m5fT/YOT0/13k - 8/9a4/P/WuPz/1zk9P9g5PT/ZeX0/2bl9P9l5fT/ZOX0/2Pl9P9i5PT/YeT0/2Dk9P9e5PT/XuT0/13k - 9P9b5PT/W+Tz/1nj8/9Z4/P/V+Pz/z/e8f8X2O//DNbu/wrW7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Dtfv/wCYtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wC/2v8A1O3/ANTt/wDS - 6/8A0ur/ANHp/wDP5/8Azub/AM3m/wDM5P8Ay+P/AMri/wDI4P8AyN//AMfe/wDF3P8AxNv/AMPa/wDC - 2f8Awdj/AMDX/wC+1f8AvtX/AL7V/wC+1f8Av9b/AMDX/wDA1/8Awdj/AMPZ/wDH3v8p1ur/TeT3/0/o - +/9P6fz/T+n8/0/p/P9P6fz/TOj7/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BdXt/xXY - 7v8X1uz/CqbA/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGvyP9N5/n/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9J5Pf/RN/y/0Xg8v9G4PL/R+Dy/0jg8v9J4PL/SuDy/0rg8v9M4fL/TeHy/03h - 8v9O4fP/T+Hz/0/h8/9Q4fP/UeLz/1Li8/9S4vP/U+Lz/1Ti8/9U4vP/VOLz/1Xi8/9V4vP/VuPz/1bj - 8/9W4/P/VuPz/1fj8/9X4/P/V+Pz/1fj8/9X4/P/WOPz/1jj8/9Y4/P/V+Pz/1fj8/9X4/P/V+Pz/1fj - 8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li8/9R4vP/UOHz/0/h - 8/9P4fP/TuHz/03h8v9N4fL/TOHy/0vh8v9K4PL/SeDy/0ng8v9g5PT/e+n2/3zp9v986fb/e+n2/3rp - 9v966fb/een2/3jo9f946PX/duj1/3Xo9f916PX/cuj1/2Ti9f9c1fT/Usjz/0+99P9NsfT/UKv0/1Ko - 9f9TpfX/VKT1/1Ol9f9RqPX/Tqv0/0ux8/9JvPP/S8bz/0/T8/9V4PP/YOT0/2Lk9P9g5PT/YOT0/17k - 9P9e5PT/XeT0/1vk9P9b5PP/WePz/1jj8/9X4/P/VOLz/y7c8P8N1u7/Cdbu/wjV7v8H1e3/BdXt/wPV - 7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8d3PL/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/wjW7v8AiKX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCzzv8A1O3/ANTt/wDU - 7f8A0uv/ANLq/wDR6f8Az+f/AM7m/wDN5f8AzOT/AMvj/wDJ4f8AyOD/AMjf/wDH3v8Axdz/AMTb/wDD - 2f8Awtn/AMLZ/wDD2f8Aw9r/AMXc/wDH3v8AyOD/AMri/wDN5f8Az+f/ANLq/wDU7f8A1O3/NOH3/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pl+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXV - 7f8X2O//Gdjv/xrY7/8Su9P/AYek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0bd - 8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/SOP0/0bg8v9H4PL/SODy/0ng8v9K4PL/S+Hy/0zh8v9N4fL/TuHz/0/h - 8/9P4fP/UOHz/1Hi8/9S4vP/UuLz/1Pi8/9U4vP/VOLz/1Xi8/9W4/P/VuPz/1bj8/9X4/P/WOPz/1jj - 8/9Z4/P/WePz/1nj8/9Z4/P/WePz/1nj8/9a4/P/WuPz/1rj8/9a4/P/WuPz/1rj8/9a4/P/WePz/1nj - 8/9Z4/P/WePz/1nj8/9Z4/P/WOPz/1jj8/9X4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9U4vP/U+Lz/1Li - 8/9S4vP/UeLz/1Dh8/9P4fP/T+Hz/07h8/9N4fL/TOHy/1Di8v9y5/X/f+n2/3/p9v9+6fb/fen2/3zp - 9v986fb/e+n2/3rp9v966fb/eOn1/3Tn9f9j3/T/Vcnz/06x9P9So/T/U6L0/1Oi9P9TovT/U6L0/1Oi - 9P9TovT/U6L0/1Oi9P9TovT/U6L0/1Oi9P9TovT/UqLz/1Ki8/9SovP/UaPz/0ew8v9HxvL/Tdzz/13k - 9P9g5PT/XuT0/17k9P9d5PT/W+Tz/1rj8/9Z4/P/WOPz/1fj8/9W4vP/Qd/y/xLX7v8J1u7/B9Xt/wbV - 7f8E1e3/A9Xt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07o/P8Cwt7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7gCmwv8A1O3/ANTt/wDU - 7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDO5v8AzeX/AM3l/wDM5P8AzeX/AM3l/wDN5v8Azub/AM/n/wDQ - 6P8A0ur/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk - +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8G1e3/GNjv/xrY7/8c2e//Hdnv/xrL4v8FkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Giqj/Oczj/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8v9I4PL/SeDy/0rg8v9L4fL/TOHy/03h8v9O4fP/T+Hz/1Dh - 8/9R4vP/UuLz/1Li8/9T4vP/VOLz/1Ti8/9V4vP/VuPz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WePz/1rj - 8/9a4/P/W+Pz/1vj8/9b4/P/W+Pz/1vj8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk - 8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj8/9a4/P/WePz/1nj8/9Z4/P/WOPz/1fj8/9W4/P/VuPz/1Xi - 8/9U4vP/VOLz/1Pi8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/1rj9P9+6fb/ger2/4Hq9v+A6fb/f+n2/3/p - 9v9+6fb/fOn2/3zp9v976fb/c+f1/1zT9P9PtPL/UqLz/1Kh8/9SofP/UqHz/1Kh8/9RofL/UaHy/1Gh - 8v9RofL/UaHy/1Gh8v9RofL/UaHy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Ch - 8v9GsfL/Rs7y/1fi8/9e5PT/XeT0/1zk9P9b5PP/WuPz/1nj8/9X4/P/V+Pz/1bi8/9P4fP/Gtnv/wnW - 7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zHh9v9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9D5fn/AKvG/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA1Oz/ANTt/wDU - 7f8A1O3/ANPs/wDT7P8A0uv/ANPs/wDT7P8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDP - 6f8Avtn/AKbC/wDH4f8A0er/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/B9Xt/xrY7/8c2e//Hdnv/x/Z7/8g2e//H9Xr/wygu/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wGEov8ntc7/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03n+v9I4PL/SuDy/0vh8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi - 8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj8/9W4/P/V+Pz/1jj8/9Z4/P/WePz/1rj8/9b4/P/W+Pz/1vj - 8/9c5PP/XOTz/13k9P9d5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k - 9P9e5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj - 8/9X4/P/VuPz/1bj8/9V4vP/VOLz/1Ti8/9S4vP/UuLz/2Hl9P+B6fb/g+r3/4Lq9/+B6vb/ger2/3/p - 9v9/6fb/fun2/33p9v946Pb/Xs7z/0+r8f9QoPH/UKDx/1Cg8f9QoPH/UJ/x/1Cf8f9Qn/H/UJ/x/1Cf - 8f9Qn/H/UJ/x/1Cf8f9Qn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/D/T5/w/0+f - 8P9Pn/D/T5/w/0+f8P9IqO//R8nx/1ni8/9c4/T/W+P0/1rj8/9Y4/P/WOPz/1bj8/9V4vP/VOLz/1Hi - 8/8g2vD/CNXu/wfV7f8F1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8+5fn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/NuL3/wCTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqCBANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCz - zf8AiKb/AIOh/wCDof8Ag6H/AIOh/wCKp/8Al7T/AKTA/wCxzP8Avtj/AMrk/wDU7P8A1O3/ANTt/wDU - 7f8A1O3/POT5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zji+P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wbV7f8b2e//Hdnv/x/Z7/8g2e//Idrv/yPa7/8k2vD/FbTO/wGEo/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xafuv9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5ff/SuDy/0zh8v9N4fL/TuHz/0/h8/9Q4fP/UeLz/1Li - 8/9T4vP/VOLz/1Xi8/9W4/P/VuPz/1fj8/9Y4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XeT0/17k - 9P9e5PT/XuT0/1/k9P9f5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk - 9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/W+Pz/1vj - 8/9a4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/2fl9P+C6vb/g+r2/4Lq9v+B6fb/gen2/3/p - 9v9/6fb/fun2/3zp9v9m1vT/Uarw/0+e8P9PnvD/T57w/0+e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e - 8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06d7/9One//Tp3v/06d - 7/9One//Tp3v/06d7/9One//TZ3v/02d7/9Kp+//StDy/1jj8/9Y4/T/V+Pz/1bi8/9U4vP/U+Lz/1Li - 8/9R4vP/T+Hz/yXb8P8H1e3/BtXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S+j7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/ynb8P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgOwDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC2 - 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/AIyq/wCa - t/8AudP/ANLq/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P844vj/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8E1e3/HNnv/x7Z7/8g2e//Idrv/yPa7/8k2vD/Jtrw/yjb8P8gx97/BYyp/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY+s/z7T6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOP0/03h8v9N4fL/T+Hz/0/h8/9R4vP/UuLz/1Pi - 8/9U4vP/VeLz/1bj8/9W4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/XOTz/13k9P9e5PT/XuT0/1/k - 9P9g5PT/YOT0/2Dk9P9h5PT/YeT0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9j5fT/Y+X0/2Pl - 9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k - 9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Y4/P/V+Pz/2bm9P+C6vb/gur2/4Lq9v+B6vb/ger2/3/p - 9v9/6fb/fun2/3jk9f9Xt/H/TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d - 7/9Nne//TJzu/0yc7v9Mne7/VJ/r/1qh6f9couj/WqHp/1Sf6/9Mne7/TJzu/0yc7v9MnO7/TJzu/0yc - 7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9Js+//Ut3z/1Tj8/9U4vP/U+Lz/1Hi - 8/9Q4vP/T+Hz/07h8/9L4fP/Idrw/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Dtfv/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cxd3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCA - gAIA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDA - 2/8AhaP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCZtf885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/A9Xt/x7Z7/8f2e//Idrv/yPa7/8k2vD/Jtrw/yfb8P8p2/D/K9vw/yjU - 6f8Mm7b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ChaP/Lr3V/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03h8v9O4fP/T+Hz/1Hi8/9S4vP/U+Lz/1Ti - 8/9V4vP/VuPz/1fj8/9Y4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Dk - 9P9h5PT/YuX0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl - 9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl9P9k5fT/ZOX0/2Pl9P9j5fT/YuX0/2Ll9P9h5PT/YeT0/2Dk - 9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/2bl9P+D6vb/g+r2/4Lq9v+C6vb/ger2/4Dq - 9v9/6vb/f+r2/27X8/9Po+//TJzu/0yc7v9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0uc - 7f9gouX/fK3b/5i20f+pvcv/usPG/73ExP+9xMT/vcTE/73ExP+9xMT/usPG/6q9y/+XttH/fK3b/2Ci - 5f9LnO3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprs/0ih7P9L0vH/UuLy/1Hi - 8v9Q4vL/T+Ly/03h8v9N4fL/S+Hy/0nh8v8b2O//BtXt/wTV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd - 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DqK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oaAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDI - 4/8Aiaj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Otzx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrk+P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb8P8p2/D/Ktvw/yzb - 8P8t3PD/Ltvv/xivyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8bpsD/S+T4/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/n+v9P4fP/UOHz/1Li8/9S4vP/VOLz/1Ti - 8/9W4/P/VuPz/1jj8/9Z4/P/WuPz/1vj8/9c5PP/XeT0/17k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll - 9P9i5fT/Y+X0/2Tl9P9l5fT/ZeX0/2Xl9P9m5fT/ZuX0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl - 9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2bl9P9l5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Pl - 9P9i5fT/YuX0/2Hk9P9g5PT/YOT0/17k9P9e5PT/XeT0/2Xl9P+C6vb/g+r2/4Pq9v+C6vb/ger2/4Dq - 9v+A6fb/f+n2/2bL8v9Km+3/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/X6Ll/4qy - 1v+xwMj/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/scDI/4uy1f9fouT/SZnr/0mZ6/9Jmev/SZnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/R5rr/0bG - 7/9P4fL/TuHy/03h8v9L4fL/SuDy/0ng8v9H4PL/RODy/xXX7v8F1e3/BNXt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f804ff/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKJVAAAAAADP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDN5v8Azeb/AM3m/wDN5v8Azub/AM3m/wDK - 4f8Ajqz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/zbT6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5fn/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/H9nv/yLa7/8k2vD/Jdrw/yfb8P8p2/D/Ktvw/yvb - 8P8t3PD/L9zw/zDc8P8y3PH/JMPZ/wSJp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wyT - sP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5ff/UeLz/1Li8/9T4vP/VOLz/1Xi - 8/9W4/P/WOPz/1nj8/9a4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Hk9P9i5fT/Y+X0/2Tl - 9P9l5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2jm9P9p5vT/aeb0/2rm9P9q5vT/aub0/2rm - 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9p5vT/aeb0/2jm9P9o5vT/Z+X0/2fl9P9n5fT/ZuX0/2Xl - 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/YOT0/2Pl9P+B6vb/hOr3/4Pq9v+C6vb/ger2/4Hq - 9v+A6vb/fur2/1/A8f9Imev/SJnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/SJnr/0iY6/9iouL/nbjP/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/vsXF/77F - xf++xcX/v8bG/7/Gxv+/xsb/wMbG/6C5z/9ioeL/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX - 6v9Gl+n/RLru/0vg8v9L4fL/SeHy/0jg8v9H4PL/RuDy/0Tg8v893vL/Ddbt/wTV7f8C1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrY7f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H0AICqDAAAAAAAzOT/AMvj/wDK4v8AyOD/AMjf/wDH3v8Ax97/AMjg/wDK4v8AzeX/AM/n/wDN - 5v8Aka//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xy+L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Q+X5/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x3Z7/8k2vD/Jdrw/yba8P8p2/D/Ktvw/yvb - 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f8v0ef/C5Wx/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/BIil/zPF3P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/UeT1/1Li8/9U4vP/VeLz/1bj - 8/9X4/P/WePz/1nj8/9b4/P/W+Pz/13k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl - 9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/a+b1/2vm9f9s5vX/bOb1/2zm - 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/a+b1/2vm9f9r5vX/aub0/2rm9P9q5vT/aeb0/2jm - 9P9n5fT/Z+X0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Pl9P976fb/hOr3/4Pq9/+D6vf/gur3/4Dp - 9v+A6fb/f+n2/13A7/9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Gl+n/Rpfp/1Gb5f+QstL/vMTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xcX/vsXF/77Fxf+/xsb/v8bG/7/Gxv/Ax8f/wMfH/8DH - x//Bx8f/wcjI/8HIyP/CyMj/wsnJ/8LJyf/Dycn/wsnJ/5W21P9SmuX/RZbp/0WW6f9Flun/RZbp/0WV - 6P9Flej/RZXo/0WV6P9Auez/SODy/0fg8v9G4PL/Rd/y/0Pf8v9C3/L/Qd/y/zLc8P8G1e3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Ddfw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8mudL/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhogAAAAAAAAAAAM3l/wDN5f8Azeb/AM/n/wDR6f8A0uv/ANTt/wDU7f8A1O3/ANTt/wDQ - 6P8AlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/LMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn - +v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8c2e//JNrw/yba8P8o2/D/Kdvw/yvb - 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f823fH/N93x/zfc7v8YqcH/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Ia/I/03n+f9P6fz/T+n8/0/p/P9P6fz/T+n8/1Pi8/9U4vP/VuPz/1bj - 8/9Y4/P/WePz/1rj8/9b4/P/XeT0/17k9P9f5PT/YOT0/2Hk9P9i5fT/YuX0/2Tl9P9l5fT/ZeX0/2bl - 9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/23m9f9u5/X/buf1/27n - 9f9u5/X/b+f1/2/n9f9v5/X/b+f1/27n9f9u5/X/buf1/27n9f9t5vX/beb1/2zm9f9s5vX/bOb1/2vm - 9f9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P916PX/hOv2/4Tr9v+C6vb/gur2/4Hq - 9v+B6vb/f+r2/2DF8P9Flun/RZbp/0WW6f9Flun/RZbp/0WV6P9Flej/RZXo/2uk3f+zwcf/vcTE/77F - xf++xcX/vsXF/7/Gxv+/xsb/v8bG/8DGxv/Ax8f/wMfH/8HHx//ByMj/wcjI/8LIyP/CyMj/wsnJ/8PJ - yf/Dycn/w8rK/8TKyv/Eysr/xMrK/8XLy//Fy8v/xcvL/8bMzP/GzMz/vcjN/2+m3f9DlOf/Q5Tn/0OU - 5/9DlOf/Q5Tn/0OU5/9DlOf/Q5Tn/z6+7f9F4PL/RODy/0Lf8v9B3/L/QN/y/z7f8v893vL/Itrv/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yPd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/E5u2/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCBo0UAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO - 5v8Ak7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HOAICZCgCAnRoAg6JCAIKhagCD - oZIAg6G7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yjA1/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9M6Pv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/F9jv/yba8P8n2/D/Kdvw/yvb - 8P8s2/D/Ltzw/zDc8P8x3PD/Mtzx/zXd8f823fH/N93x/zne8f873vH/PN7x/yi91P8Ch6T/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbX/Rt3x/0/p/P9P6fz/T+n8/1Dn+v9U4vP/VuPz/1fj - 8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k9P9g5PT/YOT0/2Ll9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2fl - 9P9o5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9v5/X/cOf1/3Dn - 9f9w5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cOf1/3Dn9f9w5/X/b+f1/2/n9f9v5/X/b+f1/27n - 9f9t5vX/bOb1/2zm9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9s5vT/hOr2/4Xq9v+D6vb/g+r2/4Lq - 9v+B6vb/gOr2/2rM8f9ElOf/RJTn/0SU5/9DlOf/Q5Tn/0OU5/9DlOf/RZXm/4ux1P+/xsb/wMbG/8DH - x//Ax8f/wcfH/8HIyP/ByMj/wsjI/8LIyP/Cycn/w8nJ/8PJyf/Dysr/xMrK/8TKyv/Eysr/xcvL/8XL - y//Fy8v/xszM/8bMzP/GzMz/x83N/8fNzf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8nPz//Jz8//lbfW/0SU - 5f9Ck+b/QpPm/0KT5v9Ck+b/QZLm/0GS5v9Bkub/QcTu/0Lf8v9C3/L/QN/y/z/f8v893/L/PN/y/zre - 8f8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f875Pj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/wKFpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoeQAgIACAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDJ - 4/8Aj6z/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8juNH/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPX7v8n2/D/Kdvw/yvb - 8P8s2/D/Ldzw/y/c8P8x3PD/Mtzx/zTd8f823fH/N93x/zne8f863vH/PN7x/z7e8f8/3/H/NtDk/wqR - rf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP85zeP/T+n8/0/p/P9S5vf/VuPz/1jj - 8/9Z4/P/WuPz/1vj8/9d5PT/XuT0/1/k9P9g5PT/YuX0/2Ll9P9k5fT/ZeX0/2bl9P9n5fT/Z+X0/2nm - 9P9q5vT/aub0/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9w5/X/cef1/3Hn9f9x5/X/cuf1/3Ln - 9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9y5/X/cuf1/3Hn9f9x5/X/cef1/3Hn - 9f9w5/X/b+f1/2/n9f9u5/X/beb1/2zm9f9s5vX/a+b1/2rm9P9p5vT/f+r2/4Xq9v+E6vb/g+r2/4Lq - 9v+C6vb/ger2/3DO5f9CleX/QpPm/0KT5v9Ck+b/QpPm/0KT5v9Ck+b/SJXk/5250P/CyMj/wsjI/8LJ - yf/Dycn/w8nJ/8PKyv/Eysr/xMrK/8TKyv/Fy8v/xMrK/8LJyf/Ax8f/v8bG/77Fxf+9xMT/vcTE/73E - xP++xcX/v8bG/8HIyP/Fy8v/yM3N/8nPz//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8vR0f/M0dH/zNHR/8zS - 0v+qwtb/SJXj/0CR5P9AkeT/QJHk/0CR5P9AkeT/QJHk/0CU5f9A0e//P9/x/z3e8f883vH/PN7x/zne - 8f843fH/Kdvw/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8F1u7/Tej8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrP5f8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDG - 3/8Ai6n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HpAIahJgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Gq7I/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zXH3v8DmbX/AJOw/wCmwf8Azuf/ANTt/wDU7f8A1O3/ANTt/wDU7f8O1u7/KNvw/yrb - 8P8r2/D/Ldzw/y/c8P8w3PD/Mtzx/zTd8f813fH/N93x/zne8f863vH/PN7x/z3e8f8/3/H/Qd/y/0Lf - 8v8/2u7/F6K8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/ye2z/9O6Pv/VOX1/1jj - 8/9Z4/P/W+Pz/1zk8/9e5PT/XuT0/2Dk9P9h5PT/YuX0/2Pl9P9l5fT/ZeX0/2fl9P9n5fT/aeb0/2rm - 9P9r5vX/bOb1/2zm9f9t5vX/b+f1/2/n9f9w5/X/cef1/3Hn9f9y5/X/cuf1/3Pn9f9z5/X/c+f1/3To - 9f906PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dOj1/3To9f9z5/X/c+f1/3Pn - 9f9z5/X/cuf1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m9f9s5vX/dej2/4Xr9v+F6/b/hOv2/4Pq - 9v+C6vb/ger2/37l9f84dqz/OYHM/0CR5f9AkeX/QJHl/0CR5f9AkeX/SJTk/6m+z//Eysr/xMrK/8XL - y//Fy8v/xcvL/8bMzP/GzMz/xszM/8fNzf/Eysr/wMbG/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/x83N/83S0v/N0tL/zdPT/87T0//O09P/ztTU/8/U - 1P/P1NT/z9TU/7fI1v9GleL/PpDj/z6Q4/8+kOP/PpDj/z6Q4/8+j+P/P5rl/z/a8P873vH/Ot7x/zne - 8f833vH/Nt7x/zDc8P8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H9AICjJAAAAAAAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC5 - 0/8AhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfLQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISiNACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKguv9P6fz/T+n8/0/p - /P9P6fz/Rt3x/xegu/8Ag6H/AIOh/wCDof8Ag6H/AJaz/wDT7P8A1O3/ANTt/wDU7f8A1O3/CNXu/ynb - 8P8r2/D/Ldzw/y7c8P8w3PD/Mtzx/zPd8f813fH/N93x/zjd8f853vH/PN7x/z3e8f8+3vH/QN/y/0Lf - 8v9D3/L/ReDy/0bg8v8ot87/AYSj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fp+6/1Db - 7v9Z4/P/W+Pz/1zk8/9e5PT/X+T0/2Dk9P9i5fT/YuX0/2Tl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm - 9f9s5vX/beb1/27n9f9v5/X/b+f1/3Hn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/duj1/3bo - 9f926PX/duj1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3bo9f926PX/duj1/3bo - 9f916PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn9f9v5/X/b+f1/4Pq9v+F6vf/her3/4Tq - 9v+D6vb/gen2/3LV+f9Zr+H/JleJ/zBurf8/kOP/P5Dj/z+Q4/8/kOP/QZLj/6W90f/GzMz/x83N/8fN - zf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8bMzP+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/y9HR/9DV1f/Q1dX/0dbW/9HW - 1v/R1tb/0tfX/9LX1//S19f/s8fX/0GQ4v89juL/PY7i/z2O4v89juL/PY7i/z2O4v9Asej/Od7x/zjd - 8f833fH/Nd3x/zPd8f8t2/D/Jtrw/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zPi9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/C5Gu/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOitwAAAAAAAAAAAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTs/wCp - xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAISiNAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoRsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj63/T+n8/0/p - /P9O6Pv/Lb3V/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Awtz/ANTt/wDU7f8A1O3/ANTt/wPV - 7f8r2/D/LNvw/y3c8P8w3PD/Mdzw/zLc8f813fH/Nt3x/zfd8f853vH/O97x/zze8f8+3vH/QN/y/0Hf - 8v9D3/L/ReDy/0bg8v9I4PL/SeDy/zrM4P8Ijar/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Lj6v/TtXn/13k9P9e5PT/X+T0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2zm - 9f9s5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9z5/X/c+f1/3To9f916PX/duj1/3bo9f936PX/d+j1/3jo - 9f946PX/eOj1/3no9v956Pb/eej2/3no9v966fb/eun2/3rp9v956Pb/eej2/3no9v956Pb/eOj1/3jo - 9f946PX/d+j1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Pn9f9x5/X/cef1/3jo9v+G6/f/hev3/4Tq - 9/+E6vf/gur2/3/l9/9au/z/M3q7/yRVh/8mWY3/PY7g/z2P4v89juL/PY7i/5K11v/Jzs7/yc/P/8nP - z//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Hzc3/0tfX/9PY - 2P/U2Nj/1NnZ/9TZ2f/V2dn/1dra/9Xa2v+fvdr/O4zg/zuM4P87jOD/O4zg/zuM4P87jOD/PIzg/z3M - 7f823fH/Nd3x/zPd8f8x3fH/Kdvw/ynb8P8P1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f9K5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qtjt/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoUwAAAAAAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/AMvk/wCZ - tf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfNQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/07p - /P9C2O3/Epq1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/J9vw/y3c8P8v3PD/MNzw/zLc8f803fH/Nd3x/zfd8f853vH/Ot7x/zze8f8+3vH/P9/x/0Hf - 8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg8v9N4fL/SNns/xWct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/w+Srv9d4/P/YOT0/2Hk9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm - 9f9u5/X/b+f1/3Dn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3bo9f926PX/d+j1/3jo9f946PX/eej2/3no - 9v966fb/e+n2/3vp9v976fb/e+n2/3vp9v986fb/fOn2/3zp9v986fb/fOn2/3vp9v976fb/e+n2/3vp - 9v976fb/eun2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3bo9f906PX/c+f1/3Pn9f+E6vb/h+v3/4br - 9/+F6/f/hOv3/4Lq9/9u0fn/SbT8/zKCw/8kVIb/I1SG/zR7xf87jeH/O43h/3Cl2//L0ND/y9HR/8vR - 0f/M0dH/zNLS/83S0v/N0tL/zNHR/8HHx/+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/8LJ - yf/U2Nj/1tvb/9fb2//X29v/19zc/9jc3P/Y3Nz/2Nzc/3us3f85i9//OYvf/zmL3/85i9//OYvf/zmL - 3/89nuT/Ndzx/zPc8f8x3PH/MNzx/yfb8P8n2/D/INnv/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8X2/H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq5 - 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDod4AgIACAAAAAAAAAAAAAAAAAAAAAADU7f8A1O3/ALfR/wCI - pv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HiAIafKAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8jtc3/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wDO5v8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yDZ7/8u3PD/MNzw/zLc8f8z3fH/Nd3x/zfd8f843fH/Ot7x/zze8f893vH/P9/x/0Hf - 8v9C3/L/RN/y/0Xg8v9H4PL/SeDy/0rg8v9M4fL/TeHy/0/h8/9Q4fL/KLHJ/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/TtLl/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9q5vT/bOb1/23m - 9f9v5/X/b+f1/3Hn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f946PX/eOj1/3no9v966fb/e+n2/3vp - 9v986fb/fOn2/33p9v996fb/fen2/33p9v9+6fb/fun2/37p9v9+6fb/fun2/37p9v9+6fb/fen2/33p - 9v996fb/fen2/3zp9v976fb/e+n2/3vp9v966fb/eej2/3jo9f936PX/duj1/3bo9f956fX/h+r2/4fq - 9v+F6vb/her2/4Pq9v+C6fb/V7r7/z+v/P85oen/I1OF/yNThf8qZaP/Oovf/0eS3//CzdT/ztPT/87T - 0//O1NT/z9TU/8/U1P/P1NT/zdLS/7/Gxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/wMfH/9Xa2v/Z3d3/2t7e/9re3v/a3t7/297e/9vf3//S2+D/S5Tf/ziK3v84id7/OIne/ziJ - 3v84id7/OYne/znE7P8w3PD/L9zw/y3c8P8k2vD/JNrw/yTa8P8H1e3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAzOb/AJ26/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAISeHQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqHXAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Aoak/wC61f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8W2O//L9zw/zHc8P8y3PH/Nd3x/zbd8f843fH/Od7x/zve8f893vH/Pt7x/0Df - 8v9C3/L/Q9/y/0Xg8v9H4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/UuLz/1Ti8/88x9v/Boqn/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/1LV5/9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/bOb1/27n - 9f9v5/X/cOf1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo9f946PX/eun2/3vp9v976fb/fOn2/33p - 9v996fb/fun2/37p9v9/6fb/f+n2/3/p9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/3/p - 9v9/6fb/f+n2/3/p9v9+6fb/fen2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f946PX/gOr2/4jr - 9v+G6/b/her2/4Tq9v+D6vb/ddr4/0iz/P8+r/z/Pq/8/yZlnP8hUoT/IlSH/zeH2f+cvNr/0NXV/9DV - 1f/R1tb/0dbW/9HW1v/S19f/0NXV/8DGxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+6t7H/spqD/6yDYf+peFH/pm9D/6dxRv+qe1X/rYho/7Sh - kP+7vrv/vcTE/73ExP/Ax8f/2d3d/9zg4P/d4OD/3eDg/93h4f/e4eH/3uHh/7DJ4P82iNz/Nojc/zaI - 3P82iNz/Nojc/zaI3P85m+H/Ltzw/y3b8P8q2/D/Idrv/yHa7/8h2u//E9fu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9H3/P/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6HzAICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAIek/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKG8AICcEgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/E5y2/0PZ7v8U2fH/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/DNbu/zDc8P8y3PH/NN3x/zXd8f833fH/Od7x/zre8f883vH/Pt7x/0Df - 8v9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9L4fL/TeHy/0/h8/9Q4fP/UuLz/1Ti8/9V4vP/VuPz/03X - 6f8SlrL/AIOh/wCDof8Ag6H/AIOh/xWXs/9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9r5vX/bOb1/27n - 9f9v5/X/cef1/3Ln9f9z5/X/dOj1/3bo9f926PX/eOj1/3jo9f966fb/e+n2/3vp9v996fb/fen2/37p - 9v9/6fb/f+n2/4Dq9v+B6vb/ger2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq - 9v+C6vb/gur2/4Lq9v+B6vb/gOr2/3/p9v9/6fb/f+n2/33p9v996fb/fOn2/3vp9v976fb/eej2/4bq - 9v+H6/b/huv2/4Xr9v+D6vb/g+r2/1/C+f8/r/z/Pq/8/z6v/P8xh8f/IVKE/yBRhP9IgLn/0NbY/9LX - 1//T2Nj/09jY/9TY2P/U2Nj/1NnZ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/7izqv+sgmH/pGY2/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pmo7/7CRdv+7vbn/vcTE/8bMzP/f4uL/3+Pj/+Dj4//g4+P/4OPj/+Dk5P/g4+T/Y6De/zWH - 2/81h9v/NYfb/zSG2/80htv/NYbb/zHL7P8q2/D/Jtvw/x7Z7/8e2e//Htnv/xzZ7/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Lb3U/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAKqqAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoJcAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiaf/Lr/W/0/p/P9P6fz/Qub5/wLV - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f8x3PD/M93x/zXd8f833fH/ON3x/zne8f883vH/Pd7x/z/f - 8f9B3/L/Q9/y/0Tf8v9F4PL/SODy/0ng8v9K4PL/TeHy/07h8/9P4fP/UuLz/1Pi8/9U4vP/VuPz/1jj - 8/9Z4/P/WeHw/yquxv8GiKb/AYSi/x+iu/9Y2+v/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Ln9f9z5/X/dej1/3bo9f936PX/eOj1/3rp9v976fb/fOn2/33p9v996fb/f+n2/3/p - 9v+A6vb/ger2/4Lq9v+C6vb/g+r2/4Pq9v+E6vb/hOr2/4Tq9v+E6vb/her2/4Xq9v+F6vb/her2/4Tq - 9v+E6vb/hOr2/4Tq9v+E6vb/g+r2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/f+n2/37p9v996fb/fOn2/37p - 9v+I6/f/h+r3/4bq9v+F6vb/her2/4Dp9v9KtPv/Pq/8/z6v/P8+r/z/O6bx/yBShf8gUIP/b4KU/9XZ - 2f/V2tr/1tra/9ba2v/W2tr/19vb/8zS0v+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E - xP+9xMT/vcTE/77ExP++xcX/vcLB/66KbP+kZjb/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pWo8/7Oei/+9xMT/0tfX/+Ll5f/i5eX/4+bm/+Pm5v/j5ub/5Obm/77S - 5P8zhdr/M4Xa/zOF2v8zhdr/M4Xa/zOF2v8zreT/KNrw/yLZ8P8b2e//G9nv/xvZ7/8b2e//BdXt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f804vf/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xKatf8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCAoyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofUAgqJaAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqKBAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/R97y/0/p/P9P6fz/T+n8/0/p - /P8k3vT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ktvw/zTd8f813fH/N93x/zne8f873vH/PN7x/z7e - 8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TOHy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1fj - 8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Hk9P9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3bo9f946PX/eej2/3vp9v976fb/fen2/33p9v9/6fb/f+n2/4Hq - 9v+C6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr9/+H6/f/h+v3/4fr9/+H6/f/h+v3/4fr - 9/+H6/f/h+v3/4fr9/+G6/b/huv2/4Xq9v+E6vb/hOr2/4Pq9v+C6vb/gur2/4Hq9v+A6vb/f+n2/37p - 9v+D6vb/iev3/4jr9/+G6vf/her2/4Tq9v9z2vj/Q7H8/z6v/P8+r/z/Pq/8/z6v/P8obqj/LViD/36C - g//FyMj/2Nzc/9jc3P/Y3d3/2d3d/9fb2/+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/77F - xf+/xcX/v8bG/8DHx//Bx8f/wMG+/6p5Uv+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/r49z/7/Gxv/h5OT/5ejo/+Xo6P/m6Oj/5unp/+bp - 6f/l6er/V5nd/zGE2P8xhNj/MYTY/zGE2P8xhNj/M43b/yfb8P8f2e//Gdjv/xnY7/8Z2O//Gdjv/wzW - 7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0bd8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocYAg58lAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh8gCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/weMqf81x97/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/S+j7/wjX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/Z7/813fH/N93x/zjd8f863vH/PN7x/z7e - 8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UOHz/1Li8/9U4vP/VeLz/1fj - 8/9Z4/P/WuPz/1zk8/9e5PT/X+T0/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v986fb/fen2/3/p9v9/6fb/ger2/4Lq - 9v+C6vb/hOr2/4Tq9v+F6vb/huv2/4fr9/+H6/f/iOv3/4jr9/+J6/f/iev3/4nr9/+J6/f/iev3/4nr - 9/+J6/f/iev3/4nr9/+J6/f/iOv3/4jr9/+H6/f/h+v3/4br9v+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq - 9v9/6fb/h+r3/4jr9/+H6/f/huv3/4Xr9/+E6vb/YMb3/z6v/P8+r/z/Pq/8/z6v/P8+r/z/NZXa/1Vs - g/+ChIT/oqWl/9ve3v/b39//29/f/9zf3//L0dH/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/v8XF/8DG - xv/Bx8f/wcjI/8LJyf/Dycn/w8nI/61/W/+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+ymIH/0NXV/+fq6v/o6ur/6Ovr/+jr - 6//p6+v/6ezs/6bF4v8wgtf/MILX/zCC1/8vgtf/L4LX/y+C1/8ny+z/G9nv/xbY7/8W2O//Ftjv/xbY - 7/8Q1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8ru9P/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ84AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoHkAqqoDAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnyAAhKG4AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/x6qxP9K4vb/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P814vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8S1+7/Nt3x/zfd8f853vH/O97x/zze - 8f8+3vH/QN/y/0Lf8v9D3/L/ReDy/0fg8v9J4PL/SuDy/03h8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1bj - 8/9Y4/P/WuPz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p9v+A6vb/gur2/4Lq - 9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iev3/4rr9/+K6/f/i+v3/4vr9/+L6/f/i+v3/4vr - 9/+L6/f/i+v3/4vr9/+L6/f/i+v3/4vr9/+K6/f/iev3/4nr9/+I6/f/h+v3/4fr9/+G6/b/hOr2/4Tq - 9v+C6vb/gur2/4nr9/+J6/f/iOv3/4fr9/+F6/f/hOv3/062+P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z6v - +/9uhZf/hIaG/4WHh//T19f/3uHh/97h4f/c4OD/v8XF/73ExP+9xMT/vcTE/73ExP+/xsb/wMfH/8HI - yP/Cycn/w8rK/8TKyv/Fy8v/xszM/7KRdP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGY1/7eqm//n6ur/6+3t/+vt - 7f/r7e3/7O7u/+zu7v/c5Ov/LoHW/y6A1v8ugNb/LoDW/y6A1v8ugNb/KbTl/xjY7v8T1+7/E9fu/xPX - 7v8T1+7/Etfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCDof8Ag6H/AIOitwCGpCoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCEonAAg6H0AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Gu/zrO5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ8f8A1O3/ANTt/wDU7f8A1O3/BNXt/zbd8f843fH/Ot7x/zze - 8f8+3vH/P9/x/0Hf8v9D3/L/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Hi8/9S4vP/VOLz/1bj - 8/9Y4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/beb1/2/n - 9f9w5/X/cef1/3Pn9f916PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fun2/3/p9v+B6vb/gur2/4Pq - 9v+E6vb/huv2/4fr9/+H6/f/iev3/4nr9/+K6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+O7Pf/juz3/47s - 9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iuv3/4nr9/+J6/f/h+v3/4fr - 9/+F6vb/hOr2/4Tq9v+K6/f/iev3/4fq9/+G6vf/her3/4Dp9/9Esvv/Pq/8/z6v/P8+r/z/Pq/8/z6v - /P8+r/z/XZ3H/4WHh/+Fh4f/r7Gx/+Dj4//g5OT/0tfX/73ExP+9xMT/vcTE/77Fxf/Axsb/wcjI/8PJ - yf/Eysr/xcvL/8bMzP/Hzc3/yM7O/8C1qf+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/oWMz/25E - I/8/JxT/JxgM/y0cDv9LLhj/gU8p/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+nbkL/19jV/+3v - 7//t7+//7vDw/+7w8P/u8PD/7/Dx/1CU2f8sf9T/LH/U/yx/1P8sf9T/LH/U/yub3P8T2O7/ENfu/xDX - 7v8Q1+7/ENfu/xDX7v8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW7/9O6Pz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/RNrv/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISgnwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOh/wCDof8AgqDVAIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDockAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhaL/JLHL/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/AtTt/wDU7f8A1O3/ANTt/wDU7f8q2/D/Od7x/zve - 8f883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9H4PL/SeDy/0rg8v9N4fL/TuHz/1Dh8/9S4vP/VOLz/1Xi - 8/9X4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9h5PT/YuX0/2Xl9P9m5fT/Z+X0/2rm9P9r5vX/bOb1/2/n - 9f9w5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq - 9v+F6vb/h+v3/4fr9/+J6/f/iev3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/4/s9/+P7Pf/kOz3/5Ds - 9/+Q7Pf/kOz3/5Ds9/+Q7Pf/kOz3/5Ds9/+P7Pf/j+z3/47s9/+O7Pf/jez3/4zs9/+L6/f/iuv3/4nr - 9/+I6/f/h+v3/4br9v+H6vb/iuv2/4jr9v+H6/b/hur2/4Xq9v933/f/QbD8/z6v/P8+r/z/Pq/8/z6v - /P8+r/z/Pq/8/0Gt9v+Di5D/h4mJ/42Pj//e4eH/4+bm/8bMzP+9xMT/vcTE/7/Gxv/Bx8f/wsnJ/8TK - yv/Fy8v/x8zM/8jOzv/Jz8//ys/P/8vQ0P+uf1r/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/jVct/x0S - Cf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP88JRP/nmEy/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7id - hv/w8fH/8PLy//Hy8v/x8vL/8fPz//Lz8/+Ot9//K37T/yt+0/8rftP/Kn3T/yp90/8rh9b/D9bu/w3W - 7v8N1u7/Ddbu/w3W7v8N1u7/BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8i3vT/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yi3z/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoXIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDodMAgqFcAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkpIHAIKigwCDofkAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8PlrL/P9Tq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yXe9P8A1O3/ANTt/wDU7f8A1O3/Gtjv/zne - 8f883vH/Pt7x/z/f8f9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/U+Lz/1Ti - 8/9W4/P/WOPz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n - 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq - 9v+G6/b/h+v3/4jr9/+J6/f/i+v3/4vr9/+M7Pf/juz3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Lt - 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Hs9/+Q7Pf/kOz3/4/s9/+O7Pf/jez3/4zs - 9/+L6/f/iuv3/4nr9/+I6/f/iev3/4rr9v+J6/b/h+v2/4br9v+F6vb/bNT3/0Cw/P8+r/z/Pq/8/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/ZZvA/4mKiv+Jior/ury8/+Xo6P++xcX/vsXF/8DGxv/ByMj/w8nJ/8XL - y//GzMz/yM7O/8nPz//K0ND/zNHR/83S0v/JyMP/pGY1/6RlNP+kZTT/pGU0/6RlNP+kZTT/ilUs/wsH - A/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/yUXDP+hYzP/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+pd0//8fPz//P09P/z9PT/8/X1//T19f/09fX/u9Lo/yl80v8pfNL/KXzS/yl80v8pfNL/K33S/w7R - 7P8L1u7/C9bu/wvW7v8L1u7/C9bu/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/P+T5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq7/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaNFAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIKh1wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOGpP8quND/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdbv/wDU7f8A1O3/ANTt/wrW - 7v873vH/PN7x/z7e8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0ng8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti - 8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9l5fT/Z+X0/2jm9P9q5vT/bOb1/23m - 9f9v5/X/cef1/3Ln9f906PX/duj1/3fo9f946PX/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq - 9v+G6/b/h+v3/4nr9/+K6/f/i+v3/4zs9/+O7Pf/juz3/5Ds9/+Q7Pf/kez3/5Lt9/+T7ff/k+33/5Tt - 9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt+P+V7fj/lO33/5Pt9/+T7ff/k+33/5Lt9/+R7Pf/kOz3/4/s - 9/+O7Pf/jez3/4vr9/+L6/f/iev3/4rr9/+L7Pf/iev2/4jr9v+G6/b/hev2/13E9f8+r/z/Pq/8/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/0es8f+JjI7/iouL/5KTk//d4OD/vsXF/8DGxv/CyMj/xMrK/8bM - zP/Hzc3/yc7O/8rQ0P/M0dH/zdLS/87T0//Q1dX/v6mW/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/y4c - Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Xjoe/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/pGU0/+Pf2v/19vb/9vf3//b39//29/f/9/j4/9rm8P8ne9D/J3vQ/yd70P8ne9D/J3vQ/yd7 - 0P8Qw+j/CNXu/wjV7v8I1e7/CNXu/wjV7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWmFwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApA4AhKGVAIOh/QCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xScuP9D2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbi9/8A1O3/ANTt/wDU - 7f8A1O3/Nd3x/z3e8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UeLz/1Pi - 8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/23m - 9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq - 9v+H6/f/iOv3/4nr9/+K6/f/i+v3/43s9/+O7Pf/j+z3/5Ds9/+R7Pf/k+33/5Pt9/+U7ff/le34/5Xt - +P+W7fj/lu34/5ft+P+X7fj/l+34/5ft+P+X7fj/l+34/5bt+P+V7fj/le34/5Xt+P+U7ff/k+33/5Lt - 9/+R7Pf/kOz3/47s9/+O7Pf/jOz3/4vr9/+K6/f/iuv3/4nr9/+H6vb/hur2/4Tq9v9TvfX/Pq/8/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/bpy7/4yNjf+MjY3/sbW1/8DGxv/CyMj/xMrK/8bM - zP/Izc3/yc/P/8vR0f/N0tL/ztPT/9DV1f/R1tb/0tfX/7aSdP+kZTT/pGU0/6RlNP+kZTT/pGU0/4ZS - Kv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xILBv+jZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP/Qvq7/+Pn5//n5+f/5+fn/+fr6//r6+v/u8/f/JnnP/yZ5z/8mec//JnnP/yV5 - z/8lec//ELvl/wXV7f8F1e3/BdXt/wXV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf - 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ia7H/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh3wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEok0Ag6HjAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/BYqn/zDB2P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ftrx/wDU - 7f8A1O3/ANTt/yLa7/8+3vH/QN/y/0Hf8v9D3/L/ReDy/0fg8v9I4PL/SuDy/0zh8v9O4fP/UOHz/1Li - 8/9U4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl9P9p5vT/aub0/2zm - 9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3vp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq - 9v+H6/f/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Hs9/+T7ff/k+33/5Xt+P+V7fj/lu34/5ft - +P+Y7vj/mO74/5nu+P+Z7vj/me74/5ru+P+Z7vj/me74/5nu+P+Y7vj/mO74/5ju+P+W7fj/le34/5Xt - +P+U7ff/k+33/5Hs9/+Q7Pf/j+z3/47s9/+M7Pf/i+v3/4rr9/+J6/f/iOv3/4bq9v+F6vb/Tbj1/z6v - /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/06v8P+LkJL/jo6O/2JkZP+Dh4f/xMrK/8bM - zP/Izs7/ys/P/8zR0f/O09P/z9TU/9HV1f/S19f/1NjY/9XZ2f+xg1//pGU0/6RlNP+kZTT/pGU0/6Rl - NP9hPB//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8CAgL/BQUF/wYGBv8FBQX/kFku/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/yK2W//v7+//7/Pz/+/z8//z8/P/8/Pz/+/z9/yR4zv8keM7/JHjO/yR4 - zv8keM7/JHjO/w+14v8C1O3/AtTt/wLU7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU - 7f9F5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef7/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oKcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWmFwCDoKcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/GaO+/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm - +f8C1O3/ANTt/wDU7f8M1u7/Pt7x/0Df8v9C3/L/RN/y/0Xg8v9I4PL/SeDy/0vh8v9N4fL/T+Hz/1Hi - 8/9S4vP/VOLz/1bj8/9Y4/P/WePz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2bl9P9n5fT/aub0/2vm - 9f9t5vX/b+f1/3Hn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+C6vb/g+r2/4Tq - 9v+G6/b/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/lO33/5Xt+P+W7fj/mO74/5ju - +P+Z7vj/mu74/5ru+P+b7vj/m+74/5zu+P+c7vj/nO74/5vu+P+b7vj/mu74/5ru+P+Z7vj/mO74/5ju - +P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs9/+L6/f/iev3/4jr9/+H6/f/hOr2/0u1 - 9f8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbL8/0iz/P9KtPz/cqC//4+QkP8lJib/HB0d/8TK - yv/Izs7/ys/P/8zR0f/O09P/0NXV/9HW1v/T2Nj/1dnZ/9ba2v/Y3Nz/r3xV/6RlNP+kZTT/pGU0/6Rl - NP+kZTT/VzYc/wAAAP8AAAD/AAAA/wwMDP8WFhb/GRkZ/xwcHP8gICD/IyMj/yYmJv8pKSn/LCws/5ds - S/+maTn/pGU0/6RlNP+kZTT/pGU0/8Ojiv/9/v7//v7+//7+/v/+//////////////8re8//InbN/yJ2 - zf8ids3/InbN/yJ2zf8NsOH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfP5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6FvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIShXwCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ijar/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/J971/wDU7f8A1O3/ANTt/zTd8f9B3/L/Q9/y/0Tf8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h - 8/9S4vP/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2rm - 9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Xo9f936PX/eOj1/3rp9v986fb/fen2/3/p9v+B6vb/gur2/4Tq - 9v+G6/b/h+v3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt+P+Y7vj/mO74/5ru - +P+a7vj/m+74/5zu+P+c7vj/ne74/57v+P+e7/j/nu/4/57v+P+d7vj/ne74/5zu+P+c7vj/m+74/5ru - +P+a7vj/mO74/5ju+P+W7fj/le34/5Pt9/+S7ff/kOz3/4/s9/+O7Pf/jOz3/4vr9/+J6/f/h+v3/4br - 9v9Mt/X/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/0iz/P9KtPz/TbX8/1az8v94fX//AQEB/wAA - AP98gID/ys/P/8zR0f/O09P/0NXV/9LW1v/U2Nj/1tra/9fb2//Z3d3/2t7e/7SIZf+kZTT/pGU0/6Rl - NP+kZTT/pGU0/2Y/IP8AAAD/AAAA/yAgIP80NDT/Nzc3/zo6Ov89PT3/QUFB/0RERP9HR0f/SkpK/01N - Tf+3kHH/vpFu/6ZoOP+kZTT/pGU0/6RlNP/Mspz////////////////////////////+/v//IXXL/yF1 - y/8hdcv/IXXL/yF1y/8gdMv/C7Ph/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/MeH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8bpsD/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8fqsT/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0zo+/8K1+//ANTt/wDU7f8d2e//Qd/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TeHy/07h - 8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm - 9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p9v+A6vb/gur2/4Tq - 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+U7ff/le34/5ft+P+Y7vj/me74/5ru - +P+b7vj/nO74/53u+P+e7/j/n+/4/5/v+P+g7/j/oO/4/6Dv+P+g7/j/oO/4/5/v+P+f7/j/nu/4/53u - +P+c7vj/m+74/5ru+P+Z7vj/mO74/5bt+P+V7fj/k+33/5Lt9/+Q7Pf/j+z3/43s9/+L6/f/iuv3/4nr - 9/+H6/f/Tbj2/z6v/P8+r/z/Pq/8/z6v/P9BsPz/RLH8/0ez/P9KtPz/TbX8/1C2/P9St/z/L0JQ/wAA - AP8AAAD/FRYW/8jOzv/O09P/0NXV/9LW1v/U2Nj/1tra/9jc3P/Z3d3/29/f/9zg4P+9mn7/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+RWS7/AwIB/xQUFP9SUlL/VVVV/1hYWP9bW1v/Xl5e/2FhYf9lZWX/aGho/2tr - a/9/eXT/zKmO/86skf+7jGf/pGU0/6RlNP+kZTT/2cm7////////////////////////////9vn7/x9z - yv8fc8r/H3PK/x9zyv8fc8r/H3PK/wm34/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/A9Xu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5vn/Aoak/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh8gCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAhKFyAIOh9QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuSrv870OX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/N+P3/wDU7f8A1O3/BtXt/0Hf8v9D3/L/ReDy/0jg8v9J4PL/S+Hy/03h - 8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9l5fT/Z+X0/2jm - 9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo9f976fb/fOn2/37p9v9/6fb/ger2/4Pq - 9v+E6vb/h+v3/4jr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu - +P+c7vj/ne74/5/v+P+f7/j/oO/4/6Hv+P+h7/j/ou/4/6Lv+P+i7/j/ou/4/6Lv+P+h7/j/oe/4/6Dv - +P+f7/j/nu/4/53u+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jez3/4vr - 9/+J6/f/h+v3/1S/9v8+r/z/Pq/8/z6v/P9AsPz/Q7H8/0ay/P9KtPz/TbX8/1C2/P9Tt/z/RpnR/wED - BP8AAAD/BwcH/wAAAP9xdHT/z9TU/9HW1v/T2Nj/1tra/9jc3P/a3t7/3N/f/97h4f/f4uL/zLmp/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/0IoFf9AQED/cnJy/3Z2dv95eXn/fHx8/39/f/+CgoL/hoaG/4mJ - if+MjIz/wa6f/9i9qP/Zv6v/07We/6RlNP+kZTT/pGY1//Hu6////////////////////////////+Ts - 8/8dcsn/HXLJ/x1yyf8dcsn/HXLJ/x1yyf8Gweb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/x3c8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/L83k/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoS4Ag6HLAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AYWi/yWzzP9M5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8X2/H/ANTt/wDU7f8v3PD/RN/y/0bg8v9I4PL/SuDy/0zh - 8v9N4fL/T+Hz/1Hi8/9T4vP/VeLz/1bj8/9Z4/P/WuPz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl - 9P9p5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/duj1/3jo9f956Pb/e+n2/33p9v9/6fb/gOr2/4Lq - 9v+E6vb/huv2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu - +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+k8Pn/pfD5/6Tw+f+k8Pn/pPD5/6Pv - +f+i7/j/oe/4/6Dv+P+f7/j/ne74/5zu+P+a7vj/me74/5ju+P+V7fj/lO33/5Pt9/+R7Pf/j+z3/47s - 9/+L6/f/iuv3/4jr9/9gy/n/Pq/8/z6v/P8/r/z/QrD8/0ay/P9Js/z/TLX8/0+2/P9St/z/Vbn8/xcy - Q/8AAAD/FyMr/xscHP8AAAD/EBER/8nNzf/T2Nj/1dra/9jc3P/a3t7/3N/f/97h4f/g4+P/4eTk/+De - 2/+laDn/pGU0/6RlNP+kZTT/pGU0/6RlNP+XXTD/dW1n/5OTk/+Wlpb/mpqa/52dnf+goKD/o6Oj/6am - pv+qqqr/wrq0/+PPwP/k0cL/5dPF/+TRwv+kZTT/pGU0/6+BXf/+/v7///////////////////////// - ///F1+r/HHDH/xxwx/8ccMf/HHDH/xxwx/8ccMf/As7r/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f854/j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKjvf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqFsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICfCACCoYUAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/D5ey/0DV6/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Reb6/wLU7v8A1O3/Fdju/0Xg8v9G4PL/SODy/0rg - 8v9M4fL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl - 9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq - 9v+D6vb/hOr2/4fr9/+I6/f/iuv3/4vr9/+O7Pf/j+z3/5Hs9/+T7ff/le34/5bt+P+Y7vj/mu74/5vu - +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+k8Pn/pfD5/6bw+f+m8Pn/p/D5/6fw+f+m8Pn/pvD5/6bw - +f+l8Pn/pPD5/6Pv+f+h7/j/oO/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt+P+T7ff/ku33/5Ds - 9/+O7Pf/jOz3/4vr9/+J6/f/atT5/z6v/P8+r/z/QbD8/0Wx/P9Is/z/S7T8/0+2/P9St/z/Vbj8/zt9 - qv8AAAD/AQID/0uRwv8TFxn/AAAA/wAAAP9gYmL/1NnZ/9fb2//Z3d3/29/f/97h4f/g4+P/4uXl/+Tn - 5//l6Oj/u5Jx/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/8Kcfv/Gv7r/t7e3/7u7u/++vr7/wcHB/8TE - xP/JyMj/39fR/+7h2P/v49r/8OXd//Hn3//k0sP/pGU0/6RlNP/EsJ7///////////////////////// - ////////kLbd/xpvxv8ab8b/Gm/G/xpvxv8ab8b/F3rK/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8J1+//Tun8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISgPgCDofsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Boqo/ym40P8ntc7/JbPM/yWzzP8ls8z/JbPM/yWzzP8ir8n/IKzG/yCsxv8grMb/IKzG/yCs - xv8dqcP/GqW//xqlv/8apb//GqW//xqlv/8Yor3/FZ65/xWeuf8Vnrn/FZ65/xWeuf8TnLf/EJiz/xCY - s/8QmLP/EJiz/xCYs/8PlrH/C5Gu/wuRrv8Lka7/C5Gu/wuRrv8Fjqv/AIim/wCNq/8dr8j/RNzv/0ng - 8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9h5PT/YuX0/2Xl - 9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo9f936PX/eOj1/3vp9v996fb/fun2/4Dq - 9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/lO33/5Xt+P+X7fj/me74/5ru - +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+l8Pn/pvD5/6fw+f+o8Pn/qPD5/6nw+f+p8Pn/qfD5/6jw - +f+o8Pn/pvD5/6bw+f+k8Pn/o+/5/6Hv+P+g7/j/n+/4/5zu+P+b7vj/mu74/5ju+P+W7fj/lO33/5Pt - 9/+Q7Pf/j+z3/43s9/+L6/f/iev3/3je+P8+r/z/P6/8/0Ox/P9Gsvz/SrT8/061/P9Rt/z/VLj8/02k - 3/8HDhP/AAAA/zNkhf9jvvz/BgsO/wAAAP8BAgL/CwwM/77Cwv/Y3Nz/2t7e/93h4f/f4+P/4eTk/+Tn - 5//m6Oj/6Orq/+Da0/+laTr/pGU0/6RlNP+kZTT/pGU0/6RlNP+xe1H/7+Tb/+7m3//p5N//6eXi/+vo - 5f/w7On/9vHt//jz7//59fL/+vf1//v59//9+/n/zKmO/6RlNP+selL/6+3s//////////////////// - /////////////0KHzf8ZbsX/GW3F/xltxf8ZbcX/GW3F/xGO0f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8myuL/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKiaACDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmj - vf9J4PL/S+Hy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9g5PT/YeT0/2Pl - 9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n9f9w5/X/cuf1/3Pn9f926PX/eOj1/3no9v976fb/fen2/3/p - 9v+B6vb/gur2/4Tq9v+G6/b/iOv3/4nr9/+L6/f/juz3/4/s9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5ru - +P+c7vj/ne74/5/v+P+h7/j/ou/4/6Tw+f+m8Pn/p/D5/6jw+f+p8Pn/q/H5/6vx+f+r8fn/q/H5/6vx - +f+r8fn/qfD5/6jw+f+n8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt - +P+T7ff/kez3/4/s9/+O7Pf/i+v3/4rr9/+F6ff/Pq/8/0Gw/P9Fsfz/SLP8/0y1/P9Ptvz/U7j8/1a3 - +f8VLDv/AQEC/zJigv9jvvz/Wqnf/wAAAP8CAwP/IiMj/wAAAP9MTk7/19vb/9zf3//e4uL/4eTk/+Pm - 5v/l6Oj/6Orq/+rs7P/s7u7/0Lek/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/9Cvlv/9+/r//v39//// - ////////////////////////////////////////6tvQ/6ZoOP+mbD7/ysS8//39/f////////////// - //////////////L1+P8XbMP/F2zD/xdsw/8XbMP/F2zD/xdsw/8KqNz/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0Lm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Cp66/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIWfMACDofwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Qtjr/0vh8v9N4fL/T+Hz/1Hi8/9T4vP/VOLz/1bj8/9Y4/P/WuPz/1zk8/9e5PT/YOT0/2Ll - 9P9j5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9v5/X/cef1/3Ln9f906PX/duj1/3jo9f966fb/e+n2/33p - 9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/l+34/5nu - +P+a7vj/nO74/5/v+P+g7/j/oe/4/6Tw+f+l8Pn/p/D5/6jw+f+q8fn/q/H5/6zx+f+t8fn/rfH5/63x - +f+t8fn/rPH5/6vx+f+q8fn/qPD5/6fw+f+l8Pn/pPD5/6Hv+P+g7/j/nu/4/5zu+P+a7vj/me74/5ft - +P+V7fj/k+33/5Lt9/+Q7Pf/juz3/4zs9/+K6/f/iev3/0m2+v9Dsfz/RrL8/0q0/P9Otfz/Ubf8/1W4 - /P8lT2v/ESIt/0mRwf9jvvz/Zb/8/0mHsP8AAAD/FCIs/0FKUP8AAAD/BgYG/4GDg//d4OD/3+Pj/+Ll - 5f/k5+f/5+np/+nr6//r7e3/7e/v//Dx8f/Go4f/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/zKmO//z6 - +f//////////////////////////////////////4Mq5/6ltPv+lZzb/xbep/+fq6v////////////// - //////////////////+0zOX/FWrC/xVqwv8VasL/FWrC/xVqwv8VasL/A8Tm/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xDY8P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O+H2/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/z3P4/9L4fL/TeHy/0/h8/9R4vP/U+Lz/1Xi8/9W4/P/WePz/1vj8/9c5PP/XuT0/2Dk - 9P9i5fT/ZOX0/2Xl9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp - 9v9+6fb/f+n2/4Lq9v+D6vb/her2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+U7ff/lu34/5ju - +P+a7vj/m+74/53u+P+f7/j/oe/4/6Pv+f+k8Pn/pvD5/6jw+f+q8fn/q/H5/63x+f+u8fn/sPL5/7Dy - +f+w8vn/r/L5/67x+f+t8fn/q/H5/6nw+f+o8Pn/pvD5/6Tw+f+i7/j/oe/4/5/v+P+d7vj/m+74/5nu - +P+Y7vj/le34/5Tt9/+S7ff/kOz3/47s9/+M7Pf/i+v3/4nr9/9eyfr/RLH8/0iz/P9Mtfz/T7b8/1O4 - /P8ya5H/NW2T/125+P9hvvz/Zb/8/2jA/P8sUGj/AAAA/zJXcP9Qfp3/AAAA/wAAAP8KCgr/srW1/+Dk - 5P/j5ub/5ejo/+jq6v/q7e3/7e/v/+/x8f/y8/P/8/Pz/8SdgP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl - NP+udkr/0rOb/+vd0v/28Ov/+PPv/+/k3P/awa3/t4Zf/6RlNP+maTn/xK+e/9fb2//8/Pz///////// - ////////////////////////RojM/xRpwf8UacH/FGnB/xRpwf8UacH/EnTG/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8u4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x7E - 3f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HrAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wGNqv9H3/H/TOHy/03h8v9P4fP/UuLz/1Pi8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k - 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3Xo9f926PX/eOj1/3vp - 9v986fb/fun2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Ds9/+T7ff/le34/5bt - +P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx+f+u8fn/sPL5/7Hy - +f+y8vr/svL6/7Hy+f+w8vn/rfH5/6zx+f+q8fn/qPD5/6bw+f+k8Pn/o+/5/6Hv+P+f7/j/ne74/5vu - +P+a7vj/mO74/5Xt+P+U7ff/ku33/5Ds9/+O7Pf/jOz3/4vr9/+J6/f/dtz4/0ay/P9KtPz/TrX8/1G3 - /P9Rr+//U67s/1y7/P9gvfz/ZL/8/2fA/P9ovvj/DBUb/wAAAP9ZmcT/Xp7J/wAAAP8AAAD/AAAA/x8f - H//S1dX/4+bm/+bp6f/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+Pj/2MCt/6xyRv+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+vflf/zsS6/9bb2//v8fH///////// - ////////////////////////2+bw/xJowP8SZ7//Eme//xJnv/8SZ7//Eme//wqY1P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo - +/8ElLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICkDgCCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCLqf8Awt3/LNvw/0zh8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/13k - 9P9e5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/23m9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo - 9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s9/+R7Pf/k+33/5Xt - +P+X7fj/mO74/5ru+P+c7vj/nu/4/6Dv+P+h7/j/pPD5/6bw+f+n8Pn/qfD5/6vx+f+t8fn/r/L5/7Dy - +f+y8vr/tfP6/7Ty+v+y8vr/sPL5/67x+f+s8fn/qvH5/6jw+f+m8Pn/pPD5/6Pv+f+h7/j/n+/4/53u - +P+b7vj/mu74/5ju+P+V7fj/lO33/5Lt9/+Q7Pf/juz3/4zs9/+L6/f/iev3/4bq9/9KtPv/S7T8/0+2 - /P9Tt/z/V7n8/1q7/P9evPz/Yr78/2a//P9pwfz/SIGn/wAAAP8ZKzj/dsb9/2Sk0P8AAAD/AAAA/wAA - AP8AAAD/Ozw8/+Ll5f/n6en/6evr/+zu7v/v8PD/8fPz//T19f/39/f/+fr6//v8/P/59vP/07af/6x0 - SP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7F/WP/Muar/3N/f/9nd3f/k5+f///////// - /////////////////////////////1mSz/8RZr7/EWa+/xFmvv8RZr7/EWa+/xFmvv8DweX/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdry/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8z2/H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFoUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKEbAISimQCDof8Ag6H/AIOh/wCE - ot0Ag6HTAIOhzACDocwAg6HMAIOhzACDocwAg6HEAIOhuwCDobsAg6G7AIOhuwCDobsAgqK0AIOhqgCD - oaoAg6GqAIOhqgCDoaoAg6KkAISimQCEopkAhKKZAISimQCDob0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/D67I/yq50f8qudH/KrnR/yq50f8rutL/L8DX/y/A1/8ivNX/ALPO/wCzzv8AtM//ALnU/wC5 - 1P8AudT/ALnU/wC92P8A0uv/ANTt/wfV7f9J4PL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj - 8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3fo - 9f946PX/e+n2/33p9v9+6fb/gOr2/4Lq9v+E6vb/huv2/4jr9/+J6/f/i+v3/43s9/+P7Pf/kez3/5Pt - 9/+V7fj/l+34/5ju+P+a7vj/nO74/57v+P+g7/j/oe/4/6Tw+f+m8Pn/p/D5/6nw+f+r8fn/rfH5/6/y - +f+w8vn/svL6/7Py+v+y8vr/sfL5/7Dy+f+t8fn/rPH5/6rx+f+o8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v - +P+d7vj/m+74/5nu+P+Y7vj/le34/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iuv3/4nr9/+H6/f/Ysr5/0y1 - /P9Qtvz/VLj8/1i6/P9cu/z/YL38/2S//P9nwPz/ab/4/xIgKv8AAQH/V5S9/3jH/f9mpc//AAAA/wAA - AP8AAAD/AAAA/wAAAP9eYGD/5+np/+nr6//s7u7/7/Dw//Hz8//09fX/9/f3//n6+v/8/Pz//f39//z8 - /P/4+fj/5NbL/86ymv/Foob/vpRz/76Xd//DoYf/y7Ke/9zVzv/h5OT/3+Li/9zg4P/g4+P//f39//// - /////////////////////////////9Df7P8PZb3/D2W9/w9lvf8PZb3/D2W9/w9lvf8Ofcf/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zbj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/FrfQ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoewAgKoGAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJkKAIOh8gCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wKnwv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w3X8P8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/J9vw/07h8/9P4fP/UuLz/1Ti8/9V4vP/V+Pz/1nj - 8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo - 9f936PX/eOj1/3vp9v996fb/fun2/4Dq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Hs - 9/+T7ff/le34/5bt+P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx - +f+t8fn/r/L5/7Dy+f+x8vn/sPL5/7Dy+f+u8fn/rfH5/6vx+f+p8Pn/p/D5/6bw+f+k8Pn/ou/4/6Dv - +P+f7/j/nO74/5ru+P+Z7vj/l+34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr9/+I6/f/h+v3/3rg - 9/9Ntfz/Ubf8/1W5/P9auvz/Xrz8/2G+/P9lv/z/acH8/zxri/8AAAD/M1dw/3jH/f97yP3/XZW7/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AgIC/4CCgv/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+fn/+vr6//r7 - +//6+vr/+Pn5//X29v/z9PT/8fLy/+7w8P/r7e3/6evr/+bp6f/k5+f/4eTk/97i4v/i5eX/+/z8//// - //////////////////////////////r7/P8sdsP/DmO8/w5jvP8OY7z/DWO7/w1ju/8NY7v/BbPe/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v9N6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/R+b6/wGIp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GSAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaAAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AiKX/Kd3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8y4fb/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7f9I4PL/T+Hz/1Li8/9U4vP/VeLz/1fj - 8/9Z4/P/W+Pz/13k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn - 9f916PX/d+j1/3jo9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s - 9/+Q7Pf/k+33/5Xt+P+W7fj/mO74/5ru+P+c7vj/ne74/5/v+P+h7/j/o+/5/6Tw+f+m8Pn/qPD5/6nw - +f+r8fn/rPH5/63x+f+u8fn/rvH5/67x+f+t8fn/rfH5/6vx+f+q8fn/qPD5/6bw+f+l8Pn/o+/5/6Hv - +P+f7/j/nu/4/5zu+P+a7vj/mO74/5ft+P+V7fj/k+33/5Hs9/+P7Pf/juz3/4vr9/+J6/f/iOv3/4br - 9v+E6vb/XMT6/1O3/P9Xufz/Wrv8/1+8/P9jvvz/Z8D8/1mi0v8EBwn/IzxN/3XE+v96yP3/fcn9/1B+ - nf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/nZ+f/+rt7f/t7+//7/Hx//Lz8//09fX/9fb2//f4 - +P/3+Pj/9/j4//b39//09fX/8vPz//Dx8f/t7+//6+3t/+jq6v/l6Oj/4+bm/+Dk5P/n6en//f39//// - //////////////////////////////////92pNL/DGK7/wxiuv8MYrr/DGK6/wxiuv8MYrr/DXvF/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8h3fT/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/yrQ5v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhMQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wm41P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tuj8/wrW - 7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/JNrw/0/h8/9S4vP/VOLz/1Xi - 8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Hn - 9f9z5/X/dej1/3fo9f946PX/e+n2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+J6/f/i+v3/43s - 9/+O7Pf/kOz3/5Lt9/+U7ff/le34/5ju+P+a7vj/m+74/5zu+P+f7/j/oO/4/6Lv+P+k8Pn/pfD5/6bw - +f+o8Pn/qfD5/6vx+f+r8fn/rPH5/6zx+f+s8fn/q/H5/6vx+f+p8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv - +P+g7/j/n+/4/53u+P+b7vj/mu74/5ju+P+W7fj/lO33/5Lt9/+Q7Pf/juz3/43s9/+L6/f/iev3/4fr - 9/+F6vb/hOr2/3be9/9TuPz/V7n8/1y7/P9gvfz/ZL/8/2S48f8SISr/KUhd/3HA9/94x/3/fMn9/3/K - /f82VWn/AAAA/wsRFf8AAAD/AAAA/wAAAP8AAAD/AAAA/wsLC/+lqKj/6+3t/+7w8P/w8fH/8vPz//P0 - 9P/09fX/9Pb2//T19f/z9PT/8vPz//Dx8f/u8PD/7O7u/+ns7P/n6ur/5Ofn/+Pm5v/x8vL///////// - //////////////////////////////////+1zOL/CmC5/wpguf8KYLn/CmC5/wpguf8KYLn/CmG6/w69 - 4/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Tuj8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8Nnrv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCC - odcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/NuL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f9G4PL/UuLz/1Ti - 8/9V4vP/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n - 9f9x5/X/c+f1/3Xo9f926PX/eOj1/3rp9v986fb/fun2/3/p9v+C6vb/g+r2/4Xq9v+H6/f/iev3/4vr - 9/+M7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/me74/5ru+P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw - +f+l8Pn/pvD5/6jw+f+o8Pn/qfD5/6rx+f+q8fn/qvH5/6nw+f+o8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv - +P+h7/j/n+/4/53u+P+c7vj/mu74/5ju+P+X7fj/le34/5Pt9/+R7Pf/kOz3/47s9/+M7Pf/iuv3/4nr - 9/+H6/f/her2/4Pq9v+B6vb/Ysj6/1i6/P9cu/z/Yb38/2W++/8pTGP/RHmc/3HE/f91xv3/ecj9/33J - /f+By/3/FSAo/wAAAP8oOkf/AAAA/wAAAP8OFBj/Exgb/wAAAP8AAAD/DQ0N/6utrf/s7u7/7e/v/+/x - 8f/w8vL/8fPz//Hz8//x8/P/8PLy/+/x8f/u8PD/7O7u/+rs7P/o6ur/5ejo/+vt7f/6+/v///////// - ///////////////////////////////////R3ej/C2C5/wlfuP8JX7j/CV+4/wlfuP8JX7j/CV+4/xCS - z/8J1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8/4PX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoHEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAe5eHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xDG3v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/TOj7/wjW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdjv/1Li - 8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl9P9q5vT/a+b1/23m - 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f966fb/e+n2/33p9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4jr - 9/+K6/f/i+v3/47s9/+P7Pf/kez3/5Pt9/+V7fj/lu34/5ju+P+a7vj/m+74/5zu+P+e7/j/oO/4/6Hv - +P+i7/j/pPD5/6Xw+f+m8Pn/pvD5/6fw+f+o8Pn/qPD5/6jw+f+n8Pn/pvD5/6bw+f+k8Pn/pPD5/6Lv - +P+h7/j/n+/4/57v+P+c7vj/mu74/5nu+P+Y7vj/le34/5Tt9/+T7ff/kOz3/4/s9/+N7Pf/i+v3/4nr - 9/+I6/f/huv2/4Tq9v+C6vb/ger2/3nj9/9au/z/Xbz8/2G+/P9Un9L/Xqvf/27D/P9yxf3/dsb9/3rI - /f9+yv3/barS/wAAAP8JDhH/P11x/wAAAP8AAAD/GSQr/0NecP8AAAD/AAAA/wAAAP8PDw//paam/+vt - 7f/s7u7/7e/v/+7w8P/v8PD/7vDw/+3v7//s7u7/6+3t/+rs7P/p6+v/7vDw//j5+f////////////// - ///////////////////////////////////a5Oz/EGK4/wddt/8HXbf/B123/wddt/8HXbb/B122/wpw - vv8Y0uz/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zvk+P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Ir3V/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAgJ8QAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAXHEkAF1y6QCBnv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Al7P/OuT4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f853vH/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2vm - 9f9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4br - 9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/mO74/5ru+P+c7vj/ne74/5/v - +P+f7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+l8Pn/pfD5/6bw+f+l8Pn/pfD5/6Tw+f+k8Pn/o+/5/6Hv - +P+h7/j/n+/4/57v+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr - 9/+J6/f/h+v3/4Xq9v+D6vb/gur2/3/p9v9+6fb/bNP5/168/P9ivvz/ZsD8/2rB/P9vw/z/c8X9/3fH - /f97yP3/gMr9/zRRZP8AAAD/Vn+b/zxXaf8AAAD/AAAA/yo6Rf91o8D/AAAA/wAAAP8AAAD/AAAA/wwM - DP+RkZH/7/Hx/+7w8P/t7+//7O7u/+3v7//u8PD/8PLy//T19f/4+fn//v7+//////////////////// - ///////////////////////////////////M2uj/DmG3/wZctf8GXLX/Bly1/wVctf8FXLX/BVy1/wZi - t/8Xw+b/FNfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPZ8P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Tej7/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6CUAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW20cAFpv4QBab/8Acoz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xTL4/9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Tej8/wvX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Ctbu/0/h8/9U4vP/VuPz/1jj8/9a4/P/XOTz/17k9P9g5PT/YeT0/2Pl9P9l5fT/Z+X0/2nm - 9P9q5vT/bOb1/27n9f9w5/X/cef1/3Pn9f926PX/d+j1/3no9v976fb/fen2/37p9v+A6vb/gur2/4Tq - 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kez3/5Pt9/+U7ff/le34/5ju+P+Z7vj/mu74/5zu - +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+i7/j/o+/5/6Pv+f+j7/n/o+/5/6Pv+f+i7/j/oe/4/6Hv - +P+f7/j/n+/4/53u+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+O7Pf/jez3/4vr - 9/+J6/f/iOv3/4br9v+E6vb/gur2/4Hq9v9/6fb/fen2/3vp9v9myPv/Yr78/2fA/P9rwvz/b8P8/3PF - /f93x/3/fMn9/3e97P8FBwn/KT5M/43Q/f8wRVP/AAAA/wAAAP9HYXP/ndX7/wwRFP8AAAD/AAAA/wAA - AP8AAAD/BAQE/11dXf/29vb///////////////////////////////////////////////////////// - //////////////////////////////3+/v+nwtz/BVy0/wRbtP8EWrT/BFq0/wRatP8EWrT/BFq0/wRc - tf8Us93/Gtjv/wvW7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f885Pn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y7K4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIakKgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVW0VAFpv2gBab/8AWm//AGF3/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Amrf/PuX5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P804vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8m2vD/VOLz/1bj8/9Y4/P/WuPz/1vj8/9e5PT/X+T0/2Hk9P9i5fT/ZeX0/2bl - 9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq - 9v+C6vb/hOr2/4br9v+I6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5nu - +P+a7vj/m+74/5zu+P+d7vj/nu/4/5/v+P+g7/j/oO/4/6Hv+P+h7/j/oe/4/6Hv+P+h7/j/oO/4/5/v - +P+f7/j/nu/4/5zu+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/jez3/4vr - 9/+K6/f/iOv3/4fr9/+F6vb/g+r2/4Lq9v9/6fb/fun2/33p9v976fb/duP3/2TB+/9nwPz/a8L8/2/D - /P9zxf3/eMf9/3zJ/f86XHP/DxYc/3275v+N0P3/Hyw2/wAAAP8AAAD/a5Ks/6HY/f8yRFD/AAAA/wAA - AP8KDQ//AAAA/wAAAP8AAAD/Dg4O/4KCgv/n5+f///////////////////////////////////////// - //////////////////////////////Dz9v9Wjcf/A1mz/wJZs/8CWbP/Almz/wJZs/8CWbP/Almz/wJa - s/8Tpdb/HNnv/xrY7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vH/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/8KkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShuAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVXcPAFpv0gBab/8AWm//AFpv/wBab/8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xXO - 5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w/Y8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/0Xg8v9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl - 9P9m5fT/Z+X0/2rm9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p - 9v+A6vb/gur2/4Tq9v+F6vb/h+v3/4nr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt - +P+X7fj/mO74/5ru+P+a7vj/m+74/5zu+P+d7vj/nu/4/57v+P+f7/j/n+/4/5/v+P+f7/j/nu/4/57v - +P+d7vj/nO74/5zu+P+a7vj/mu74/5ju+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs - 9/+K6/f/iev3/4fr9/+F6vb/hOr2/4Lq9v+A6vb/f+n2/33p9v976fb/eun2/3jo9f9y4Pb/aMP7/2vC - /P9vw/z/c8X9/3fH/f9np9L/DRUa/3Gu2P+Jzv3/jdD9/wsQE/8AAAD/BwkL/5fP8/+e1/3/VnWL/wAA - AP8AAAD/P1tu/wAAAP8AAAD/AAAA/wAAAP8AAAD/AgIC/zY2Nv97e3v/vLy8//b29v////////////// - ////////////////////////+fr6/6jD3P8NX7T/AVix/wFYsf8BWLH/AVix/wFYsf8BWLH/AVix/wFY - sf8RndL/Hdnv/xvZ7/8W2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/Qeb6/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8xzeT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - okIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAXXQLAFtvyABab/8AWm//AFpv/wBab/8AWm//AGJ5/wCCoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ambb/OuP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P885Pn/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTs/wCuyf8DkK3/DJGt/yqxyf9X4fD/W+Pz/1zk8/9e5PT/YOT0/2Ll - 9P9k5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9u5/X/cOf1/3Hn9f9z5/X/dej1/3fo9f946PX/e+n2/3zp - 9v996fb/f+n2/4Hq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/ku33/5Pt - 9/+U7ff/le34/5ft+P+Y7vj/mO74/5ru+P+a7vj/m+74/5zu+P+c7vj/nO74/5zu+P+c7vj/nO74/5zu - +P+c7vj/m+74/5ru+P+a7vj/me74/5ju+P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs - 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Lq9v+B6vb/f+n2/33p9v986fb/e+n2/3jo9f936PX/dej1/2/d - 9/9rwfz/b8P8/3PF/f90wvf/JDpJ/2ahyf+EzP3/iM79/4C95/8AAAD/AAAA/z5WZ/+a1f3/m9b9/3ak - wv8AAAD/AAAA/2eWtv8OFRr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/Pj4+/2xs - bP+YmJj/tLS0/8rKyv/Fxsb/mKq7/xpRi/8APHv/AFSr/wBXsf8AV7H/AFex/wBXsf8AV7H/AFex/wBY - sf8RndL/H9nv/xzZ7/8b2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6Pv/CpGu/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ocQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAASW0HAFpvvQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8GgZz/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/xDH4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x3c8/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wClwP8Ag6H/AIOh/wCDof8Ag6H/I6nB/1rj8/9c5PP/XuT0/2Dk - 9P9h5PT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3To9f926PX/eOj1/3no - 9v976fb/fen2/37p9v+A6vb/gur2/4Pq9v+E6vb/huv2/4jr9/+J6/f/i+v3/4zs9/+O7Pf/j+z3/5Ds - 9/+R7Pf/k+33/5Tt9/+V7fj/le34/5ft+P+Y7vj/mO74/5nu+P+Z7vj/mu74/5ru+P+a7vj/mu74/5ru - +P+a7vj/me74/5nu+P+Y7vj/mO74/5ft+P+V7fj/le34/5Pt9/+T7ff/kez3/5Ds9/+P7Pf/juz3/4zs - 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/f+n2/37p9v996fb/e+n2/3no9v946PX/duj1/3To - 9f9z5/X/btv3/27E/P9yxf3/Qm6N/2iq1/9/yv3/g8z9/4fN/f9klbf/AAAA/wYICv+Dut//l9T9/5fU - /f+Hv+T/AAAA/wAAAP9Ygp7/RGd+/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAIEf8AL1//AFKn/wBXsf8AV7H/AFex/wFb - s/8Wrdr/INnv/x7Z7/8c2e//Gtjv/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0no - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LMje/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAZmYFAFlvsQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Qd4v/Ocrh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ak6//NeL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/BNXu/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC81v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9W3+//W+Pz/13k - 9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo - 9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iOv3/4nr9/+L6/f/jOz3/43s - 9/+O7Pf/kOz3/5Hs9/+S7ff/k+33/5Tt9/+V7fj/le34/5bt+P+X7fj/l+34/5ju+P+Y7vj/mO74/5ju - +P+Y7vj/mO74/5ft+P+X7fj/lu34/5Xt+P+V7fj/lO33/5Pt9/+S7ff/kOz3/5Ds9/+O7Pf/jez3/4vr - 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/e+n2/3rp9v946PX/duj1/3Xo - 9f9z5/X/cef1/3Dn9f9u3/b/XqbT/2y36v95yP3/fcn9/4HL/f+Fzf3/RmqC/wAAAP9Ue5X/kdL9/5PS - /f+U0/3/kdD6/wIDBP8AAAD/UnqV/3m34P8BAgL/AAAA/wAAAP8BAgL/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAGDP8AMmf/AFew/wVq - uv8cwOL/Idrv/x/Z7/8d2e//HNnv/xbY7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y/h - 9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sub6/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVVUDAFpupABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Ncob/SN3w/0/p/P8Xobv/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wq92P9N6fv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yzg - 9f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO5v8Aiqf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Utvt/1vj - 8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3To - 9f926PX/eOj1/3no9v976fb/fOn2/33p9v9/6fb/ger2/4Lq9v+E6vb/her2/4fr9/+I6/f/iev3/4rr - 9/+L6/f/jez3/47s9/+P7Pf/kOz3/5Hs9/+S7ff/k+33/5Pt9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt - +P+V7fj/le34/5Xt+P+V7fj/lO33/5Tt9/+T7ff/k+33/5Lt9/+Q7Pf/kOz3/4/s9/+O7Pf/jOz3/4vr - 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3rp9v946PX/d+j1/3Xo - 9f9z5/X/cuf1/3Hn9f9v5/X/beb1/2vg9/9yyvv/eMf9/3zJ/f+Ayv3/g8z9/yAwPP8qP03/jND9/47Q - /f+Q0f3/kNH9/5DR/f8IDA//AAAA/1B4lP+Fzf3/Gyk0/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wQi - Mv8duM3/Itrv/yHa7/8f2e//Hdnv/xvZ7/8L1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xLZ - 8P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yW81f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAABAFtvmABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8LboP/Rtns/0/p/P9P6fz/Qdfs/wGE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ajar/KN3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9O6Pz/Edjw/wDU7f8A1O3/ANTt/wDU7f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/BpOv/1jj - 8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Hn - 9f9z5/X/dej1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+B6vb/gur2/4Tq9v+F6vb/h+v3/4fr - 9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Pt9/+T7ff/k+33/5Pt - 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Ds9/+Q7Pf/juz3/47s9/+N7Pf/i+v3/4vr - 9/+J6/f/iOv3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v946PX/d+j1/3bo - 9f906PX/cuf1/3Hn9f9v5/X/buf1/2zm9f9q5vT/aeX0/3DS+v96yP3/fsn9/3e86v8THST/fb7q/4nO - /f+Lz/3/jND9/4zQ/f+Mz/3/AwQF/wAAAP9KcYz/g8z9/z5hev8AAAD/AAAA/wAAAP8RHCX/AAAA/wEC - Av8dNkf/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AgkK/xaQnf8g2e//Htnv/xzZ7/8Z2O//AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU - 7f9C5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Ph9f8ChaT/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpwiwBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Jan//RNXo/0/p/P9P6fz/T+n8/0/p - /P8eqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOrx/9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0Lm+v8C1O3/ANTt/wDU7f8AtM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/zbQ - 5v833/L/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n - 9f9x5/X/cuf1/3Pn9f916PX/d+j1/3jo9f966fb/e+n2/33p9v9+6fb/f+n2/4Hq9v+C6vb/hOr2/4Tq - 9v+G6/b/h+v3/4jr9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+O7Pf/j+z3/5Ds9/+Q7Pf/kOz3/5Ds - 9/+R7Pf/kez3/5Hs9/+R7Pf/kOz3/5Ds9/+Q7Pf/j+z3/4/s9/+O7Pf/juz3/43s9/+L6/f/i+v3/4rr - 9/+J6/f/h+v3/4fr9/+F6vb/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo - 9f906PX/c+f1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aeb0/2fl9P9m5fT/atv3/3nL/P9OfJz/aqfQ/4TM - /f+Gzf3/iM79/4nO/f+Jzv3/hMbz/wAAAP8AAAD/W46w/4HL/f9Wiq7/AAAA/wAAAP8AAAD/L1Ns/wAA - AP8AAAD/JEZc/zhvlP8BAwT/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAQH/EXWA/x3Z7/8c2e//ENfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8p3/T/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8WpsD/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/gCEojQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvfABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8HZnv/QtHl/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Q9js/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/GdLp/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/KN/0/wDU7f8AyOP/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ya0 - zv9P6fz/Ten7/0bj9v9a4/P/XOTz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9p5vT/aub0/2zm - 9f9u5/X/b+f1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3rp9v976fb/fen2/37p9v9/6fb/ger2/4Lq - 9v+D6vb/hOr2/4Xq9v+H6/f/h+v3/4nr9/+J6/f/iuv3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/47s - 9/+O7Pf/juz3/4/s9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iev3/4nr - 9/+I6/f/h+v3/4br9v+E6vb/hOr2/4Lq9v+B6vb/f+n2/37p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/2zm9f9r5vX/aub0/2jm9P9m5fT/ZeX0/2Pl9P9i4/T/XbXT/37L - /f+By/3/g8z9/4TM/f+Fzf3/hc39/2Wbv/8AAAD/AAAA/2uq1P9+yv3/aqzZ/wAAAP8AAAD/AAAA/1KS - vP8EBQb/AQEB/wEBAv9PnNH/P4Kv/wMGCP8AAAD/AAAA/wAAAP8BAQL/Dig6/wABAv8AAAD/AAAA/wAA - AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8MW2T/G9ju/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8Q2PD/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P800un/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvbgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8FY3f/P8zg/0/p/P9P6fz/SuDz/ze/ - 0v8knLD/EnqO/wJdcv8Ab4n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCYtP824vf/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o/P8P1+7/AJWy/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xCY - tP9N5/r/T+n8/0/p/P9P6fz/Tub4/1rj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/aOb0/2rm - 9P9r5vX/bOb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p - 9v+A6vb/gur2/4Lq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iuv3/4rr9/+L6/f/i+v3/4vr - 9/+M7Pf/jOz3/4zs9/+M7Pf/jOz3/4zs9/+M7Pf/i+v3/4vr9/+L6/f/iuv3/4rr9/+J6/f/iev3/4fr - 9/+H6/f/huv2/4Xq9v+E6vb/g+r2/4Lq9v+A6vb/f+n2/37p9v996fb/e+n2/3rp9v946PX/d+j1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Pl9P9i5fT/YOT0/17k - 9P9h4fX/btX5/37L/P+By/3/gcv9/4LL/f9DaoT/AAAA/w4WHP98x/v/e8j9/3O+8v8AAAD/AAAA/wAA - AP9jsuf/KEBQ/w8PD/8NDQ3/Kk1l/1q6/P9Ci73/CQ0Q/wQEBP8DAwP/AQEB/wouM/8mtcb/E15n/wIJ - Cv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wheaP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8E1e7/ROb5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J5/r/B46r/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDofUAgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpwYABab/4AWm//AFpv/wBab/8AWm//AFpv/wBab/8DX3X/NbrO/zW6zv8imKz/D3WJ/wFb - cP8AWm//AFpv/wBab/8AWm//AFtx/wB+mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CLzW/0rn - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/IrXO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOH - pf9D2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6Pv/WuT1/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl - 9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/c+f1/3To9f926PX/d+j1/3jo9f966fb/e+n2/3zp - 9v996fb/f+n2/3/p9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+I6/f/iev3/4nr - 9/+J6/f/iuv3/4rr9/+K6/f/iuv3/4rr9/+K6/f/iuv3/4nr9/+J6/f/iev3/4jr9/+I6/f/h+v3/4fr - 9/+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v9/6fb/f+n2/33p9v986fb/e+n2/3rp9v946PX/d+j1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/1/k - 9P9e5PT/W+Pz/1rj8/9b4vT/aNj3/3XP+v9+yv3/FyQu/wAAAP84W3L/esj9/3jH/f91xv3/AQIC/wAA - AP8HDRH/asH8/0l9oP8eHh7/HBwc/x4kKP9MsO//PbLs/yyNqv8TFxj/ERER/w8PD/8NDQ3/FktR/yrY - 7f8ozeD/FWNs/wUHCP8CAgL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AHB9/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HbPM/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtwVABab/wAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AZ37/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ - p/8e1+7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tp/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8uvtb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Do+/9Y5fb/XuT0/2Dk9P9h5PT/YuX0/2Tl - 9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/cOf1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3no - 9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr - 9/+H6/f/h+v3/4fr9/+I6/f/iOv3/4jr9/+I6/f/iOv3/4fr9/+H6/f/h+v3/4fr9/+G6/b/her2/4Xq - 9v+E6vb/hOr2/4Lq9v+C6vb/ger2/3/p9v9/6fb/fun2/33p9v976fb/e+n2/3no9v946PX/duj1/3bo - 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/1/k - 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/RLLC/wAAAP8FCgz/Ybjg/3HL+/9yyPz/cMf8/wAA - AP8AAAD/HTpK/13I+f9Rtdv/LCws/yoqKv8oKCj/M6e0/zfd8f813fH/LJai/x8fH/8eHh7/HBwc/xoa - Gv8heIP/Kdvw/yfb8P8iucv/FUlP/w8PD/8NDQ3/CwsL/wkJCf8ICAj/BgYG/wQFBf8BhZX/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N9jt/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HaAJKSBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtwSQBab/kAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wB2kv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AJu3/zjj+P9P6fz/T+n8/0/p/P9P6fz/TOX4/wyTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8XoLv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1bm+P9e5PT/YOT0/2Ll - 9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3bo - 9f946PX/eej2/3rp9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Lq9v+D6vb/hOr2/4Tq - 9v+E6vb/hOr2/4Xq9v+F6vb/huv2/4br9v+G6/b/huv2/4Xq9v+F6vb/her2/4Tq9v+E6vb/hOr2/4Pq - 9v+C6vb/gur2/4Lq9v+A6vb/f+n2/3/p9v9+6fb/fen2/3zp9v976fb/eun2/3jo9f936PX/duj1/3Xo - 9f9z5/X/cuf1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2nm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/xxOVP8AAAD/K3uE/0zh8v9K4PL/SeDy/0XZ - 6v8AAAD/AAAA/yV+if9B3/L/Pt7x/ztPUf84ODj/Nzc3/zaDjP823fH/NN3x/zLc8f8vdX3/LCws/yoq - Kv8oKCj/Jikp/yequf8m2vD/JNrw/yPa7/8fnKr/Gykr/xkZGf8XFxf/FhYW/xQUFP8SEhL/DxcY/wKz - yP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Dtjv/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOb5/wePq/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AX3X/AIGe/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8IutX/Sej7/0/p/P9P6fz/T+n8/yGux/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Giqj/R9/z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/U+j6/17k - 9f9g5PT/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/cuf1/3Pn - 9f916PX/duj1/3fo9f946PX/eej2/3vp9v976fb/fOn2/33p9v9+6fb/f+n2/3/p9v+A6vb/ger2/4Lq - 9v+C6vb/gur2/4Lq9v+D6vb/g+r2/4Pq9v+D6vb/hOr2/4Pq9v+D6vb/g+r2/4Pq9v+C6vb/gur2/4Lq - 9v+B6vb/gOr2/3/p9v9/6fb/fun2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f926PX/duj1/3To - 9f9z5/X/cuf1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9U4vP/U+Lz/0S8yv8BAgL/G09V/03g8f9L4fL/SeDy/0jg - 8v86uMf/AAAA/wIFBv88zd7/P9/x/z7e8f9Eg4v/R0dH/0VFRf8/cnn/Nd3x/zPd8f8y3PH/Mdjr/zhU - WP84ODj/Nzc3/zU1Nf8yRUf/JtHm/yTa8P8h2u//INnv/x/H2/8kV13/JiYm/yQkJP8jIyP/ISEh/x8f - H/8YOz//Ac3l/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/B9bu/0Tm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xatxv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOhowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFttKgBab/AAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/oAFluqwBzjqkAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIel/xfT6v9P6fz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9R6Pv/XOX1/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/b+f1/3Hn - 9f9y5/X/c+f1/3To9f926PX/duj1/3jo9f946PX/eej2/3vp9v976fb/fOn2/33p9v996fb/fun2/3/p - 9v9/6fb/f+n2/4Dq9v+A6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+A6vb/gOr2/3/p - 9v9/6fb/f+n2/37p9v996fb/fen2/3zp9v976fb/e+n2/3rp9v956Pb/eOj1/3fo9f926PX/dej1/3Pn - 9f9z5/X/cef1/3Dn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/8aSE7/GERJ/0va6v9L4fL/SuDy/0jg - 8v9G4PL/LpWh/wAAAP8WSlH/QN/y/z7e8f883vH/Rqm0/1VVVf9TU1P/TWhr/zTd8f8y3PH/MNzw/y/c - 8P80u8r/RkhI/0VFRf9DQ0P/QUFB/zSDjP8j2u//Idrv/x/Z7/8d2e//Gtfu/xqHlP8yMzP/MTEx/y8v - L/8tLS3/Kysr/xhvev8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/A9Xu/z7l+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yvN4/8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh5wCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABbb7cAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnDgAFpwogBbb2UAWWwoAAAAAAAAAAAA//8BAISgugCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AlLH/LN/0/0ni9f8Hjar/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/HqrD/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/o/P9Y5/j/YOT0/2Ll9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/bOb1/27n - 9f9v5/X/cOf1/3Hn9f9z5/X/c+f1/3Xo9f926PX/duj1/3jo9f946PX/eej2/3rp9v976fb/e+n2/3zp - 9v996fb/fen2/33p9v9+6fb/fun2/37p9v9/6fb/f+n2/3/p9v9/6fb/f+n2/3/p9v9/6fb/fun2/37p - 9v996fb/fen2/33p9v986fb/e+n2/3vp9v966fb/eej2/3jo9f946PX/d+j1/3bo9f916PX/dOj1/3Pn - 9f9y5/X/cef1/2/n9f9v5/X/beb1/2zm9f9q5vT/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/88qbf/HVJZ/0vd7f9M4fL/SuDy/0jg - 8v9H4PL/ReDy/yJye/8AAAD/L6Ox/z7e8f893vH/PN7x/0HG1v9kZGT/YmJi/1tvcf8z3fH/Mdzw/zDc - 8P8t3PD/LNvw/0eEi/9TU1P/UVFR/1BQUP9NUFD/J8bY/yDZ7/8e2e//HNnv/wzW7v8A1O3/FaO0/z1E - RP89PT3/PDw8/zo6Ov84OTn/DLDD/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AdXt/zfj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zzf9P8ChaT/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm/vAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/XAFpumwBabl0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - nh0Ag6LwAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKoxP8Uor3/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/CpCt/0vk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Xo+f9f5PX/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm - 9f9s5vX/beb1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3Xo9f926PX/duj1/3fo9f946PX/eOj1/3no - 9v966fb/e+n2/3vp9v976fb/fOn2/3zp9v986fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3zp - 9v986fb/e+n2/3vp9v976fb/eun2/3no9v946PX/eOj1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Ln - 9f9x5/X/cOf1/2/n9f9u5/X/bOb1/2vm9f9q5vT/aeb0/2fl9P9m5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk - 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4fL/OaOw/03h8v9M4fL/SuDy/0ng - 8v9H4PL/ReDy/0Tf8v8RNzz/EDY7/z/f8f8+3vH/PN7x/zre8f863e//cHZ3/3BwcP9ne33/Mtzx/zDc - 8P8u3PD/Ldzw/yvb8P8yydv/YWNj/2BgYP9eXl7/XFxc/0SMlP8f2e//Hdnv/xXY7v8A1O3/ANTt/wDU - 7f8Rtsn/RldZ/0pKSv9ISEj/RkZG/zhdYf8B0en/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fm+v8IlbH/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFpvywBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/0AWm/RAFtwkgBbcFQAWW8XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKgXgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AYSi/zzR5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Uun6/13l9v9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm - 9P9q5vT/aub0/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/duj1/3bo - 9f936PX/eOj1/3jo9f946PX/eej2/3no9v966fb/eun2/3rp9v976fb/e+n2/3vp9v976fb/eun2/3rp - 9v966fb/eej2/3no9v946PX/eOj1/3jo9f936PX/duj1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn - 9f9w5/X/b+f1/27n9f9t5vX/bOb1/2rm9P9q5vT/aOb0/2fl9P9m5fT/ZeX0/2Pl9P9i5fT/YOT0/1/k - 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9M4fL/SuDy/0ng - 8v9I4PL/RuDy/0Tf8v89zd7/AQME/zOxwP8+3vH/PN7x/zve8f853vH/N93x/3GTl/9+fn7/cI2R/zHc - 8P8v3PD/Ldzw/yvb8P8q2/D/Kdvw/1mQlv9ubm7/bGxs/2pqav9obG3/ItLn/xvZ7/8D1e3/ANTt/wDU - 7f8A1O3/ANTt/xG90f9SYWP/VlZW/1VVVf9TU1P/JZmm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8Srcf/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeAAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm/8AFpv/wBab/8AWm//AFpv/wBab/0AWm/HAFtvigBb - b0wAYHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6fv/Wub3/2Ll9P9j5fT/ZOX0/2Xl - 9P9n5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9y5/X/c+f1/3Pn - 9f906PX/dej1/3bo9f926PX/duj1/3fo9f936PX/eOj1/3jo9f946PX/eOj1/3jo9f946PX/eOj1/3jo - 9f946PX/eOj1/3fo9f936PX/duj1/3bo9f926PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn - 9f9v5/X/buf1/23m9f9s5vX/a+b1/2rm9P9p5vT/Z+X0/2fl9P9l5fT/ZOX0/2Ll9P9i5fT/YOT0/1/k - 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/KIiU/xhVXP8+3vH/PN7x/zve8f853vH/ON3x/zbd8f9uq7L/jY2N/3Ki - qP8w3PD/Ltzw/yzb8P8r2/D/Kdvw/yfb8P9Bv87/fX19/3t7e/95eXn/d3d3/z21w/8K1u7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Fb/T/19rbf9jY2P/YWFh/1hnaf8GzeT/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/x3c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8hxt3/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAhKM6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFttOABab58AWm+qAFtvgQBZb0UAXXQLAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICqDACCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/w+Xsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9X5/j/YeX0/2Ll - 9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9v5/X/cef1/3Hn - 9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/dej1/3bo9f926PX/duj1/3bo9f926PX/duj1/3bo - 9f926PX/duj1/3Xo9f916PX/dej1/3To9f9z5/X/c+f1/3Pn9f9y5/X/cef1/3Hn9f9w5/X/b+f1/2/n - 9f9u5/X/beb1/2zm9f9r5vX/aub0/2nm9P9o5vT/Z+X0/2bl9P9l5fT/Y+X0/2Ll9P9h5PT/YOT0/17k - 9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt7x/xNBRv870uT/Pd7x/zze8f853vH/ON3x/zfd8f813fH/Zb7J/5ub - m/9vtLz/Ltzw/y3c8P8r2/D/Kdvw/yjb8P8m2vD/J9jt/4WPkP+JiYn/h4eH/4WFhf9Zoar/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8avtH/bnN0/29vb/9ubm7/OZ2p/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/x/c8/9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u1uz/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6J7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaEuAIOh9gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wOGpP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Po - +v9f5fX/YuX0/2Pl9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n - 9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Ln9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn - 9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Ln9f9y5/X/cef1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m - 9f9s5vX/bOb1/2vm9f9q5vT/aeb0/2jm9P9n5fT/ZuX0/2Xl9P9k5fT/YuX0/2Ll9P9g5PT/X+T0/17k - 9P9d5PT/W+Pz/1rj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0rg - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/zCksv8wq7n/Pd7x/zze8f863vH/Od7x/zfd8f813fH/NN3x/1HE - 0/+UlJT/XL3J/y3c8P8r2/D/Ktvw/ynb8P8m2vD/Jdrw/yTa8P9zrbT/l5eX/5aWlv+UlJT/eZ2h/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y62xf9+fn7/fHx8/3V9fv8KzOP/ANTt/wDU - 7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P833fP/AYel/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKC6AKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWQAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8tvdT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Uej7/1zl9v9h5PT/XeDx/0rM3/9Dxdn/WNjp/2fl9P9n5fT/aOb0/2nm9P9q5vT/a+b1/2zm - 9f9s5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn - 9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/cOf1/2/n9f9v5/X/b+f1/27n9f9t5vX/bOb1/2zm - 9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/XuT0/17k - 9P9c5PP/W+Pz/1rj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Dc7v8yssD/Pt7x/zze8f863vH/Od7x/zfd8f823fH/NN3x/zLc - 8f9By9v/g4OD/0XE0/8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//WrnE/5mZmf+bm5v/nZ2d/5ei - o/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S625/4qKiv+IiIj/Ray4/wDU - 7f8A1O3/ANTt/yTe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P894vf/A4+s/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HgAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISinwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Wn7r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9A1ev/JazF/waIpv8Ag6H/AIOh/wKEo/89vtP/ZeX0/2bl9P9n5fT/aOb0/2nm - 9P9q5vT/aub0/2vm9f9s5vX/bOb1/2zm9f9t5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/b+f1/2/n - 9f9v5/X/b+f1/2/n9f9v5/X/b+f1/2/n9f9u5/X/buf1/27n9f9t5vX/bOb1/2zm9f9s5vX/a+b1/2rm - 9P9q5vT/aeb0/2jm9P9n5fT/Z+X0/2bl9P9l5fT/ZOX0/2Pl9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k - 9P9b4/P/WuPz/1nj8/9Y4/P/VuPz/1bj8/9U4vP/U+Lz/1Li8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8+2+z/Pt7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zLc - 8f8x3PD/NNXo/3Jycv8z0eT/K9vw/ynb8P8o2/D/Jtrw/yTa8P8j2u//Idrv/zzB0f+IiIj/ioqK/4yM - jP+Ojo7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7P92p63/l5eX/4qa - nP8H0en/ANTt/ybe9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5fn/BZa0/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIKhMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBopQAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Giqj/R97y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/S+T4/zDB2P8QmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/1rb7P9l5fT/ZeX0/2bl - 9P9n5fT/Z+X0/2jm9P9p5vT/aub0/2rm9P9q5vT/a+b1/2vm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm - 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9r5vX/a+b1/2rm9P9q5vT/aub0/2nm - 9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9k5fT/YuX0/2Ll9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk - 8/9b4/P/WePz/1nj8/9X4/P/VuPz/1Xi8/9U4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y7c8P9adHf/K9vw/ynb8P8o2/D/Jtrw/yXa8P8j2u//Idrv/yDZ7/8j0OT/d3d3/3l5 - ef97e3v/e35//wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Eszi/4+c - nv+bm5v/QrzL/yrf9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/CaC8/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoEYAg6H+AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8/1Or/IKzG/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof9Ex9r/YuX0/2Pl - 9P9k5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9p5vT/aub0/2rm9P9q5vT/aub0/2rm - 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aeb0/2nm9P9o5vT/Z+X0/2fl - 9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Pl9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9d5PT/XOTz/1vj - 8/9a4/P/WePz/1jj8/9W4/P/VuPz/1Ti8/9T4vP/UuLz/1Hi8/9P4fP/TuHz/03h8v9L4fL/SuDy/0ng - 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y7c8P8t3PD/QI2W/yrb8P8p2/D/Jtrw/yXa8P8k2vD/Itrv/yDZ7/8b2e//BNXt/15u - cP9oaGj/ampq/2F3ef8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8yucn/ioqK/3ybnv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5/v/DqjC/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnBIAg6HkAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/HajC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/y/A - 1/8QmLP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T9Pl/2Dk - 9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl9P9l5fT/ZeX0/2bl9P9m5fT/Z+X0/2fl9P9n5fT/Z+X0/2jm - 9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2fl9P9n5fT/Z+X0/2fl9P9m5fT/ZuX0/2Xl - 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Ll9P9h5PT/YOT0/2Dk9P9f5PT/XuT0/13k9P9c5PP/W+Pz/1rj - 8/9Z4/P/WOPz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UuLz/1Dh8/9P4fP/TeHy/03h8v9L4fL/SuDy/0jg - 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/K9vw/zGruv8p2/D/J9vw/yXa8P8k2vD/Itrv/yHa7/8a2O//A9Xt/wDU - 7f9HaW3/V1dX/1paWv9CfYT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/B9bv/2mWm/97e3v/WtLh/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/D6zF/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhvQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GqAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/Co+s/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/H6vF/wSI - pf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HqK8/17k - 9P9e5PT/X+T0/2Dk9P9g5PT/YeT0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9Q0OL/Pb3S/03N - 4f9l5fT/ZuX0/2bl9P9m5fT/ZuX0/2bl9P9m5fT/ZuX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZOX0/2Tl - 9P9j5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/1nj - 8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/0/h8/9O4fP/TeHy/0zh8v9K4PL/SeDy/0jg - 8v9H4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p0OT/J9vw/yba8P8k2vD/Itrv/yHa7/8W2O//AtTt/wDU - 7f8A1O3/Mmpw/0ZGRv9JSUn/J4yX/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Dtjv/0Xn+v9T1+f/aW5v/2Gdpf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/DavG/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AYSi/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+P3/y+/1/8Pl7L/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5Ov/1jg - 8P9b4/P/XOTz/13k9P9e5PT/XuT0/1/k9P9g5PT/YOT0/2Dk9P9h5PT/YuX0/2Ll9P8prMP/AIOh/wCD - of8Ag6H/IaO8/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/Y+X0/2Pl9P9j5fT/Y+X0/2Ll9P9i5fT/YuX0/2Ll - 9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1jj - 8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9N4fL/TeHy/0vh8v9K4PL/SeDy/0jg - 8v9G4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p2/D/J9vw/yba8P8k2vD/I9rv/yHa7/8P1+7/ANTt/wDU - 7f8A1O3/ANTt/yFsdf81NTX/ODg4/xOgsf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/GNry/0vo+/9P6fz/T+n8/1WbpP9bZWb/UOb4/0/p/P9P6fz/T+n8/0/p/P9G5/r/DKnE/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh4wCApBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKUfAIOi8ACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/ySyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+1On/H6rE/wSIpf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCat/8AutX/BdTr/zfd - 8f9Z4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/17k9P9e5PT/X+T0/2Dk9P9Gyd3/AIOh/wCD - of8Ag6H/AIOh/wCDof89wNX/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9g5PT/YOT0/2Dk - 9P9g5PT/X+T0/1/k9P9e5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Z4/P/WOPz/1bj - 8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9K4PL/SODy/0fg - 8v9F4PL/ReDy/0Pf8v9C3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/I9rv/xzZ7/8I1e7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Ub3n/JCQk/ycpKv8DyN//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Jd70/07p/P9P6fz/T+n8/0/p/P9O3u//S1ZX/0+5xv9P6fz/T+n8/0/p/P9F5vn/CqXB/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOhwACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6Vsf9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/y6+1v8PlrL/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKrF/wDJ4/8A1O3/ANTt/wDU - 7f8A1O3/Ftjv/0jg8v9Z4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XOTz/13k9P9e5PT/LbHJ/wCD - of8Ag6H/AIOh/wCDof8Ag6H/JanC/1/k9P9f5PT/X+T0/1/k9P9f5PT/X+T0/1/k9P9e5PT/XuT0/17k - 9P9e5PT/XuT0/13k9P9d5PT/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1bj - 8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/1Dh8/9P4fP/TuHz/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0bg - 8v9F4PL/RN/y/0Pf8v9B3/L/Qd/y/z/f8f8+3vH/PN7x/zve8f853vH/Od7x/zfd8f823fH/Nd3x/zPd - 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/Itrv/xPX7v8B1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Cm56/xQUFP8RQ0n/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8H1u//OeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0WUnf9DeoH/T+n8/0/p/P9D5fn/CaK+/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCCoz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdQCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKGpP9B2O3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+j/HqrE/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCbt/8Autb/ANPs/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/JNrw/1Dh8/9Y4/P/WOPz/1nj8/9Z4/P/WuPz/1rj8/9b4/P/W+Pz/yWq - w/8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGlvv9d5PT/XeT0/13k9P9d5PT/XeT0/13k9P9c5PP/XOTz/1zk - 8/9c5PP/W+Pz/1vj8/9b4/P/WuPz/1rj8/9Z4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9W4/P/VeLz/1Ti - 8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9O4fP/TeHy/0zh8v9L4fL/SuDy/0ng8v9I4PL/R+Dy/0Xg - 8v9F4PL/Q9/y/0Lf8v9B3/L/QN/y/z7e8f893vH/PN7x/zve8f853vH/ON3x/zfd8f813fH/NN3x/zLc - 8f8x3PD/MNzw/y/c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/yba8P8f2e//ENfu/wPV7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wBue/8DAwP/AnSB/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8X2/H/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N3e//MENG/0/n+v864vf/Bpq3/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCBo0UAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD - ofkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/SuP2/y691f8OlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKvG/wDK5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1e3/L9zw/1Ti8/9W4/P/V+Pz/1jj8/9Y4/P/WePz/1nj - 8/8lqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8dp8H/WOT2/1vj8/9b4/P/W+Pz/1vj8/9a4/P/WuPz/1rj - 8/9a4/P/WePz/1nj8/9Z4/P/WePz/1jj8/9Y4/P/V+Pz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Pi - 8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0vh8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg - 8v9E3/L/Q9/y/0Hf8v9B3/L/P9/x/z7e8f893vH/PN7x/zre8f853vH/N93x/zfd8f813fH/NN3x/zLc - 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/x/Z7/8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AdYP/AAAA/wCvw/8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU - 7v8u4PX/T+n8/0Xc8P81x97/PNDm/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zJ4gf8qscL/AZCt/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfCACD - odMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P890uj/HqrD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AYSi/xehvP8nxt3/B9Xu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8M1u7/NN3x/1Pi8/9W4/P/VuPz/1bj - 8/9W4/P/JKrD/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9R6Pr/VuX1/1nj8/9Y4/P/WOPz/1jj - 8/9Y4/P/V+Pz/1fj8/9X4/P/VuPz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li - 8/9R4vP/UOHz/0/h8/9P4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0fg8v9G4PL/ReDy/0Tf - 8v9D3/L/Qt/y/0Hf8v9A3/L/Pt7x/z3e8f883vH/O97x/zne8f853vH/N93x/zbd8f813fH/M93x/zLc - 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/Itrv/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AH2L/wAZHP8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y - 7/9B5vr/T+n8/yy80/8BhaL/AIOh/wCDof8Vn7n/Tef6/0/p/P9P6fz/T+n8/07o/P8dtMj/BkpZ/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDolIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oY0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiqf/Rt7x/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/SuL2/y291f8OlLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Jj6v/J7bP/0bd8f9P6fz/T+n8/0no+/8Z2/L/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u7/MNzw/1Hi - 8/9U4vP/VOLz/yOqw/8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p/P9R5/r/VOX1/1bj - 8/9W4/P/VuPz/1Xi8/9V4vP/VeLz/1Ti8/9U4vP/VOLz/1Pi8/9T4vP/UuLz/1Li8/9R4vP/UeLz/1Dh - 8/9P4fP/T+Hz/07h8/9N4fL/TeHy/0zh8v9K4PL/SuDy/0ng8v9I4PL/R+Dy/0bg8v9F4PL/RN/y/0Pf - 8v9C3/L/Qd/y/0Df8v8/3/H/Pt7x/z3e8f883vH/Ot7x/zne8f843fH/N93x/zXd8f813fH/M93x/zLc - 8f8w3PD/L9zw/y3c8P8t3PD/K9vw/yrb8P8n2/D/E9fu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCImP8AV2L/ANTt/wDU7f8A1O3/AtTt/yfe - 9f9N6fv/T+n8/0Ta7v8BhKL/AIOh/wCDof8Ag6H/AIOh/yq50f9P6fz/T+n8/0rn+/8Vw9z/AIaj/wA8 - Sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCFoEsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - okIAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8bc/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P890ef/HanD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AYSi/xihvP83y+H/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8H1e3/K9vw/03h8v8iqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p - /P9Q5/r/U+P1/1Pi8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Li8/9R4vP/UeLz/1Dh8/9P4fP/T+Hz/0/h - 8/9O4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Lf - 8v9B3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f873vH/Od7x/zne8f833fH/Nt3x/zXd8f803fH/Mtzx/zHc - 8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8a2O//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Aj6D/AIyc/wDU7f8A1O3/Fdnx/0Pm - +f9P6fz/T+n8/0/p/P82yd//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T+n8/0Lm+v8MsMv/AIOh/wCD - of8AVWn/AHqW/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCDoUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH - pREAg6HiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKbB/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/y29 - 1P8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKH/FqrE/0bd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/xja - 8v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8E1e3/DabB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P5/n/UOT0/1Hi8/9Q4fP/UOHz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TeHy/03h - 8v9M4fL/TOHy/0vh8v9K4PL/SuDy/0ng8v9I4PL/SODy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf - 8v9B3/L/P9/x/z7e8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc - 8P8w3PD/Ltzw/y3c8P8r2/D/K9vw/yHa7/8K1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJao/wCzyP8I1u//M+L3/0/p - /P9P6fz/T+n8/0/p/P9P6fz/MsTb/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqn/y7e8/8Dm7f/AIOh/wCD - of8Ag6H/AH6b/wBkev8Ag6H/AIOh/wCDof8Ag6H/AIOh7QCCoDsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6GmAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY6s/0ri9v9P6fz/T+n8/0/p/P880Of/HajC/wKG - pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ats7/S+f7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/N+P3/wnW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5vn/TuL0/03h8v9N4fL/TeHy/03h8v9M4fL/TOHy/0vh - 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Df - 8v8/3/H/Pt7x/z3e8f883vH/PN7x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8y3PH/Mdzw/zDc - 8P8v3PD/Ldzw/y3c8P8r2/D/JNrw/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wGqv/8h2vH/Suf7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3S6P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ai6j/AIOh/wCD - of8Ag6H/AIOh/wCDof8AaYH/AIOh/wCDof8Ag6H/AISh3wCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKBZAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zvP5f9P6fz/SeL1/yy80/8Mk7D/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wybt/9B4vf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9L6Pv/JN30/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMH/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vj/S+L0/0rg8v9K4PL/SuDy/0ng - 8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Hf8v9B3/L/QN/y/z/f - 8f8+3vH/Pd7x/zze8f883vH/Ot7x/zne8f843fH/N93x/zbd8f813fH/NN3x/zPd8f8y3PH/MNzw/zDc - 8P8u3PD/Ldzw/yzb8P8l2vD/ENfu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8v8/3vL/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AHSP/wCDof8Ag6H/AIOgzQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yOwyv87z+b/HKfC/wKFpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoMEAg6DtAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4in/y7Q5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/Gtvy/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKTB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5fj/SeH0/0jg - 8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v8/3/H/Pt7x/z7e - 8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/N93x/zXd8f813fH/M93x/zLc8f8x3PD/MNzw/y/c - 8P8t3PD/LNvw/x7Z7/8N1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/z7l+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wB/nP8Ag6H/AIKgsgCAqgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAA//8BAIOhvQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8KkK3/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKHfAIOiewCAnRoAAAAAAICkHACD - ocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fq3G/0fm+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+5fn/Etnw/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn - wf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9K5fj/RuHz/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pd7x/zze - 8f883vH/O97x/zre8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/yzb - 8P8g2e//Etfu/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8R2PD/OuT4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKigwCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIShcgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoa0AgaJHAICAAgAAAAAAAAAAAAAAAAAA - AAAAjqoJAIOhqACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8GjKr/Ndbs/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P854/j/Etnw/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Ap8T/AIOh/wCDof8Ag6H/AIOh/wCD - of8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07p/P9J5ff/Q+Dz/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pt7x/z7e8f893vH/PN7x/zve - 8f863vH/Od7x/zne8f843fH/N93x/zbd8f813fH/Nd3x/zPd8f8y3PH/Mdzw/yjb8P8g2e//Fdju/wvW - 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xfa8f864/j/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HanD/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HzAIOhVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIWiLACDofcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKLdAIOgeQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqFsAIOh+ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Yr8j/RuX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P865Pj/F9rx/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKnF/wCDof8Ag6H/AIOh/wCD - of8Ag6H/GKG8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo+/844fX/I9rw/yHa7/8j2u//JNrw/yTV6v8bvtb/HcTb/yja - 7/8q2/D/Kdvw/yjb8P8l2vD/Itrv/yDZ7/8c2e//F9jv/xLX7v8N1u7/B9Xt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/IN3z/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq40P8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICqBgCDotIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoasAgaNFAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqgYQAfZr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wSJpv8szOL/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9E5vn/It30/wTV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wCpxf8Ag6H/AIOh/wCD - of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/8n3vX/Bdbu/wC40v8Ah6T/AIOh/wCD - of8AkrD/AM/o/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrW7/8q3/X/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P81x97/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKCfAIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoYoAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HcAIOhdwCFphcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/KAFxy/wB1kP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w+duP873fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/L+H2/xLZ8P8A1O3/ANTt/wDU7f8AqcX/AIOh/wCD - of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3Y7f8BhqT/AIOh/wCD - of8Ag6H/AIOh/wCivv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8G1e7/INzz/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEofQAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACGpCoAg6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh+QCDoakAg6FEAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXG0vAFpv/wBab/8AWm//AGiB/wCAnv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmwyf9E4/f/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/ROb5/yfe9f8K1u//AKnF/wCD - of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8QmLP/AIOh/wCD - of8Ag6H/AIOh/wCDof8Aj6v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/AtTt/xnb8v824vf/Ten8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl - +P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKJ+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HaAISidgCAohYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvlQBab/8AWm//AFpv/wBab/8AXnT/AHeT/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/yG81f9I5vn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x6w - yv8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ots7/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV - 7v8b2/L/MuH2/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/CY6s/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKiaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhmACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+ACD - oKcAg6JCAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBab+8AWm//AFpv/wBab/8AWm//AFpv/xaB - lv8xwdn/BYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4im/yPB - 2v9H5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AoWj/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC30f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8Q2PD/J971/z3k - +f9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HYAISgdACG - nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbbl8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/81us7/T+n8/0jh9P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/Aoel/yG/2P9G5fn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vn/DZSw/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCduP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v8U2fH/Jd70/zXi9/9I5/v/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/SeH1/0/p - /P9P6fz/T+n8/0nn+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8grcb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6G1AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISieACDofUAg6H/AIOh+ACDoaYAg59AAP//AQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/EAFpv/wBab/8AWm//AFpv/wBa - b/8HZnv/Teb5/0/p/P9P6fz/T+n8/zvP5v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x+40f9C4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/JLHK/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJqP8Azef/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/B9bv/xLZ8P8h3fT/MuH2/0Lm+f9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0LY7f8QmLP/AIOh/wGF - ov8eq8T/Od/1/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/LLzT/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8QAICeIgCApA4AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW20qAFpv/wBab/8AWm//AFpv/wBa - b/8AWm//JZ2x/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/yu70/8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xaowv842e7/Tun8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PNDn/wGE - ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AutX/ANTt/wDU7f8D1e7/B9bv/wzX8P8Q2fD/Fdnx/xrb - 8v8i3fT/LOD1/zfj9/9B5vr/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8Lkq7/AIOh/wCD - of8Ag6H/AIOh/xehvP9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfL4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAmZkFAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlvjwBab/8AWm//AFpv/wBa - b/8AWm//AFtw/0PT5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuL2/ySxyv8ChqT/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuUsP8syeD/SOX5/0/p - /P9P6fz/T+n8/0/p/P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/wuR - rv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8dqcP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D2e7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEltBwBab+wAWm//AFpv/wBa - b/8AWm//AFpv/xR+k/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rt3x/x2o - wv8BhKL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKF - o/8YqML/NdTq/03o+/9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x+r - xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Lm+f8m3fP/CLbR/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb1oAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8zt8r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/RNru/x2pw/8BhKH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Giqj/HbHK/zbV6/8kssv/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/znM - 4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Os3k/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/K97z/xDG3f8Aor7/AIim/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yy91P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq//AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKFyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm++AFpv/wBa - b/8AWm//AFpv/wBab/8GZHr/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0zj9v8glKn/AGB3/wBzjf8AgqD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0ni - 9f8Ijqv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/N+P3/yDX7/8KutT/AJu4/wCGpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8Xb/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AISglwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV3ImAFpv/gBa - b/8AWm//AFpv/wBab/8AWm//I5it/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/z3J3f8LboP/AFpv/wBab/8AWm//AF90/wBxjP8AgZ7/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xeh - vP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8cpsH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Ku/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9L6Pv/O+T4/yrb8f8UxNz/AqTB/wCPrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/87z+X/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yOwyv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpviABa - b/8AWm//AFpv/wBab/8AWm//AFpv/0LS5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Teb5/yWesv8BXHH/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1x/wBthf8Afpv+AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P81x97/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/z3S6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p - /P8+5fn/Lt7z/x3P5/8NuNP/AZ+8/wCOrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/z7T - 6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8vv9f/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFWABgBa - b+kAWm//AFpv/wBab/8AWm//AFpv/xJ7j/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/QdDk/w90if8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXDQAFpwMACE - oU8AhKC6AIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9I3/P/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/9A5fr/NeL3/yvc8f8g0ej/FsLa/wuw - y/8CnLj/AI6r/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Hi6n/RNrv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZ - b1MAWm//AFpv/wBab/8AWm//AFpv/wBab/8xs8f/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9O6Pv/Kqa6/wJec/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/wAWm+IAFWABgAA - AAAAAAAAAAAAAACAoh4Ag6F9AIOh3ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSPrP8cyN//Gsnh/x/L5P8gzub/IM/n/yTS - 6f8l0en/JM/m/x/M5P8ayOD/FcLa/xG61P8Nsc3/CKrF/wOfu/8AlbL/AIup/wCEov8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDocEAhKKkAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wuSrv9G3fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be - 8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWm+4AFpv/wBab/8AWm//AFpv/wBab/8FY3f/TOT2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9E1ej/E3yQ/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab9wAWG46AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAhKA+AIShmwCDoesAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/McHZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HQAIOhdwCAnyAAAAAAAAAAAACCoYcAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/DpWx/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/A4el/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWHAgAFpv/QBab/8AWm//AFpv/wBab/8AWm//IZaq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o - +/8vsMP/BGF2/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/gBbb5gAXXQLAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiQgCDoJQAg6HlAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/Rd3x/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqD/AIOhywCDoX8Ag6IpAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOipACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/Tef6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvgwBab/8AWm//AFpv/wBab/8AWm//AFpv/0DO4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0fb - 7v8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv5gBZbkgAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCD - oaAAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIGf/wB2kf8AaoL/AGB4lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq - qgMAg6GxAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xiivf9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GiAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAECABABZb+QAWm//AFpv/wBab/8AWm//AFpv/xB3jP9P6fz/T+n8/0/p/P9P6fz/T+n8/zW5 - zf8GZHr/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpvqABVcRIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/y291P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/wB3 - kv8AbYb/AGN6/wBbcP8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICZCgCDocgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/JbTN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhxgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABZcU0AWm//AFpv/wBab/8AWm//AFpv/wBab/8vsMP/T+n8/0/p/P9P6fz/St/y/xyM - oP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/tAFpuWAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0PZ7v8DhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8DhqT/EZm1/yGvyP8xwtr/Qdfs/y+x - xf8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhp4VAIOh1ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ot8//T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe0AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+zAFpv/wBab/8AWm//AFpv/wBab/8EYXb/S+L1/0/p/P9P6fz/OcLV/whp - fv8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AW2+3AF5xGwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07n+/8RmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8ChaP/Co+s/xOctv8fq8X/K7rS/zjL4f9E2u//Tuj7/0/p/P9P6fz/T+n8/0/p - /P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AW2/IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/ - 1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICcEgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAW20cAFpv+wBab/8AWm//AFpv/wBab/8AWm//H5Km/0/p/P9M5Pb/IZWp/wBb - cP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/QAWnBpAAAAAQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8puM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh9gCCon4Ag6F/AIKikQCD - oaMAgqG2AIOhuwCDobsAg6HLAIOhzACDocwAg6HMAIOhzACDosoAg6G7AIOhuwB3lPIAdI7/AHGK/wBu - h/8Aa4T/AGd//wt3jP9H3/P/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFlwiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8BhKL/N8rg/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC - oTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFpufQBab/8AWm//AFpv/wBab/8AWm//AFpv/z/M3/89yd3/C2+E/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab8YAXHEkAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9A1ev/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoGEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW252AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//P8zg/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqI3AIOh9QCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wKGpP87z+X/T+n8/0/p/P9P6fz/T+n8/0/p/P8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD - of8AhKJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBacOAAWm//AFpv/wBab/8AWm//AFpv/wxxhv8mnrP/AV1x/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+QBbcHkAVVUDAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9N5/n/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFxwGQBa - b/sAWm//AFpv/wBab/8AWm//AFpv/yGWqv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoE4Ag6H8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/BYmn/0HX7P9P6fz/T+n8/0/p/P9P6fz/Epq1/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFlv0wBcbS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/JbLL/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoesAgJ8YAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW2+0AFpv/wBab/8AWm//AFpv/wBab/8GZHn/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiYwCD - of4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/RNvv/0/p/P9P6fz/T+n8/x2owv8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+tAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/9AFlwiQBJbQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/PdHn/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJVAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtwVABab/8AWm//AFpv/wBab/8AWm//AFpv/zS4zP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFpviAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuSrv9I4fT/T+n8/0/p/P8puND/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW8XAFpv+gBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/dAFlvPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xeh - vP9P6fz/T+n8/0/p/P9P6fz/TOX4/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GlAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABVgAYAWm/sAFpv/wBab/8AWm//AFpv/wBab/8Xg5f/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0zl+P9P6fz/NMfd/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlveABab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnCZAGJ2DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD - of8Xobz/T+n8/0/p/P9P6fz/T+n8/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HlAIelEQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFtwkgBab/8AWm//AFpv/wBab/8AWm//AVxx/0fa7f9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgIACAIOipACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/Tef6/0DW - 6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBab9sAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab+YAWHBLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD - of8Ag6H/F6G8/0/p/P9P6fz/T+n8/znN4/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOfSAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYbTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8qpbn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjaH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn - wv9L5Pf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZcEIAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab6oAXmsTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCD - of8Ag6H/AIOh/xehvP9P6fz/T+n8/0ri9v8Jj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhmAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlw0ABab/8AWm//AFpv/wBab/8AWm//DHCF/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/JLHK/wiOq/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2+mAFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv7gBbb1oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCD - of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P8dqML/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi3QCA - qgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabm8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/89yNz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBb - cEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhp4VAISh2wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv+ABab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpvugBecRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCD - of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P82yN//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACC - oz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVW0VAFpv+QBab/8AWm//AFpv/wBa - b/8AWm//H5Kn/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoyQAg6HqAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhswAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdyJgBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/1AFlvagAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9I4PT/B4up/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - oIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb64AWm//AFpv/wBa - b/8AWm//AFpv/wRid/9M5Pb/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjKH/AFpv/wBab/8AWm//AFpv/wBa - b/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD - ovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodgAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWm/wAFpv/wBa - b/8AWm//AFpv/wBab/8AW2/IAFpuJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/GaO9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - odQAgJ8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXFNAFpv/wBa - b/8AWm//AFpv/wBab/8AWm//MrTI/0/p/P9P6fz/T+n8/0/p/P9P6fz/CGl+/wBab/8AWm//AFpv/wBa - b/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6JCAIOh+QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H6AIC/BAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpuXQBa - b/4AWm//AFpv/wBab/8AWm96AECABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofoAhaMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABa - b+gAWm//AFpv/wBab/8AWm//AFpv/xR9kv9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBZbkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACEoVcAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCA - oyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAW3EtAFtuhABabn0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6F/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnCLAFpv/wBab/8AWm//AFpv/wBab/8AW3D/Rdfp/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBa - b/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6LKAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFdwKQBab/8AWm//AFpv/wBab/8AWm//AFpv/yehtf9P6fz/T+n8/0/p/P8cjKH/AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H3AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm/HAFpv/wBab/8AWm//AFpv/wBab/8KbIH/Tuj7/0/p/P9P6fz/CGl+/wBa - b/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCEoJcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCEopkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOgcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFlvZwBab/8AWm//AFpv/wBab/8AWm//AFpv/zvE2P9P6fz/RNXo/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOhsQCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOhwACAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8AWm/2AFpv/wBab/8AWm//AFpv/wBab/8djqL/T+n8/zCx - xP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HDAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh8gCDoiEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvpQBab/8AWm//AFpv/wBab/8AWm//A191/0vh - 9P8cjKH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD - oe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDockAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCCoWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb0UAWm//AFpv/wBab/8AWm//AFpv/wBa - b/8wscT/CGl+/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgKQcAIOh4wCDof8Ag6H/AIOh/wCDof8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDobUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAFpv4QBab/8AWm//AFpv/wBa - b/8AWm//B2Z7/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACFphcAhKGdAIOh/QCDoesAg6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoe0AhaMZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab4MAWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAXXQLAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDonsAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8AgqBWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV20jAFpv/gBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6GpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b8EAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oZ4Ag6H/AIOh/wCDof8Ag6HQAICfEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnBgAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOgRgCDoXcAhKBbAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFVqDABab/MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/oAXXQLAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+fAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP/////////////A///////gB////////////////////////////////8B//////8AH - ////////////////////////////////wD//////wAP///////w////////////////////////AH/// - ///AA///////+D///////////////////////8AP/////4AD///////gH/////////////////////// - wA//////gAP//////8Af///////////////////////AB/////8AA///////gD////////////////// - /////+AD/////wAB//////8AP///////////////////////4AH/////AAH//////gA///////////// - ///////////gAP////4AAf/////8AD///////////////////////+AAf////gAB//////AAf/////// - ////////////////4AA////8AAH/////4AB////////////////////////gAB////wAAP/////AAH// - //////////////////////AAD////AAA/////4AAf///////////////////////8AAH///gAAAA//// - AAD////////////////////////wAAP/+AAAAAAD//4AAP////////////////////////AAAf8AAAAA - AAAf/AAA////////////////////////8AAA8AAAAAAAAAHwAAH////////////////////4f//wAAAA - AAAAAAAAACAAAf////////////////////Af//gAAAAAAAAAAAAAAAAB////////////////////8Af/ - +AAAAAAAAAAAAAAAAAH////////////////////wA//4AAAAAAAAAAAAAAAAA/////////////////// - //AA//gAAAAAAAAAAAAAAAAD////////////////////+AB/+AAAAAAAAAAAAAAAAAP///////////// - ///////4AB/4AAAAAAAAAAAAAAAAB/////////////////////wAB/wAAAAAAAAAAAAAAAAH//////// - /////////////AAD+AAAAAAAAAAAAAAAAAf////////////////////+AADgAAAAAAAAAAAAAAAAA/// - //////////////////4AAAAAAAAAAAAAAAAAAAAA/////////////////////wAAAAAAAAAAAAAAAAAA - AAA/////////////////////AAAAAAAAAAAAAAAAAAAAAB////////////////////+AAAAAAAAAAAAA - AAAAAAAAB////////////////////4AAAAAAAAAAAAAAAAAAAAAD////////////////////wAAAAAAA - AAAAAAAAAAAAAAD/////////////h//////AAAAAAAAAAAAAAAAAAAAAAH////////////8B/////8AA - AAAAAAAAAAAAAAAAAAAAH////////////wB/////4AAAAAAAAAAAAAAAAAAAAAAP////////////AB// - ///gAAAAAAAAAAAAAAAAAAAAAAf///////////8AD////8AAAAAAAAAAAAAAAAAAAAAAAf////////// - /4AD////gAAAAAAAAAAAAAAAAAAAAAAA////////////gAD///8AAAAAAAAAAAAAAAAAAAAAAAB///// - ///////AAD///gAAAAAAAAAAAAAAAAAAAAAAAD///////////+AAD//8AAAAAAAAAAAAAAAAAAAAAAAA - D///////////4AAD//gAAAAAAAAAAAAAAAAAAAAAAAAH///////////wAAD/8AAAAAAAAAAAAAAAAAAA - AAAAAAP///////////AAAD/gAAAAAAAAAAAAAAAAAAAAAAAAAf//////////+AAAD8AAAAAAAAAAAAAA - AAAAAAAAAAAA///////////4AAADgAAAAAAAAAAAAAAAAAAAAAAAAAB///////////wAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAD///////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////////+AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAP//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////////wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////// - //+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH// - ////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAf//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAB//////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////gAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAH//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////8AAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAf/////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////gH///gAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAD////+AB///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////wAB//+A - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////AAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///8A - AD//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////wAAH/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD - ////gAAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///+AAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP///4AAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gAAD+AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAB///+AAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4AAAfAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAH///gAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//+AAADgAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAA///4AAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///gAAA4AAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAD//+AAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//4AAAMAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//gAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8A - AACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//wAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAD/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAD+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAHAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAB/gAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAB/4AAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAB - /+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AD8AB//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAP/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAA/AB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8Af//gAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAfwD//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/A///gAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAP8H//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/x///AAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAH/f//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///+AAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///8AAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4 - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - A///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/4AAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/+AAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAA//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//wAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////4AA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///// - ///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - f///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////+AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAH///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAD///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAB///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////+AAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAA///////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////AAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////// - /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/ - //////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAA///////AAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////8AAH/gAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAf//////wAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////gH//8AAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAf///////H///4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////wAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAP////////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////////////+AAAAA - AAAAAAAAAAAAAAAAAAAAAAAAP////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////////// - gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// - /////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/// - //////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAA - AA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAAAAAAA - AAAAAAA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAA - AAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////gAAAAAAAAAAA - AAAAAAAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wAAAAAA - AAAAAAAAAAAAAAAAAAAAAA/////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wA - AAPAAAAAAAAAAAAAAAAAAAAAAAA//////////////AAAD+AAAAAAAAAAAAAAAAAAAAAAAP////////// - ///4AAB/+AAAAAAAAAAAAAAAAAAAAAAB//////////////AAAf/4AAAAAAAAAAAAAAAAAAAAAAP///// - ////////4AAP//gAAAAAAAAAAAAAAAAAAAAAD//////////////gAD//+AAAAAAAAAAAAAAAAAAAAAAf - /////////////+AB///wAAAAAAAAAAAAAAAAAAAAAB//////////////wAf///AAAAAAAAAAAAAAAAAA - AAAAD//////////////gP///8AAAAAAAAAAAAAAAAAAAAAAP//////////////D////gAAAAAAAAAAAA - AAAAAAAAAA///////////////////+AAAAAAAAAAAAAAAAAAAAAAD///////////////////wAAAAAAA - AAAAAAAAAAAAAAAP///////////////////AAAAAAAAAAAAAAAAAAAAAAA///////////////////8AA - AAAAAAAAAAAAAAAAAAAAD///////////////////gAAAAAAAAAAAAAAAAAAAAAAH//////////////// - //+AAAAAAAAAAAAAAAAAAAAAAAf//////////////////wAAAAAAAAAAAAAAAAAAAAAAB/////////// - ////////AAAABgAAAAAAAAAAAAAAAAAH//////////////////8AAAAPwAAAAAAAAAAAAAAAAAf///// - /////////////gAAAD/wAAAAAAAAAAAB4AAAB//////////////////+AAAAf/4AAAAAAAAAAA/wAAAH - //////////////////wAAAH//4AAAAAAAAAAH/gAAAP//////////////////AAAA///gAAAAAAAAAA/ - /AAAA//////////////////8AAAP//+AAAAAAAAAAD/+AAAD//////////////////gAAB///4AAAAAA - AAAAP/8AAAP/////////////////+AAAf///gAAAwAAAAAA//4AAA//////////////////4AAD///+A - AAH//wAAAH//wAAD//////////////////AAA////4AAAf//AAAAf//gAAH/////////////////8AAH - ////gAAD//8AAAB///AAAf/////////////////gAA////+AAAf//4AAAH//+AAB//////////////// - /+AAP////4AAB///gAAA///4AAH/////////////////4AB/////gAAP//+AAAD///wAAf////////// - ///////AAf////+AAB///8AAAP///gAB/////////////////8AD/////4AAH///wAAA////AAH///// - ////////////gA//////gAA////gAAH///+AAP////////////////+AH/////+AAH///+AAAf///8AA - /////////////////4B//////4AAf///4AAB////4AD/////////////////gP//////gAD////wAAH/ - ///wAP/////////////////D//////+AAf////AAA/////gA/////////////////+///////4AD//// - 8AAD/////AD/////////////////////////gAP////4AAP////8AP////////////////////////+A - B/////gAA/////4Af////////////////////////4AP/////AAH/////wB///////////////////// - ////gA/////8AAf/////gH////////////////////////+AH/////wAB//////Af/////////////// - /////////4A//////gAH/////+D/////////////////////////gD/////+AA//////8f////////// - //////////////+Af/////4AD////////////////////////////////8D//////wAP//////////// - ////////////////////wP//////AA/////////////////////////////////B//////+AH/////// - /////////////////////////////////4Af////////////////////////////////////////gB// - ///////////////////////////////////////AH///////////////KAAAAIAAAAAAAQAAAQAgAAAA - AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgJwSAIOh+gCDof8Ag6H/AIKjLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvrwBab/8AWm//AFpv/wBa - b/8AWm+eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuiCwCZmQUAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6H/AIOh/wCDof8Ag6H4AIOfJQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABecRsAWm/7AFpv/wBab/8AWm//AFpv/wBab9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDoK8Ag6H/AIOh2gCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpufwBab/8AWm//AFpv/wBa - b/8AWm//AFpv/QBidg0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAg6GYAIOh/wCDof8Ag6H/AIGgQwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCD - of8Ag6HaAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABAgAQAWm/iAFpv/wBab/8qprr/BmR6/wBab/8AWm//AFlwQgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgKoMAIOhtQCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKGbAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HPAIiZDwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBab/8AWm//Al1y/0jd - 8P8Xg5f/AFpv/wBab/8AWW94AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCDoM0Ag6H/AIOh/wCDof8Ag6H/AIOh8wAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6HDAICZCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm+1AFpv/wBab/8bi6D/T+n8/yehtv8AWm//AFpv/wBZb64AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF - oiwAg6HhAIOh/wCDof8Ag6H/AIOh/wCDof8AhKKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/CY+s/yWzzP8Ag6H/AIOh/wCDof8Ag6K3AICqBgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFprHwBab/0AW3H/AF1z/zO9 - 0v9D2e7/LLPJ/wBief8AYXf/AF926QCAoh4AkpIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOi8ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - olIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQcAIOh/wCD - of8Ag6H/SuP2/yGtx/8Ag6H/AIOh/wCDof8Ag6GpAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAICZCgCC - oDsAg6JrAIOhkACDoK8AfpvnAICd/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6HqAIOi0gCCoLIAgqCJAIKiWgCFoiwAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShXwCD - ofkAg6H/AIOh/wGEov8grMb/AIOh/wCDof8Ag6H8AIiZDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYFAFxwGQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6LwAIOh/wCDof8+0+j/Tuj7/xynwv8Ag6H/AIOh/wCD - of8Ag6GaAP//AQCAnxgAgqJgAIOhngCDotIAg6H7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofUAhKC6AIOhdwCCnzUAgIACAP//AQCDoX8Ag6H+AIOh/wCDof8EiKX/OMvh/yq50f8Ag6H/AIOh/wCD - ocMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABZb78AWm//AFpv/QBabz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - ocYAg6H/AIOh/zHB2f9P6fz/Tef6/xihvP8Ag6H/AIOh/wCDof8Ag6LoAIOh/gCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoeUAg6HWAIOh/wCD - of8Ag6H/CI2q/z/U6v9P6fz/FJ24/wCDof8Ag6H/AIOhfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW0HAFpv/wBab/8AWm//AFpv/wBZ - cKAAWXMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgnACDof8Ag6H/JLHK/0/p/P9P6fz/TOX4/xSc - uP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wCOrP8AmLT/AKK9/wCq - xf8Cr8r/BbXQ/wW30f8Du9X/ALnU/wC10P8As87/ALHN/wCrxv8Aob3/AJm2/wCPq/8AhKL/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w2UsP9G3fH/T+n8/0vk+P8ChaT/AIOh/wCD - of8AhKE2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWm/4AFpv/wBab/8AWm//AFpv/wBab+wAWm9jAAAAAQAAAAAAAAAAAAAAAAAA - AAAAhKFyAIOh/wCDof8WoLv/T+n8/0/p/P9P6fz/SuP2/xCYtP8Ag6H/AIOh/wCDof8Ag6H/AIWj/wGX - s/8Nr8z/Gsff/yfY7/804vf/PeT5/0Lm+f9F5fj/OMzh/yayyv9M5fj/T+n8/0/p/P9P6fz/TOj7/0rn - +/9F5/r/PuX5/zPi9/8o3/T/Htzz/xPW7v8GxN7/ALLO/wCeuv8Aiaf/AIOh/wCDof8Ag6H/AIOh/wCD - of8Unbj/SuL2/0/p/P9P6fz/OMvh/wCDof8Ag6H/AIOh7QCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabz4AWm//AFpv/wBa - b/8AWm//AFpv/wBab/8AWm/DAF1uLAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wmPrP9P6fz/T+n8/0/p - /P9P6fz/SOH0/w2TsP8Ag6H/AIOh/wSLqf8+5Pn/Ten8/0/p/P9P6fz/T+n8/03m+v82x93/G6O8/wSF - oP8Af5v/AH+b/zC/1f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9B5vr/L+D2/xfK4v8Biqj/AIOh/wCDof8Ag6H/HanD/03n+v9P6fz/T+n8/0/p/P8hr8j/AIOh/wCD - of8Ag6CnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb8UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AFpviABm - ZgoAgqExAIOhxgCDof8Ag6H/AIOh/0vk9/9P6fz/T+n8/0/p/P9P6fz/Rt3y/wCDof8Ag6H/FZ+5/0/p - /P9P6fz/T+n8/0ri9v8otcz/CIql/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpKs/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P880Ob/BIil/wCDof8Ag6H/AIOh/ye1 - zv9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCEobgAgJ8QAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBa - b/8AWm//AFpv/w92iv8AWm//AFpv/wBab/8AXnT/AHSP8wCDof4Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p - /P9P6fz/T+n8/0/p/P9P6fz/S+T4/zrO5P9N5/n/T+n8/0zl+P8qt87/BYei/wB/m/8Af5v/AH+b/wB/ - m/8djqb/NZqw/wB/m/8Af5v/Hr7U/ynT6P8s1ej/L9bq/zPY7P883fD/R+P1/07m+f9P6fz/T+n8/0/p - /P9P6fz/T+n8/xiivf8Ag6H/AIOh/wKFo/8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/AIOh/wCD - of8Ag6H/AIOh/wCDoesAg6BpAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAFlv0wBab/8AWm//H5Gm/zrC1v8Lb4P/AGuE/wCB - nv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xwtn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P890uf/Co+q/wB/m/8Af5v/AH+b/wGAm/85nrb/j87f/7ro9/+Lydn/EYii/wB/m/8Ak67/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/BcLY/w7F2f8Yy+D/J9Pn/zrc8P9L5fj/Lb3U/wCDof8Fiqf/Os7k/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HFAIOiKQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWW9cAFpv/wBab/8CXXL/Ps/j/xOct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weVsf8Ag6H/AIOh/ySx - yv9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+/8y3/T/EK/I/wCAnP8Af5v/AH+b/wB/m/9ApLv/pdzs/8fw - /v/I8P7/yPD+/7Xk8/86nLL/AH+b/wCAnP8At8//AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wLA1/8Rx9z/H8ng/zXY7f9O5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GaO9/wCD - of8Ag6H/AYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H4AIShegCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6B5AIOh5QCDor8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWnDgAFpv/wBqgv8ChKL/AIOh/wCD - of8Ag6H/AIOh/wqfvP823fL/LLzT/wCDof8Ag6H/F6C7/0/p/P9P6fz/S+j7/zHh9v8S0+n/Acbd/wCf - uP8Af5v/AH+b/wB/m/8RiaT/jtDj/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/3rA0f8EgZ3/AH+b/wCb - tf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wbE - 2f8e0Ob/OuDz/07o/P9P6fz/T+n8/0/p/P8Fiqf/AIOh/wCDof8zy+L/HMXd/wCJp/8Ag6H/AIOh/wCD - of8Ag6H/AIOhxgCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDofYAg6H/AIOh/wCD - of8Ag6GoAIahJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABecnIAdI//AIOh/wCDof8Ag6H/AIOh/wGMqP8nz+f/TOj7/0/p/P83yuD/AIOh/wCD - of8Lkq7/ROb5/yLc8/8Ezeb/AMTb/wDB2P8Apb//AH+b/wB/m/8Af5v/NJ63/7bn9//H8P7/yPD+/8jw - /v/G8P7/uuz+/8jw/v/I8P7/ptzs/yeTqv8Af5v/AIKe/wC81P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/BsXc/yHT6f9E5fj/T+n8/xKa - tf8Ag6H/CI6r/03n+f9P6fz/PuP4/xGvyf8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACColgAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOh8gCDof8Ag6H/AIOh/wCDof8Ag6H7AIShmwCEnh0AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh0QCDof8Ag6H/AIOh/wCD - of8NqMT/P+P4/0/p/P9P6fz/T+n8/0fe8v8Ag6H/AIOh/wicuf8Bzub/AMTb/wDC2f8Awtn/ALTM/wCA - nP8Af5v/AH+b/0Glvv+96/z/xO/+/8bw/v/H8P7/vO3+/6bn/v+i5v7/uOz+/8Lw/v+77vz/W6/D/wB/ - m/8Af5v/AKO9/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA - 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHC2P8a0Ob/Ot3x/zXH3v9I3/P/T+n8/0/p/P9P6fz/T+n8/zPZ - 7/8HkrD/AIOh/wCDof8Ag6H/AIOh/wCCoI8AqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhqQqAIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCDoo4AgKYUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOhRACDoewAg6H/AIOh/wCDof8Ah6T/Hsrh/03p+/9P6fz/T+n8/0/p/P9P6fz/Suf7/xzP - 5v8Bs83/AMPa/wDD2f8Aw9n/AMPZ/wDB1/8AiqX/AH+b/wB/m/8znrj/u+v8/8Lv/v/D7/7/w+/+/6/q - /v+i5v7/oub+/6Hl/f+g5/3/vO/9/7bu/f+Q0+P/E4mi/wB/m/8AiKP/AMHY/wDC2f8Awtn/AMLZ/wDC - 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Awtn/Es3j/zjg9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+v8YsMr/AIOh/wCDof8Ag6H/AIOh/wCD - orcAiJkPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KWAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - ofMAg6GAAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoFYAg6H5AIOh/wCDof8Ag6H/ApGt/y/b - 8P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLh9/8G1e7/AM3l/wDE2/8AxNv/AMTb/wDE2/8AxNv/AKS+/wB/ - m/8Af5v/EYql/6/m+f+/7v7/we7+/8Du/v+p6P7/oub+/6Lm/v+g5f3/l+T9/43k/P+s7P3/sO79/6bq - +f9Bobf/AH+b/wB/m/8Arsb/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD - 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/xXP5f9B5Pf/T+n8/0/p - /P9P6fz/T+n8/0/p/P8uz+b/A4ak/wCDof8Ag6H/AIOh/wCCoNUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG - nhUAg6HxAIOh/wCDof8Fiqf/CY+s/wCDof8Ag6H/AIOh/wCDof8Ag6HrAIOicwCOqgkAAAAAAAAAAAAA - AAAAgqBmAIOh/ACDof8Ag6H/AIOh/wSfu/864vf/T+n8/0/p/P9P6fz/T+n8/0jn+/8Z2/L/ANLq/wDI - 4P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDC2v8AhKD/AH+b/wB/m/93x97/ve3+/77u/v+/7v7/qej+/6Lm - /v+i5v7/n+X9/5bk/f+M4/z/g+P8/4/n/P+q7f3/pO38/3rJ2v8Fgp3/AH+b/wCQq/8AxNv/AMTb/wDE - 2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD - 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPZ/wHF2/8g1er/Sej7/0/p/P9P6fz/T+n8/0/p/P9C4vX/D5m0/wCD - of8Ag6H/AIOh/wCDoewAg59AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoXcAg6H/AIOh/wKFpP9C2e3/LL3U/weL - qf8Ag6H/AIOh/wCDof8Ag6H/AIOh5ACCoGYAgL8EAIShcgCDof4Ag6H/AIOh/wCDof8IqMP/QOX5/0/p - /P9P6fz/T+n8/0/p/P895Pn/CNfv/wDQ6P8AyN//AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AK3F/wB/ - m/8Af5v/FIyo/7Xq/f+87f7/ve3+/7Lq/v+i5v7/oub+/57l/f+V5P3/i+P8/4Li/P954vv/ceH7/6Ds - /P+e7Pz/keT0/ymUrP8Af5v/AH+b/wC30P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF - 3P8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE - 2/8Fyd//M97y/0/p/P9P6fz/T+n8/0/p/P9N6Pv/ILHK/wCDof8Ag6H/AIOh/wCDofoAgqJYAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICfCACDoeAAg6H/AIOh/xumwP9P6fz/S+T4/yi3z/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCD - oOoAhKH+AIOh/wCDof8Ag6H/Ca3J/0Tm+v9P6fz/T+n8/0/p/P9P6fz/L+H2/wLV7f8Azeb/AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Al7H/AH+b/wB/m/9UtM3/uez+/7rt/v+87f7/pef+/6Lm - /v+d5f3/lOT9/4rj/P+B4vz/eOH7/27g+/9k4Pr/guf7/5jr+/+T6/v/YLzP/wB/m/8Af5v/AJmz/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axdz/AMXc/wDF - 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/HNPp/0vn+v9P6fz/T+n8/0/p - /P9P6fz/KsLZ/wCDof8Ag6H/AIOh/wCDofwAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiWACDof8Ag6H/AIOh/z3S - 6P9P6fz/T+n8/0nh9f8kssv/A4ak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmyzf9E5vr/T+n8/0/p - /P9P6fz/T+n8/yTe9P8A1O3/AM3l/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCF - oP8Af5v/AH+b/4nS6f+47P7/uez+/7br/v+i5v7/nOX9/5Pk/f+J4/z/gOL8/3fh+/9t4Pv/ZN/6/1rf - +f9d4Pn/kev7/4zq+/9+2+3/FYqj/wB/m/8AgZz/AMDY/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH - 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG - 3f8Axt3/AMbd/wDG3f8Axt3/Ccrh/z3h9f9P6fz/T+n8/0/p/P9P6fz/M8/l/wKFpP8Ag6H/AIOh/wCD - ofwAg6BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAA//8BAIOhyACDof8Ag6H/EZm0/0/p/P9P6fz/T+n8/0/p/P9H3vL/IKzG/wGF - ov8Ag6H/AIOh/wCDof8Ag6H/EJq2/zzQ5v9P6fz/T+n8/03p/P8c2/L/ANTt/wDN5v8AyuL/AMri/wDK - 4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AxNz/AH+b/wB/m/8CgZz/qef8/7fs/v+47P7/r+n+/5vl - /f+S5P3/ieP8/3/i/P924fv/bOD6/2Pf+v9Z3vn/UN75/0bd+P955/r/her6/4Do+v9Dqb//AH+b/wB/ - m/8Aor3/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI - 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMfe/wDH3v8Ax97/A8jf/zPa - 7v9P6fz/T+n8/0/p/P9P6fz/PNft/wWKp/8Ag6H/AIOh/wCDof4AgqBmAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKM6AIOh/gCD - of8Ag6H/NMfd/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/xynwv8BhKL/AIOh/wCDof8Ag6H/AIOh/xih - vP9K5fj/GNrx/wDU7f8Azeb/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wC+ - 1/8Af5v/AH+b/xOLp/+u6f7/tuv+/7fs/v+l6P3/keT9/4jj/P9+4vv/deH7/2vg+v9i3/r/WN75/0/d - +f9F3fj/PNz4/1Hg+P9/6fr/fun6/3HS4/8Ggp3/AH+b/wCGof8Ax+D/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI - 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/Acjg/yrV6f9O5/r/T+n8/0/p/P9P6fz/QNvw/wWJ - p/8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GrAIOh/wCDof8KkK3/Teb5/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0HX7P8Yobz/AIOh/wCDof8Ag6H/AIOh/w+91v8A1O3/AM7m/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AL7X/wB/m/8Af5v/HZKt/63p/v+16/7/r+v9/5rm - /P+H4/z/feL7/3Ph+/9q4Pr/Yd/6/1je+f9O3fn/RNz4/zvc+P8y2/f/K9v3/3jo+v9+6fr/fuf4/y2a - sf8Af5v/AH+b/wCvyP8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK - 4v8AyuL/AMri/wDK4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ - 4f8AyeH/AMnh/yDT6f9N5Pj/T+n8/0/p/P9P6fz/Ptrv/wWJp/8Ag6H/AIOh/wCDof4AhKBbAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA - niIAg6H4AIOh/wCDof8qutL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/FJ23/wCD - of8Bh6T/AMfh/wDP5/8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AxN3/AH+b/wB/m/8akKv/q+j+/6vq/f+m6v3/kub8/3zi+/9y4fv/aeD6/2Df+v9X3vn/Td35/0Pc - +P862/j/Mdr3/yfa9/8n2vf/WOL5/37p+v9+6fr/Ycfa/wB/m/8Af5v/AI6p/wDM5P8AzOT/AMzk/wDM - 5P8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL - 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xvR5/9M4/b/T+n8/1vr - /P9h6/z/R9ru/wKFo/8Ag6H/AIOh/wCDofkAg6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYsAg6H/AIOh/wSJpv9I4fT/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HM7m/wDM5v8A0en/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDK5P8Af5v/AH+b/weEn/+d5fz/oun9/53o - /f+N5vz/ceH7/2jg+v9f3/r/Vd75/0zd+f9C3Pj/Odv4/zDa9/8n2vf/J9r3/yfa9/813Pj/fun6/37p - +v985PX/GIym/wB/m/8Af5v/ALrT/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM - 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM - 5P8AzOT/AMvj/wDL4/8EzOP/Qdjq/47p9P/J9/z/0fn+/9H5/v/R+f7/t+72/2m2yP8Vjan/AIOh/wCD - ovAAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICfEACDoesAg6H/AIOh/yGux/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/y/h - 9v8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wCHo/8Af5v/AH+b/3vU7f+Z6P3/k+f8/43n/P9r4fr/Xt/6/1Te+f9L3fn/Qdz4/zjb - 9/8v2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9p5fn/fun6/37p+v9KuM7/AH+b/wB/m/8AmLL/AM3m/wDN - 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN - 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8EzuX/VN3u/6vv9v+98vj/vfL4/8b0 - +v/R9/z/0fn+/9H5/v/R+f7/xu70/7nd5f9escT/AoSi/wCDoeoAgKMkAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWjGQCDopYAg6GYAISgdACB - o0UAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCDof8Ag6H/AYWi/0LZ - 7f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5vr/AdXt/wDT7P8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AJiy/wB/m/8Af5v/QrHL/43m - /P+J5vz/g+b7/33l+/9l4fr/S935/0Dc+P832/f/Ltr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Xf - +P9+6fr/fun6/3bd7/8Ggp3/AH+b/wCAnP8AxNz/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO - 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDN5v8Azeb/AM3m/wDN - 5v8Bzeb/Rtvt/6fu9v+98vn/oOz1/3fd6/9Sytz/RcDT/0u80P9jx9f/fNXk/6vs9f/Q+f7/wunv/73f - 5/+Qydb/D4qn/wCEodsAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKG+AIOh/wCDof8Ag6H/AIOh/wCDofsAg6G1AIGjRQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAmZkFAIOh4gCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj8/wfW - 7v8A1O3/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR - 6f8A0en/ANHp/wDR6f8Ascv/AH+b/wB/m/8Af5v/euD4/37l+/955fv/c+T6/3Dk+f9l4/n/Rt74/y3a - 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/Kdr3/3fo+v9+6fr/fun6/zOmvf8Af5v/AH+b/wCi - vP8A0Oj/ANDo/wDQ6P8A0Oj/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/L9jr/5nr9f+i7PX/W9Dg/xunwP8Ah6P/AIGe/wCB - nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/xeQrP9zz97/td/o/73f5/+e0Nv/E4yo/wCCoLIA//8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoeYAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOhowCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6HeAIOh/wCD - of8Ag6H/Os7k/0/p/P9P6fz/T+n8/0/p/P8c3PL/ANTt/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDN5v8Agp3/AH+b/wB/ - m/8MiqX/Rb/Y/2vi+f9q4/n/ZeP5/2Hj+f9d4/n/TeH4/zDc9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa - 9/8n2vf/VuL5/37p+v9+6fr/Z9Ll/wB/m/8Af5v/AIOf/wDN5f8A0en/ANHp/wDR6f8A0en/ANHp/wDR - 6f8A0en/ANHp/wDR6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/G9Xq/4Ho - 9P+J5O//NbbM/wGHo/8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB - nv8Xkaz/fsDQ/73f5/+f0Nz/BYWj/wCDoHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhzACJ - nQ0AAAAAAAAAAAAAAAAAAAAAAIShXwCDof8Ag6H/AIOh/wCDof8OlbH/Tuj7/0/p/P9P6fz/O+T4/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wCeuf8Af5v/AH+b/wB/m/8Af5v/EZKu/0TL5P9a4vn/VuL5/1bi - +f9b4/n/XeP5/0ng+P8r2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8z3Pf/fun6/37p+v9+6Pn/HZWu/wB/ - m/8Af5v/AK7I/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS - 6v8A0ur/ANLq/wDR6f8A0en/FNXr/2rk8v972uj/Jqa9/wCBnv8AgZ7/AIyp/wCatf8Apb//AKrF/wCr - xv8AqsT/AKO+/wCYtP8AhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8BgZ7/XK7C/7ze5v9XrcH/AIOh/ACB - oUEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCD - of8Ag6H/ALbR/wCbuP8Ag6L/AIOh/wCDof8Ag6H/AIOhmAAAAAAAAAAAAAAAAACAnwgAg6HiAIOh/wCD - of8Ag6H/AIOh/wCDof8xwdn/T+n8/0zo/P8D1u3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wC0 - zv8AiKP/AH+b/wB/m/8Af5v/AH+b/xafu/9B1O3/VOL5/1ji+f9c4/n/X+T5/17j+f9B3vj/KNr3/yfa - 9/8n2vf/J9r3/yfa9/9m5fn/fun6/37p+v9Sxdv/AH+b/wB/m/8Ai6b/ANLr/wDT7P8A0+z/ANLr/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Fdbt/2ff7v9uz9//Hpix/wCJ - pv8Aor3/ALnS/wDM5f8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8Aw9v/EqrE/waI - pf8AgZ7/AIGe/wCBnv8AgZ7/QKG3/7DZ4v8Qi6f/AIOg6ACAnRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoZAAg6H/AIOh/wCEov8A1O3/ANTt/wDF3v8Aiqf/AIOh/wCD - of8Ag6H7AIOiIQAAAAAAAAAAAIOgcQCDof8Ag6H/AIOh/wOTsP8Ag6H/AIOh/wiNqv9L5Pj/Htzy/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDQ6f8AqML/AIKe/wB/m/8Af5v/AH+b/wGB - nf8jrsj/T9z0/1nj+f9c4/n/YOT5/2Tk+f9c4/n/ON34/yfa9/8n2vf/J9r3/0Lf+P996fr/fun6/3rl - 9/8Kh6P/AH+b/wB/m/8AudT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT - 7P8A0+z/JNnv/1nU5v9SvM//D5ex/wCow/8Awdv/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDS - 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8C0+v/O+D0/y691P8Giab/AIGe/wCBnv8AgZ7/Tqi8/3G6 - y/8Ag6H/AIOhwwCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKigwCD - of8Ag6H/AIOh/wDS6/8A1O3/ANTt/wC30v8Ag6H/AIOh/wCDof8AgqGNAAAAAACAvwQAg6HhAIOh/wCD - of8Aiaf/KdTq/wOHpf8Ag6H/AIOh/yCzzP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8C1O3/AtTt/wLU7f8Dy+T/AqC6/wCAnP8Af5v/AH+b/wB/m/8Fh6P/MLzV/1ji9/9e4/n/YeT5/2Xl - +f9o5fn/VuL5/y/b9/8n2vf/Ot34/3ro+v9+6fr/fun6/zy2zv8Af5v/AH+b/wCUr/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//PtTo/0zE1v84tcv/Aq/J/wDJ4v8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT - 7P8I1u7/SOb5/0rh9f8hq8T/AYKf/wCBnv8AgZ7/Wq7B/yGTrf8Ag6H/AIOihgAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KGAIOh/wCDof8Ag6H/ANLq/wDU7f8A1O3/ANPs/wCP - rP8Ag6H/AIOh/wCDodEAAAAAAIKgVgCDof8Ag6H/AIOh/wqyzf9N6fz/J7bP/wCDof8Ag6H/AIek/wDS - 6v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8I1e7/B8Xe/wKV - sP8Af5v/AH+b/wB/m/8Af5v/DZGs/0DK4v9f5Pn/YuT5/2bl+f9q5fn/beb6/3Hn+v905/r/d+j6/3zp - +v9+6fr/Z9zw/wB/m/8Af5v/AH+b/wDD3f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BMri/wa9 - 1v8Av9r/ANDp/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8T2fD/Ten8/0/p/P85y+L/Boil/wCB - nv8AgZ7/TKe8/wCDof8Ag6H9AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oZAAg6H/AIOh/wCDov8A1O3/ANTt/wDU7f8A1O3/AKfD/wCDof8Ag6H/AIOh/QCOqgkAg6HHAIOh/wCD - of8AhaP/Md70/0/p/P9M5fj/CY+r/wCDof8Aiqf/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW - 7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8N1u7/Dtbu/wq60/8Ci6f/AH+b/wB/m/8Af5v/AH+b/xqf - uv9Q1u3/ZOT5/2fl+f9r5vr/bub6/3Ln+v915/r/eOj6/1je9P8XqMT/AH+b/wB/m/8Af5v/AZ65/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8h3fP/T+n8/0/p/P9F3fD/EJex/wCBnv8OiaP/JpWv/wCDof8Ag6HmAIaeFQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhqACDof8Ag6H/AIyq/wDU7f8A1O3/ANTt/wDU - 7f8AqcX/AIOh/wCDof8Ag6H/AIOiQgCDof8Ag6H/AIOh/weow/9N6Pz/T+n8/0/p/P9C3PD/A6/L/wDH - 4f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wPV - 7f8E1e3/BtXt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8P1+7/ENfu/xDX7v8Q1+7/Edfu/xLX - 7v8S1+7/Etfu/xPV7P8Kr8n/AYSg/wB/m/8Af5v/AH+b/wKAnf8pr8j/Xt/0/2jl+f9s5vr/cOb6/2Lk - +P8oudP/AoWg/wB/m/8Af5v/AH+b/wCAnf8DpsD/BNXt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8x4fb/T+n8/0/p - /P9M5fj/FJ23/wCBnv8Yjqn/A4Si/wCDof8AgqK0AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg6HQAIOh/wCDof8AmbX/ANTt/wDU7f8A1O3/ANTt/wCfu/8Ag6H/AIOh/wCDoe0Ag6GQAIOh/wCD - of8Ag6H/JtXt/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wTV7f8G1e3/B9Xt/wnW7v8K1u7/DNbu/w3W7v8O1u7/ENfu/xDX - 7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xXY7v8W2O//Ftjv/xfY7/8X2O//F9jv/xfY7/8V0ej/CqS+/wCA - nf8Af5v/AH+b/wB/m/8Gh6H/O77W/2Lj+P85yOH/B46q/wB/m/8Af5v/AH+b/wB/m/8Dl7L/Ccjh/wnW - 7v8I1e7/B9Xt/wXV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8/5fn/T+n8/0/p/P9M5vn/EZ23/wCCoP8EhaL/AIOh/wCD - of8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCCoPwAgqD/AIOh/wCpxf8A1O3/ANTt/wDU - 7f8A1O3/AJCt/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGRrv9F5/r/T+n8/0/p/P9M6Pv/Bdbu/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/A9Xt/wTV7f8H1e3/CNXu/wrW - 7v8M1u7/Ddbu/w7W7v8Q1+7/Edfu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xjY7/8Z2O//Gtjv/xrY - 7/8b2e//G9nv/xvZ7/8c2e//HNnv/xzZ7/8b2e//F8ri/wiYsv8Af5v/AH+b/wB/m/8Af5v/AYCb/wB/ - m/8Af5v/AH+b/wB/m/8DjKj/Db7X/xDX7v8P1+7/Dtbu/wzW7v8L1u7/Cdbu/wfV7f8F1e3/BNXt/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX - 7/9L6Pv/T+n8/0/p/P9N5/r/Cpm1/wCCof8Ag6H/AIOh/wCDoewAhp4VAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKBZAIKg/wCBnv8AgJ7/ALvU/wDU7f8A1O3/ANTt/wDU7f8AhqT/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/F77X/0/p/P9P6fz/T+n8/zHh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8B1O3/A9Xt/wXV7f8H1e3/Cdbu/wvW7v8M1u7/Dtbu/xDX7v8R1+7/E9fu/xTX7v8V2O7/F9jv/xjY - 7/8a2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z7/8f2e//H9nv/x/Z7/8f2e//INnv/yDZ7/8g2e//INnv/yDZ - 7/8g2e//H9nv/xbA2P8Fjqj/AH+b/wB/m/8Af5v/AH+b/wB/m/8ChaD/DbDJ/xfW7f8W2O//Fdju/xPX - 7v8S1+7/ENfu/w7W7v8N1u7/C9bu/wnW7v8H1e3/BdXt/wTV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x7d8/9P6fz/T+n8/0/p/P9I5Pf/Ao+q/wCD - of8Ag6H/AIOh/wCEoZsAAAAAAAAAAAAAAAAAAAAAAAAAAACCorQAg6H/AIKg/wCDof8AyuP/AMzk/wDO - 5v8A0uv/ANTt/wCcuP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof813vT/T+n8/0/p/P9P6fz/Edjw/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xDX - 7v8T1+7/FNfu/xXY7v8X2O//Gdjv/xrY7/8b2e//HNnv/x7Z7/8f2e//INnv/yHa7/8h2u//Itrv/yPa - 7/8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yLZ7f8UtMz/A4ai/wB/ - m/8BgJ3/DaS+/xzS6P8c2e//HNnv/xrY7/8Z2O//GNjv/xbY7/8U1+7/E9fu/xDX7v8P1+7/Ddbu/wvW - 7v8J1u7/B9Xt/wXV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p/P8r1uv/AIil/wCDof8Ag6H/AIOh/gCEnzgAAAAAAAAAAAAA - AAAAhKUfAIOh+wCDof8Ag6H/AJy5/wDU7f8A0uv/AM/n/wDL4/8Ay+P/AMfg/wCcuf8AhKL/AIOh/wCD - of8Ag6H/BZy4/03p/P9P6fz/T+n8/0Hl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV - 7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xHX7v8T1+7/Fdju/xbY7/8Y2O//Gtjv/xvZ7/8d2e//H9nv/yDZ - 7/8h2u//Itrv/yTa8P8k2vD/Jdrw/yba8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb - 8P8p2/D/Kdvw/ynb8P8o2/D/J9vw/yba8P8k1Ov/E6/H/x7L4v8k2vD/Itrv/yHa7/8f2e//H9nv/x3Z - 7/8b2e//Gtjv/xjY7/8W2O//Fdju/xPX7v8R1+7/D9fu/w3W7v8L1u7/Cdbu/wfV7f8E1e3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/C9fv/03p+/9P6fz/T+n8/03p - /P8IuNP/AIOh/wCDof8Ag6H/AIShuAAAAAAAAAAAAAAAAACDoZIAg6H/AIOh/wCDof8Au9X/ANTt/wDU - 7f8A1O3/ANTt/wDT7P8Azeb/AMvj/wDJ4P8Av9n/AK3J/wCsx/8a1+7/T+n8/0/p/P9P6fz/Jd70/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wPV7f8F1e3/CNXu/wrW7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY - 7/8Z2O//Gtjv/xzZ7/8e2e//H9nv/yHa7/8j2u//JNrw/yXa8P8m2vD/KNvw/ynb8P8q2/D/K9vw/yvb - 8P8s2/D/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb - 8P8p2/D/Kdvw/yjb8P8m2vD/Jdrw/yTa8P8i2u//Idrv/x/Z7/8e2e//HNnv/xrY7/8Y2O//F9jv/xXY - 7v8T1+7/ENfu/w7W7v8M1u7/Ctbu/wfV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Kd/0/0/p/P9P6fz/T+n8/yvg9f8Amrj/AIOh/wCDof8Ag6H+AIakKgAA - AAAAgKYUAIOh8wCDof8Ag6H/AI6q/wDS6v8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDP - 5/8A0Oj/ANLr/zLh9v9L4/f/Os7k/z3S6P8LyOD/AMni/wDO5v8A0er/ANTt/wLU7f8E1e3/B9Xt/wnW - 7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY7/8Z2O//G9nv/xzZ7/8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb - 8P8p2/D/Ktvw/yvb8P8s2/D/Ldzw/y7c8P8v3PD/MNzw/zDc8P8x3PD/Mtzx/zLc8f8y3PH/Mtzx/zLc - 8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/L9zw/y3c8P8t3PD/LNvw/yvb8P8p2/D/KNvw/yba - 8P8l2vD/JNrw/yHa7/8g2e//Htnv/xzZ7/8a2O//GNjv/xbY7/8U1+7/Etfu/xDX7v8N1u7/C9bu/wnW - 7v8G1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8G1e7/S+j7/0/p - /P9P6fz/R+f6/wHG3/8Ag6H/AIOh/wCDof8Ag6CEAAAAAACEoHwAg6H/AIOh/wCDof8Ascv/ANLq/wDQ - 6P8Azub/ANDo/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/RuX3/wqPrP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIim/wGMqf8Bj63/A5Sx/wSZtf8Fm7j/B5+6/wmlwf8LqcT/DazG/w+x - yv8Sts//FbrS/xe91f8aw9v/Hsbe/yLO5P8q2/D/K9vw/y3c8P8u3PD/MNzw/zDc8P8y3PH/Mtzx/zPd - 8f813fH/Nd3x/zXd8f823fH/Nt3x/zfd8f833fH/N93x/zfd8f833fH/Nt3x/zbd8f813fH/Nd3x/zTd - 8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//INnv/x7Z - 7/8c2e//Gtjv/xjY7/8V2O7/E9fu/xDX7v8O1u7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8s4PX/T+n8/0/p/P9P6fz/FNrw/wCZtf8Ag6H/AIOh/wCD - odYAgJ8QAIOh7ACDof8Ag6H/AIqn/wDS6/8A1O3/ANPs/wDS6v8Az+f/AM3l/wDM5P8Az+f/ANPs/wDU - 7f8A1O3/ANTt/xDZ8P9F3PD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6f - uf8w3PD/Mdzw/zLc8f803fH/Nd3x/zbd8f833fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/O97x/zve - 8f883vH/O97x/zve8f873vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zDc - 8P8v3PD/Ldzw/yvb8P8p2/D/KNvw/yba8P8k2vD/Idrv/x/Z7/8d2e//G9nv/xnY7/8W2O//FNfu/xHX - 7v8P1+7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y - 7/9P6fz/T+n8/0/p/P8t4PX/ALLO/wCDof8Ag6H/AIOh/wCDoIwAg6H/AIOh/wCDof8Arcj/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDQ6P8Azeb/AM7m/wDR6f8A1O3/It30/0/p/P8hr8j/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zHY7f813fH/N93x/zjd8f853vH/Ot7x/zze - 8f883vH/Pd7x/z7e8f8+3vH/P9/x/z/f8f9A3/L/QN/y/0Df8v9A3/L/P9/x/z/f8f8+3vH/Pt7x/z7e - 8f883vH/PN7x/zve8f853vH/ON3x/zfd8f823fH/NN3x/zLc8f8x3PD/L9zw/y3c8P8r2/D/Kdvw/yfb - 8P8l2vD/I9rv/yHa7/8f2e//HNnv/xrY7/8X2O//Fdju/xLX7v8P1+7/Ddbu/wrW7v8H1e3/BNXt/wLU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0rn+/9P6fz/T+n8/z/k+f8Awdv/AIOh/wCD - of8Ag6H/AIOh+QCDof8Ag6H/AIuo/wDP6f8A0+z/ANLr/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDT7P8y4fb/T+n8/0/p/P8yw9r/AIak/wCDof8Ag6H/AIOh/xGZtP8/1On/QNbr/z3S - 6P86zuT/Ncfe/zHC2v8uvtb/KrnR/yazzf8jr8n/H6vF/xqlv/8Xobv/FJy4/xCYs/8Lkq//CI6r/wCD - of8arsf/ON3x/zne8f873vH/PN7x/z7e8f8+3vH/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9D3/L/RN/y/0Tf - 8v9E3/L/RN/y/0Tf8v9E3/L/Q9/y/0Pf8v9D3/L/Qt/y/0Hf8v9A3/L/P9/x/z7e8f883vH/O97x/zne - 8f843fH/N93x/zXd8f8z3fH/Mdzw/y/c8P8t3PD/K9vw/ynb8P8m2vD/JNrw/yHa7/8f2e//HNnv/xrY - 7/8Y2O//Fdju/xPX7v8Q1+7/Ddbu/wrW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/O+T4/0/p/P9P6fz/Ten8/wLL5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AtdD/ANTt/wDT - 7P8A0ur/ANHp/wDP5/8Az+f/AM/n/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/0Hm+v9P6fz/T+n8/0vo - +/8BxuD/AI+s/wCDof8Ag6H/AIOh/wqQrf8/1Or/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9O6fz/Ot/y/zre8f883vH/Pt7x/z/f8f9B3/L/Qt/y/0Pf - 8v9E3/L/ReDy/0bg8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9J4PL/SeDy/0jg8v9I4PL/SODy/0fg - 8v9G4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f863vH/Od7x/zfd8f813fH/Mtzx/zDc - 8P8v3PD/ON3x/0Df8v9F4PL/SODy/0bg8v9C3/H/Od7x/y3b8P8f2fD/Fdju/xPX7v8Q1+7/Ddbu/wrW - 7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p/P9P6fz/B8/o/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AJWy/wDT7P8A1O3/ANTt/wDT7P8A0uv/ANLq/wDR6f8Az+f/AM3m/wDM - 5P8AzOT/AM3m/wDR6f8C1O3/Tuj8/0/p/P9P6fz/POT5/wDU7f8Az+n/AJ26/wCDof8Ag6H/AIOh/wKF - pP8vwNf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vn - +v883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9G4PL/SODy/0ng8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h - 8v9N4fL/TeHy/03h8v9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9G4PL/ReDy/0Pf - 8v9C3/L/QN/y/z7e8f883vH/Ot7x/zrd8f9M4fP/X+T0/23m9f9u5vT/bOb0/2rm9P9o5vT/Z+X0/2Xl - 9P9i5PT/YeT0/1/k9P9T4vP/O97x/yDZ7/8Q1+7/Ddbu/wnW7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P8Mzeb/AIOh/wCDof8Ag6H/AIOh/wCHpf8AyeL/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLq/wvU6/9P6Pv/T+n8/0/p - /P8x4fb/ANTt/wDU7f8A1O3/AbDL/wCEo/8Ag6H/AIOh/wCDof8dqML/TOX4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+T3/0Hf8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg - 8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi8/9R4vP/UuLz/1Li8/9S4vP/UuLz/1Li8/9S4vP/UeLz/1Dh - 8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9J4PL/SODy/0Xg8v9E3/L/Qt/y/0Lf8v9b4/P/cuf1/3To - 9f9z5/X/cef1/3Dn9f9u5vT/bOb0/2rm9P9p5vT/Z+X0/2Xl9P9j5fT/YeT0/1/k9P9d5PT/W+Tz/0Xg - 8v8f2u//DNbu/wnW7v8G1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MOH2/0/p/P9P6fz/T+n8/wvG - 3/8Ag6H/AIOh/wCDof8Ag6H/ALfS/wDT7P8A0ur/AM/n/wDN5v8AzOT/AMvj/wDK4v8AyuL/AMnh/wDJ - 4f8AyeH/AMnh/wDK4v8Ay+P/E9Tp/0/o+/9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8C1O3/EMPc/wOM - qf8Ag6H/AIOh/wCDof8OlLD/Q9nu/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9G4vX/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Dh8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj - 8/9W4/P/VuPz/1bj8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h - 8v9L4fL/SuDy/0jg8v9X4/P/def1/3rp9v946fX/d+j1/3Xo9f9z6PX/aeL0/1/X9P9Y0PT/Vsv0/1TI - 9P9SzfT/VNLz/1jb8/9f5PP/YOT0/17k9P9d5PT/W+Tz/1nj8/873vH/Etfu/wjV7v8F1e3/AtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/BrXQ/wCDof8Ag6H/AIOh/wCpxf8A1O3/ANPs/wDR - 6f8Azub/AM3l/wDL4/8AyuL/AMri/wDK4v8AyuL/AMzk/wDN5f8Azub/ANHp/wDT7P8c2/L/T+n8/0/p - /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wLU7f8Z2O//GtHo/wiZtf8Ag6H/AIOh/wCDof8Eiab/Ncfe/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8/9I4PL/SuDy/03h8v9O4fP/UOHz/1Li - 8/9T4vP/VOLz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj - 8/9Z4/P/WePz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UeLz/0/h8/9Q4fL/bOf0/4Dp9v9/6fb/fOn2/3vp - 9v9x5PX/Xsv0/1Gz8v9SovP/UqLz/1Kh8/9SofP/UqHz/1Kh8/9SofP/UqHz/06n8v9MvfP/UdTz/1zj - 9P9c5PT/WuPz/1jj8/9O4fP/Htnv/wfV7f8E1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k+f9P6fz/T+n8/0zo - +/8Anbj/AIOh/wCDof8Ag6HpANTt/wDU7f8A0+z/ANPs/wDT7P8A1O3/ANTt/wDU7f8AutT/AIOh/wCq - xf8AtM//AL/a/wDK5P8A0+z/ANTt/xzb8v9P6fz/T+n8/0/p/P8d3PL/ANTt/wDU7f8A1O3/A9Xt/xvZ - 7/8f2e//Idnu/xGsx/8Ag6H/AIOh/wCDof8Ag6H/I7DK/03n+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9N5/v/SuDy/03h8v9P4fP/UOHz/1Li8/9U4vP/VuPz/1fj8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k - 9P9f5PT/X+T0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj - 8/9V4vP/WOPz/3np9v+B6vb/gOr2/3/q9v925fX/Wsby/06j8f9Pn/H/T5/w/0+f8P9Pn/D/T5/w/0+f - 8P9PnvD/T57w/0+e8P9PnvD/Tp7w/06e8P9OnvD/Rq/w/0rU8v9Y4/T/V+Lz/1Ti8/9R4vP/Jtvw/wbV - 7f8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Suf7/0/p/P9P6fz/P+T5/wCHpP8Ag6H/AIOh/wCCoa4A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ALPN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFo/8Am7f/G9Tr/0/p - /P9P6fz/T+n8/yHd8/8A1O3/ANTt/wDU7f8C1O3/Htnv/yHa7/8k2vD/J9vw/x7C2P8Diab/AIOh/wCD - of8Ag6H/Epu2/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/07l9/9O4fP/UOHz/1Li8/9U4vP/VuPz/1nj - 8/9a4/P/W+Pz/17k9P9e5PT/YOT0/2Hk9P9i5fT/YuX0/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/ZOX0/2Pl - 9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9c5PP/W+Pz/13k8/986fb/gur2/4Hq9v9/6vb/atj0/02l - 7v9MnO7/TJzu/0yc7v9MnO7/Tp3t/2il4/97rd3/hrDZ/4uy1v9/rdr/c6ne/1ig6f9Lm+3/S5vt/0ub - 7f9Lm+3/S5vt/0O57v9P4PL/UeLz/0/i8/9N4fP/Jdrw/wXV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wzY8P9P6fz/T+n8/0/p - /P8y2O7/AIOh/wCDof8Ag6H/AIKhZADS6v8A0ur/ANHp/wDR6f8A0en/ANHp/wC40v8Ag6L/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Xssz/T+n8/0/p/P9P6fz/It70/wDU7f8A1O3/ANTt/wDU - 7f8h2u//JNrw/yfb8P8q2/D/Ldzw/yrR5v8KlbH/AIOh/wCDof8Ag6H/B4up/zvP5f9P6fz/T+n8/0/p - /P9P6fz/T+T2/1Li8/9U4vP/VuPz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9k5fT/ZeX0/2bl - 9P9n5fT/Z+X0/2jm9P9o5vT/aeb0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuX0/2Dk - 9P9f5PT/fen2/4Pq9v+B6vb/f+r2/2PK8v9Kmuz/SZns/0mZ7P9Jmez/YaLk/5W10f+2wsf/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vMTE/6i8zP9+rNn/TJrq/0iY6/9HmOr/R5jq/0Op7P9J2vH/SuHy/0ng - 8v9G4PL/Gtju/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/H9zz/0/p/P9P6fz/T+n8/yK+1v8Ag6H/AIOh/wCDofwAhaMZAMzk/wDM - 5P8AzeX/AM3m/wDQ6P8Avtj/AIWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xWl - wP9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANTt/yDZ7/8m2vD/Ktvw/y3c8P8w3PD/M93x/zTa - 7v8XqMH/AIOh/wCDof8Ag6H/AYSi/ym4z/9P6fz/T+n8/0/p/P9T4vT/VeLz/1jj8/9a4/P/XOTz/17k - 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9s5vX/beb1/23m9f9t5vX/beb1/2zm - 9f9s5vX/bOb1/2vm9f9q5vT/aOb0/2fl9P9l5fT/ZOX0/3bo9f+E6vb/gur2/4Dq9v9iwvD/Rpbp/0aW - 6f9Glun/UJnm/5a00P+8xMT/vcTE/77Fxf+/xcX/v8bG/8DHx//Bx8f/wcjI/8LIyP/Dycn/w8rK/8TK - yv+4xs3/cafd/0SV6P9Elej/RJXo/0Sf6v9F2PH/ReDy/0Lf8v893/L/C9bt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p - /P9P6fz/Dpez/wCDof8Ag6H/AIShuAAAAAAA1O3/ANTt/wDU7f8A1O3/AL7Y/wCIpf8Ag6H/AIOh/wCD - of8Ag6KkAImdDQCFoS4AhKFRAIOh/wCDof8Ag6H/Ep65/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU - 7f8A1O3/Htnv/ynb8P8s2/D/MNzw/zLc8f823fH/Od7x/zze8f8ovdP/A4ek/wCDof8Ag6H/AIOh/xeh - vP9J4vX/UOj7/1bj8/9Z4/P/W+Pz/17k9P9g5PT/YuX0/2Xl9P9m5fT/aOb0/2rm9P9s5vX/beb1/27n - 9f9v5/X/cOf1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/b+f1/27n9f9s5vX/a+b1/2rm - 9P9v5/X/her2/4Pq9v+B6vb/acvt/0OU5/9Dk+b/Q5Pm/1uc4P+xwcr/wcjI/8LIyP/Cycn/w8rK/8PK - yv/CyMj/wsjI/8HIyP/Dycn/xcvL/8jOzv/Jzs7/yc/P/8rQ0P/K0ND/krbW/0KS5P9BkuX/QZLl/0Gh - 6P9A3PH/Pt7x/zve8f8n2/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AtXt/0rn+/9P6fz/T+n8/0nl+P8BhKL/AIOh/wCDof8AhKBbAAAAAADU - 7f8A1O3/ANTt/wC0z/8AhKL/AIOh/wCDof8Ag6H/AIOhvQCAqgYAAAAAAAAAAAAAAAAAgqGNAIOh/wCD - of8Kj6z/T+n8/0/p/P8+1On/Dp25/wCTsP8AxN//ANTt/wDU7f8a2O//K9vw/y7c8P8y3PH/Nd3x/zjd - 8f883vH/Pt7x/0Lf8v85z+P/CpGt/wCDof8Ag6H/AIOh/wqQrf9B0ub/WePz/1zk8/9f5PT/YeT0/2Tl - 9P9m5fT/aOb0/2rm9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f926PX/duj1/3bo - 9f926PX/dej1/3To9f9z5/X/cuf1/3Hn9f9v5/X/buf1/4Dp9v+F6/b/gur2/3XZ8/8xbar/QJHk/0CR - 5P9Vm+H/u8fO/8bMzP/Hzc3/x83N/8XLy//Ax8f/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/v8XF/8XL - y//O09P/z9TU/9DV1f/Q1dX/nb3Z/z6P4v8+j+L/Po/i/zyw6P863vH/ON3x/zLd8P8M1u7/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p - /P9P6fz/M8je/wCDof8Ag6H/AIOh6wCOqgkAAAAAANTt/wDT7P8AqMT/AIOh/wCDof8Ag6H/AIOh/wCE - obwAgJkKAAAAAAAAAAAAAAAAAAAAAACDoX0Ag6H/AIOh/wGEov9K5Pj/I6/J/wGEov8Ag6H/AIOh/wCi - vv8A1O3/ANTt/xXY7v8t3PD/Mdzw/zTd8f833fH/O97x/z7e8f9B3/L/ReDy/0jg8v9G2+7/GaG8/wCD - of8Ag6H/AIOh/wKFpP9N1Ob/YOT0/2Ll9P9l5fT/Z+X0/2rm9P9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo - 9f936PX/eOj1/3no9v966fb/e+n2/3vp9v976fb/e+n2/3rp9v966fb/eOj1/3jo9f926PX/dej1/3Pn - 9f936PX/huv3/4Tq9/984vj/SKnr/yVViP86iNj/SZTf/7nI0f/K0ND/y9HR/8vR0f/Dysr/vcTE/73E - xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/7/Gxv/P1NT/1dra/9ba2v/W29v/irTd/zqM - 4P86jOD/Oo3g/zXQ7v8z3PH/K9zw/x7Z7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y3g9f9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8AgqKDAAAAAAAA - AAAAx+H/AJe0/wCDof8Ag6H/AIOh/wCDof8AhKGdAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCD - of8Ag6H/AIOh/wmQrP8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/Dtbu/zDc8P8z3fH/N93x/zne - 8f893vH/Qd/y/0Pf8v9H4PL/SuDy/03h8v9R4vP/L7jO/wGFov8Ag6H/AIOh/yyvx/9i5fT/ZeX0/2jm - 9P9r5vX/beb1/3Dn9f9y5/X/dOj1/3bo9f946PX/eun2/3vp9v996fb/fen2/37p9v9/6fb/f+n2/3/p - 9v9/6fb/f+n2/37p9v996fb/fOn2/3vp9v946PX/d+j1/4Hq9v+G6vb/hOr2/2HF+P89rfn/JFiM/y9y - uP+du9b/z9TU/9DV1f/Q1dX/w8rK/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+5ta7/tKGO/7KZ - g/+0o5H/urm0/77Fxf/U2dn/29/f/9zg4P/a3+D/Vpnc/zeJ3f83id3/NaLi/y/c8P8n2+//I9rv/wXV - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/SOf6/0/p - /P9P6fz/Tef6/wWJp/8Ag6H/AIOh+QCFoxkAAAAAAAAAAACEov8Ag6H/AIOh/wCDof8Ag6H+AIOgeQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/DZOw/zvP - 5v8W2vH/ANTt/wDU7f8E1e3/Mtzx/zXd8f853vH/PN7x/z/f8f9D3/L/RuDy/0ng8v9N4fL/UOHz/1Pi - 8/9W4/P/QMjb/wCDof8MkKz/TtHj/2bl9P9p5vT/bOb1/2/n9f9x5/X/c+f1/3bo9f946PX/eun2/3zp - 9v9+6fb/f+n2/4Hq9v+C6vb/g+r2/4Tq9v+E6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v+A6vb/fun2/33p - 9v986fb/h+v2/4br9v+D6vb/R7L5/z6v/P8rdbH/PGiT/9PY2f/U2dn/1dra/8nPz/+9xMT/vcTE/73E - xP+9xMT/vcTE/73ExP+6ubP/q4Bd/6RlNP+kZTT/pGU0/6RlNP+kZjb/ropr/77Bvv/d4OD/4eTk/+Ll - 5f+8z+H/NIbb/zSG2/8zhtr/KdPu/yHa7/8d2e//Dtbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXa8f9P6fz/T+n8/0/p/P83yuD/AIOh/wCDof8Ag6KhAAAAAAAA - AAAAAAAAAIOh/wCDof8Ag6H/AIOg6ACDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF - oHMAg6H/AIOh/wCDof8Ag6H/AoWj/ye1zv9N5/n/T+n8/0Tm+f8C1O3/ANTt/wDU7f8t3PD/N93x/zre - 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9S4vP/VuPz/1nj8/9c5PP/YOT0/2Ll9P9l5fT/aeb0/2zm - 9f9v5/X/cef1/3To9f936PX/eej2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+H6/f/iOv3/4jr - 9/+I6/f/iOv3/4fr9/+H6/f/her2/4Tq9v+C6vb/gOr2/4Lq9v+I6/f/huv3/3jd9/8/r/z/Pq/8/zea - 4v9jc4H/w8bG/9re3v/W2tr/vsTE/73ExP+9xMT/vsXF/8DGxv/ByMj/u7Sp/6dsP/+kZTT/pGU0/6Rl - NP+kZTT/pGU0/6RlNP+kZTT/p3JH/8bBuv/m6en/5+np/+fq6v9Sldn/MIPY/zCD2P8ptuX/Gtnv/xfY - 7/8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p - /P9P6fz/T+n8/xulwP8Ag6H/AIOh/wCCnzUAAAAAAAAAAAAAAAAAg6H/AIOh/wCDoasAgKYUAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEnh0AgqK0AIOh/wCDof8Ag6H/AIOh/xGZtf9B1+z/T+n8/0/p - /P9P6fz/T+n8/ybe9f8A1O3/ANTt/yDZ7/853vH/PN7x/0Df8v9D3/L/R+Dy/0rg8v9O4fP/UeLz/1Ti - 8/9Y4/P/W+Pz/17k9P9i5fT/ZeX0/2jm9P9s5vX/b+f1/3Hn9f916PX/eOj1/3vp9v996fb/f+n2/4Lq - 9v+E6vb/huv2/4jr9/+J6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+M7Pf/i+v3/4vr9/+J6/f/iOv3/4br - 9v+E6vb/h+v2/4jr9/+G6/f/XcT2/z6v/P8+r/z/Pq/8/3OLnf+go6P/3+Li/8rQ0P+9xMT/vsXF/8HH - x//Dycn/xcvL/8XIxf+obkL/pGU0/6RlNP+jZTT/fU0o/2Y/IP+FUir/pGU0/6RlNP+kZTT/qHhQ/+Tn - 5v/s7u7/7e/v/52+3v8tgNX/LYDV/yqX3P8T1+7/Etfu/xLX7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7v9L6Pv/T+n8/0/p/P9L5fj/A4el/wCDof8Ag6H/AAAAAAAA - AAAAAAAAAAAAAACDotIAg59IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6JrAIOh8gCD - of8Ag6H/AIOh/wSIpf8tvdT/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wrW7/8A1O3/Edfu/zre - 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Tl9P9n5fT/a+b1/27n - 9f9x5/X/dOj1/3jo9f976fb/fen2/4Dq9v+D6vb/her2/4jr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Hs - 9/+R7Pf/kez3/5Hs9/+Q7Pf/j+z3/47s9/+L6/f/iev3/4fr9/+J6/b/iOv2/4Xq9v9MtPT/Pq/8/z6v - /P8+r/z/WaHS/4mKiv/Y29v/wMbG/7/Gxv/Dycn/xszM/8nOzv/L0ND/vaaR/6RlNP+kZTT/nmEy/ykZ - Df8AAAD/AAAA/wAAAP8+JhT/o2U0/6RlNP+kZTT/zL+y//Lz8//y9PT/0t/q/yp90/8qfdP/KoDT/wzW - 7v8M1u7/DNbu/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gtvy/0/p - /P9P6fz/T+n8/zLF3P8Ag6H/AIOh/wCDoaMAAAAAAAAAAAAAAAAAAAAAAP//AQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAg6IpAIOhxQCDof8Ag6H/AIOh/wCDof8WoLv/Rdzw/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/NuP3/wDU7f8D1e3/Ot7x/z/f8f9D3/L/R+Dy/0rg8v9O4fP/UuLz/1Xi - 8/9Z4/P/XOTz/2Dk9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3Pn9f936PX/e+n2/33p9v+A6vb/g+r2/4fr - 9/+J6/f/i+v3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/le34/5bt+P+W7fj/le34/5Xt+P+T7ff/kez3/5Ds - 9/+N7Pf/i+v3/4vr9/+I6/b/hev2/0Wv9v8+r/z/Pq/8/z6v/P9Brvn/g5CZ/6qtrf/Axsb/xMrK/8fN - zf/L0ND/ztPT/9DV1f+xhGD/pGU0/6RlNP9SMxr/AAAA/wAAAP8AAAD/AAAA/wAAAP9qQSL/pGU0/6Rl - NP+5lnn/9/j4//j5+f/r8PT/J3rQ/yd60P8netD/Cs/s/wfV7f8H1e3/AtTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/FqC7/wCDof8Ag6H/AIOgcQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAISifgCDofkAg6H/AIOh/wCD - of8Gi6j/MsTb/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/F9vx/wDU - 7f8r2/D/Qd/y/0Tf8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n - 9f9z5/X/duj1/3no9v996fb/gOr2/4Pq9v+H6/f/iev3/4zs9/+P7Pf/ku33/5Tt9/+W7fj/mO74/5nu - +P+a7vj/mu74/5ru+P+a7vj/mO74/5ft+P+V7fj/k+33/5Ds9/+O7Pf/i+v3/4jr9/+F6vb/P6z3/z6v - /P8+r/z/P6/8/0Wy/P9lptL/aGlp/4KFhf/Izs7/zNHR/8/U1P/T19f/1tra/6pzSP+kZTT/pGU0/y0c - Dv8AAAD/AgIC/woKCv8RERH/FRUV/1E4Jf+kZjX/pGU0/7CCXv/8/Pz//f39//z9/f8jd83/I3fN/yN3 - zf8Hxej/AdTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Bdbu/03p - /P9P6fz/T+n8/0jl+P8BhaL/AIOh/wCDof8AhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAISfOACDodMAg6H/AIOh/wCDof8Ag6H/HKfB/0jh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vr/AtTu/xTX7v9C3/L/ReDy/0ng8v9N4fL/UeLz/1Ti - 8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr - 9/+M7Pf/j+z3/5Lt9/+V7fj/mO74/5ru+P+c7vj/ne74/5/v+P+f7/j/n+/4/57v+P+c7vj/m+74/5nu - +P+W7fj/k+33/5Ds9/+O7Pf/iuv3/4fr9/9Arff/Pq/8/z6v/P9Fsfz/SrT8/1C2+/8oLjL/HB0d/8rQ - 0P/Q1dX/1NjY/9fb2//b3t7/rnxU/6RlNP+kZTT/OyQT/xsbG/9GRkb/TExM/1NTU/9ZWVn/lIBx/7mJ - ZP+kZTT/toxr////////////+fv8/yB0y/8gdMv/IHTL/wTJ6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8g3PP/T+n8/0/p/P9P6fz/Lsvi/wCDof8Ag6H/AIOh/ACJ - nQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqDACCopEAg6H9AIOh/wCDof8Ag6H/CY+s/zjM - 4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8n3vX/AdTt/z7e8f9H4PL/SuDy/07h8/9S4vP/VeLz/1nj8/9d5PT/YOT0/2Tl9P9n5fT/a+b1/2/n - 9f9z5/X/duj1/3rp9v996fb/ger2/4Tq9v+I6/f/i+v3/47s9/+S7ff/le34/5ju+P+b7vj/nu/4/6Dv - +P+h7/j/o+/5/6Tw+f+k8Pn/ou/4/6Hv+P+f7/j/nO74/5nu+P+W7fj/k+33/5Ds9/+M7Pf/iev3/0Ov - 9v8+r/z/Q7H8/0mz/P9Ptvz/QpDE/wgOEf8HBwf/e39//9PY2P/Y3Nz/3N/f/9/j4//BoIX/pGU0/6Rl - NP+KVSz/aGZl/4iIiP+Ojo7/lZWV/6Kfnf/Yw7P/28Ov/6RlNP/Ks6D////////////s8fX/HXHI/x1x - yP8dcsj/AdLs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k - +f9P6fz/T+n8/0/p/P8Qn7r/AIOh/wCDof8Ag6HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - olUAg6H/AIOh/wCDof8Ag6H/AIOh/w2TsP82yeD/Os7k/zrO5P86zuT/Oczi/zXH3v81x97/Ncfe/zXH - 3v81x97/NMbd/y/A1/8vwNf/L8DX/y/A1/8vwNf/L7/X/ym50f8FsMr/Gb3W/0jg8v9L4fL/T+Hz/1Pi - 8/9W4/P/WuPz/17k9P9i5fT/ZeX0/2nm9P9s5vX/cOf1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr - 9/+N7Pf/kOz3/5Tt9/+Y7vj/m+74/57v+P+h7/j/pPD5/6bw+f+n8Pn/qPD5/6jw+f+m8Pn/pPD5/6Hv - +P+f7/j/m+74/5ju+P+V7fj/kez3/47s9/+K6/f/SrT0/0Cv/P9Hs/z/TrX8/1O09v8RIy//OXCV/wIC - A/8WFxf/0dbW/9vf3//g4+P/5Ofn/9vTyv+lZjb/pGU0/6RlNP/AppL/19LO/9jV0v/h3dr/8Onj//Xu - 6f/m1Mb/pms9/+ro5P///////////8jY5/8absb/Gm7G/xd7yv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1/D/T+n8/0/p/P9P6fz/QuP3/wCDof8Ag6H/AIOh/wCD - onMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JlAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0zh8v9P4fP/VOLz/1fj8/9b4/P/XuT0/2Ll9P9m5fT/aub0/23m - 9f9x5/X/dej1/3jo9f986fb/gOr2/4Tq9v+H6/f/i+v3/47s9/+S7ff/lu34/5ru+P+d7vj/oe/4/6Tw - +f+n8Pn/qvH5/6zx+f+t8fn/rPH5/6rx+f+n8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+T7ff/j+z3/4vr - 9/9gyfj/RLH8/0u0/P9St/z/KFVz/zlxlv9aqd7/ChAU/xYXF/9eYGD/3uHh/+Ll5f/n6ur/7O7u/8io - jv+kZTT/pGU0/610SP/w5d3/////////////////9vDr/7SBWf/Drpv//v7+////////////daTT/xZr - w/8Wa8P/DpfU/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf - 9P9P6fz/T+n8/0/p/P8lyOD/AIOh/wCDof8Ag6H/AIafKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - oasAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8YsMn/TeHy/1Dh - 8/9U4vP/WOPz/1vj8/9f5PT/Y+X0/2fl9P9q5vT/buf1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr - 9/+L6/f/kOz3/5Pt9/+X7fj/mu74/5/v+P+i7/j/pvD5/6nw+f+t8fn/sPL5/7Hy+f+w8vn/rPH5/6nw - +f+l8Pn/oe/4/57v+P+a7vj/l+34/5Pt9/+P7Pf/i+v3/3ne+P9Hsvz/T7b8/0yj3f9UquP/ZL/8/z5x - kv8nQ1b/KjpF/xITE/+Tlpb/5Ofn/+rs7P/v8PD/8/X1/9O3of+maTr/pGU0/6VnN/+/knD/zKiM/8GX - df+rckf/x7Wl//Hy8v////////////f5+v8bbMH/E2jA/xNowP8Ft+H/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/Ref6/0/p/P9P6fz/Tun8/wmcuf8Ag6H/AIOh/wCD - odwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISjOgCDof8Ag6H/AIOh/wCDoe4Ag6HhAISi3QCE - ot0AhKLdAISi3QCEot0Ag6HRAIOhzACDocwAg6HMAIOh7wCDof8Ag6H/AISi/w+cuP8Vnrn/FZ65/xWe - uf8Gnbn/AJ66/wCeuv8Anrr/AKnF/wzS6/9M4fL/UeLz/1Ti8/9Y4/P/W+Pz/2Dk9P9j5fT/Z+X0/2vm - 9f9v5/X/cuf1/3bo9f966fb/fen2/4Hq9v+E6vb/iev3/4zs9/+Q7Pf/lO33/5ju+P+b7vj/n+/4/6Pv - +f+m8Pn/qvH5/67x+f+y8vr/tPL6/7Dy+f+t8fn/qfD5/6bw+f+h7/j/nu/4/5ru+P+X7fj/k+33/4/s - 9/+L6/f/h+v3/0u0+P9Rt/z/Wbr8/2G9/P9nv/r/Ficz/12cx/8vTF//DQ8R/wsMDP++v7//6+3t//Dx - 8f/19vb/+/v7//Hp4//KqI3/t4di/7F9VP+2iGT/xKaO/9nW0v/o6ur/////////////////l7rb/xBl - vv8QZb7/EG/C/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xTZ - 8f9P6fz/T+n8/0/p/P863/T/AIOh/wCDof8Ag6H/AIKhhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhKJNAIOh/wCDof8Ag6H/GMXe/0/p/P9P6fz/T+n8/zfj9/8A1O3/ANTt/wDU7f8A1O3/ANTt/zHc - 8P9R4vP/VOLz/1jj8/9c5PP/YOT0/2Pl9P9n5fT/a+b1/2/n9f9y5/X/duj1/3rp9v996fb/ger2/4Tq - 9v+J6/f/jOz3/5Ds9/+T7ff/mO74/5vu+P+f7/j/ou/4/6bw+f+p8Pn/rPH5/6/y+f+w8vn/rvH5/6vx - +f+o8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+S7ff/juz3/4vr9/+H6/f/Zs34/1S4/P9cu/z/ZL/8/0By - lP81W3X/esj9/ypCUv8THCL/AAAA/xscHP/W2Nj/7/Dw//T19f/4+Pj/+fn5//b39//x8/P/7O7u/+fq - 6v/i5eX/6evr/////////////////+zx9f8SZrz/DWO7/wxiu/8Motj/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p/P9P6fz/T+n8/x291v8Ag6H/AIOh/wCD - of0AgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAvwQAg6HnAIOh/wCDof8BlrP/Q+X5/0/p - /P9P6fz/Tun8/wzY8P8A1O3/ANTt/wDU7f8A1O3/Ctbu/0/h8/9U4vP/WOPz/1vj8/9g5PT/Y+X0/2fl - 9P9q5vT/b+f1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr9/+L6/f/j+z3/5Pt9/+W7fj/mu74/53u - +P+h7/j/pPD5/6bw+f+p8Pn/q/H5/6vx+f+q8fn/qPD5/6Xw+f+i7/j/n+/4/5zu+P+Y7vj/le34/5Hs - 9/+O7Pf/iev3/4br9v9/5vf/Vrj5/168/P9XotT/PWqJ/3fG/f9+yv3/EBge/xsoMP8FBgj/BAQE/yco - KP/b3d3/8PHx//Lz8//z9PT/8fPz/+7w8P/q7Oz/5+rq//L09P/////////////////2+Pr/MXnC/wlf - uP8JX7j/DHDA/wbS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV - 7v9P6fz/T+n8/0/p/P9L6Pv/A4uo/wCDof8Ag6H/AIKhtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAGZmCgByjeEAg6H/AIOh/wCDof8f0Oj/T+n8/0/p/P9P6fz/NOL3/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/Kdvw/1Ti8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9u5/X/cef1/3Xo9f946PX/fOn2/4Dq - 9v+E6vb/h+v3/4vr9/+O7Pf/kez3/5Xt+P+Y7vj/m+74/5/v+P+h7/j/pPD5/6Xw+f+m8Pn/pvD5/6bw - +f+k8Pn/ou/4/5/v+P+c7vj/me74/5bt+P+T7ff/j+z3/4vr9/+I6/f/hOr2/4Hq9v9t1fj/X738/2Gy - 6v9wxP3/ecf9/2ijy/8YJCz/IC44/xgiKf8oNj//AAAA/yoqKv/P0dH/7vDw/+7w8P/v8fH/8PLy//b2 - 9v/+/v7/////////////////9ff4/0uJxv8GXbb/Bl22/wZet/8VueH/AdTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Kt/0/0/p/P9P6fz/T+n8/zHT6f8Ag6H/AIOh/wCD - of8AhKFRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVgAYAWm+1AF91/wCDof8Ag6H/AIOh/wOa - t/9J6Pv/T+n8/0/p/P9P6fz/Etnw/wDU7f8A1O3/ANTt/wDU7f8D1e3/SODy/1fj8/9b4/P/XuT0/2Ll - 9P9l5fT/aub0/23m9f9x5/X/dOj1/3jo9f976fb/f+n2/4Lq9v+G6/b/iev3/4zs9/+Q7Pf/k+33/5bt - +P+Z7vj/m+74/57v+P+f7/j/oe/4/6Lv+P+i7/j/oe/4/6Dv+P+e7/j/nO74/5nu+P+W7fj/k+33/5Ds - 9/+N7Pf/iev3/4fr9/+D6vb/f+n2/3zp9v9mx/n/aMH8/3HE/f96yP3/Mk9i/2+myv8SGiD/L0FM/1V0 - iP8AAAD/AgMD/w8PD/+BgYH/+fn5/////////////////////////////////+Ho7/8ncLz/A1q0/wNa - s/8DWrP/EqXX/xDX7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW - 7/9M6Pv/T+n8/0/p/P9O6Pz/DZm1/wCDof8Ag6H/AIOh5ACAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVVUDAFtwqQBab/8AWm//AHWQ/wCDof8Ag6H/AIOh/yXT6v9P6fz/T+n8/0/p/P8+5fn/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Qv9j/O8Xa/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n9f9z5/X/duj1/3rp - 9v996fb/gOr2/4Tq9v+H6/f/iuv3/47s9/+Q7Pf/k+33/5Xt+P+Y7vj/mu74/5zu+P+c7vj/ne74/53u - +P+c7vj/nO74/5ru+P+Y7vj/lu34/5Pt9/+Q7Pf/juz3/4vr9/+H6/f/hOr2/4Hq9v996fb/eun2/3Xl - 9v9nxfn/ccT9/2CeyP9ek7j/hMby/wEBAf9kiqT/f6/O/wAAAP8lNkL/AAAA/wQEBP8LCwv/SkpK/5CQ - kP+6urr/3t7e/9LV1v9rjrH/A1iw/wBXsf8AV7H/AFmy/xGd0v8c2e//CNXu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ld/1/0/p/P9P6fz/T+n8/zLT6f8Ag6H/AIOh/wCD - of8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBZbp0AWm//AFpv/wBab/8XiqH/AYSi/wCD - of8Ag6H/Ape0/0bn+v9P6fz/T+n8/0/p/P8b2/L/ANTt/wDU7f8A1O3/AK/K/wCDof8Ag6H/N7/U/1zk - 8/9g5PT/Y+X0/2fl9P9q5vT/buf1/3Hn9f916PX/eOj1/3vp9v9/6fb/gur2/4Xq9v+I6/f/i+v3/47s - 9/+Q7Pf/k+33/5Tt9/+W7fj/mO74/5ju+P+Z7vj/me74/5ju+P+Y7vj/lu34/5Tt9/+S7ff/kOz3/43s - 9/+L6/f/h+v3/4Tq9v+C6vb/fun2/3vp9v946PX/dOj1/3Dk9v9rw/H/ZafU/4DK/f9spMn/FiEo/5HO - 9/+Rzfb/AAAA/1N9mf8AAAD/BQcI/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ADhz/ADl0/wNg - tv8Ytd7/H9nv/xrY7/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y - 8P9O6Pz/T+n8/0/p/P9O6Pz/C5az/wCDof8Ag6H/AIOg6ACLogsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnCQAFpv/wBab/8AWm//GIWZ/03m+f8cp8H/AIOh/wCDof8Ag6H/G8rj/0/p/P9P6fz/T+n8/0fm - +v8G1e7/ANTt/wDE3v8AhaP/AIOh/wCDof8mtc3/W+Pz/1/k9P9i5fT/ZeX0/2nm9P9s5vX/b+f1/3Pn - 9f926PX/eej2/3zp9v9/6fb/gur2/4Xq9v+I6/f/iuv3/4zs9/+O7Pf/kOz3/5Lt9/+T7ff/lO33/5Tt - 9/+U7ff/lO33/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iev3/4fr9/+E6vb/gur2/3/p9v986fb/eOj1/3bo - 9f9y5/X/b+f1/2zk9f9w0vr/fcn9/0Zth/9wp83/jtD9/47Q/f8DBAX/aJ7D/xIdJP8AAAD/BQkL/wUK - Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Cj1F/x7I2/8d2e//D9fu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/QOX5/0/p/P9P6fz/T+n8/y7N5P8Ag6H/AIOh/wCD - of8AhKJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/xR/k/9M5Pb/T+n8/0LY - 7f8BhKL/AIOh/wCDof8Ai6n/PuT4/0/p/P9P6fz/T+n8/zTi9/8A0uv/AJCt/wCDof8Ag6H/CpCt/zze - 9P9U4/T/XeT0/2Dk9P9k5fT/Z+X0/2rm9P9u5/X/cef1/3Pn9f936PX/eun2/33p9v9/6fb/gur2/4Tq - 9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/kOz3/5Ds9/+P7Pf/juz3/47s9/+M7Pf/iuv3/4jr - 9/+G6/b/hOr2/4Hq9v9/6fb/fOn2/3no9v926PX/c+f1/3Dn9f9s5vX/aub0/2bl9P9p2/b/YqLI/4PM - /f+Hzf3/fb/r/wAAAP9ys9//LEhb/wAAAP8lQ1b/CREX/y1bev8AAQH/AAAA/wQLEP8AAQH/AAAA/wAA - AP8AAAD/BB4h/xi6zP8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yXe - 9P9P6fz/T+n8/0/p/P9H5fn/BYqn/wCDof8Ag6H/AIOh5ACOqgkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb - b3MAWm//AFpv/wBab/8Pdor/OcHV/yqluf8XhZj/BWN3/wBzjf8Ag6H/AIOh/wCDof8Qssz/Tuj8/0/p - /P9P6fz/T+n8/wypxP8Ag6H/AIOh/wGEov880Ob/T+n8/0rn+v9X5fX/XuT0/2Ll9P9l5fT/aOb0/2vm - 9f9v5/X/cef1/3To9f936PX/eun2/33p9v9/6fb/ger2/4Pq9v+F6vb/h+v3/4nr9/+J6/f/iuv3/4vr - 9/+L6/f/i+v3/4vr9/+K6/f/iev3/4jr9/+G6/b/hOr2/4Lq9v+A6vb/fen2/3vp9v946PX/duj1/3Pn - 9f9w5/X/beb1/2rm9P9n5fT/Y+X0/2Dk9P9d4/T/aNn3/3jO+/9WiKv/ERwj/3rI/f84XXf/BAgK/1CN - t/8VFRX/OXSb/ytojv8LCwv/ChMV/yCXpf8OTFP/AAEB/wAAAP8AAAD/Ahga/wC90/8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8S2fD/Ten8/0/p/P9P6fz/T+n8/xqwyf8Ag6H/AIOh/wCD - of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29lAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AF1z/wB/nP8Ag6H/AIOh/wCDof8o1ev/T+n8/0/p/P86zeT/AIOh/wCDof8Ag6H/JbPM/0/p - /P9P6fz/T+n8/0/o/P9X5vj/YOT0/2Ll9P9m5fT/aeb0/2zm9f9v5/X/cef1/3To9f926PX/eej2/3vp - 9v996fb/f+n2/4Lq9v+D6vb/hOr2/4Xq9v+G6/b/h+v3/4fr9/+H6/f/h+v3/4br9v+E6vb/hOr2/4Lq - 9v+A6vb/fun2/3zp9v966fb/eOj1/3Xo9f9z5/X/cOf1/23m9f9q5vT/Z+X0/2Tl9P9h5PT/XuT0/1vj - 8/9X4/P/VOLz/xg+RP84hZf/XdT3/ypjdP8TMDj/S8/u/zI3OP8yYWf/Nd3x/y19hv8kJCT/Ij1B/yjQ - 5P8hr8D/Fzo+/xEREf8ODg7/CC0y/wDM5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0bn - +v9P6fz/T+n8/0/p/P832O3/AIOh/wCDof8Ag6H/AIShvgD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvVQBa - b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AGqC2ACDof8Ag6H/AIOh/wKR - rv8/5Pn/SeL1/wiOq/8Ag6H/AIOh/w+Wsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9U5/n/X+T1/2Pl - 9P9m5fT/aeb0/2zm9f9v5/X/cef1/3Pn9f926PX/eOj1/3rp9v986fb/fen2/3/p9v+A6vb/ger2/4Lq - 9v+C6vb/gur2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/fen2/3zp9v976fb/eOj1/3bo9f906PX/cef1/2/n - 9f9s5vX/aub0/2fl9P9k5fT/YeT0/17k9P9b4/P/WOPz/1Xi8/8+rLn/IV5l/0vh8v9I4PL/GVNZ/yV/ - iv8+3vH/SnJ3/0ldX/803fH/Mdvv/z1hZf89PT3/MXuE/yLa7/8f1On/IXiD/ysrK/8nJyf/FWhy/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f844/j/T+n8/0/p/P9P6fz/RuX5/waNq/8Ag6H/AIOh/wCD - ofcAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/bAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv9QBa - b70AWm5/AFlwQgBmZgoAAAAAAIOgrwCDof8Ag6H/AIOh/wyqxf8apcD/AIOh/wCDof8ChqT/Qtjt/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9S6Pr/XeX1/2Pl9P9m5fT/aeb0/2vm9f9u5/X/cOf1/3Ln - 9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3vp - 9v956Pb/eOj1/3bo9f906PX/cuf1/3Dn9f9u5/X/bOb1/2nm9P9n5fT/ZOX0/2Hk9P9e5PT/W+Pz/1jj - 8/9V4vP/UuLz/zmir/9M4fL/SODy/0Xg8v8QNzz/O9Dh/zze8f9VnKX/ZW9w/zLc8f8u3PD/Nr/P/1pa - Wv9WWFn/KMTX/xvZ7/8D1e3/HJ6t/0NHSP9AQUH/EKy//wDU7f8A1O3/ANTt/wDU7f8A1O3/LuD2/0/p - /P9P6fz/T+n8/07o/P8TpL//AIOh/wCDof8Ag6H/AISgfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b7wAWm//AFpv/wBab/8AWm+1AFlveABYbjoAZmYFAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh7ACD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y291P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9Q6fz/Wub3/2Pl9P9l5fT/aOb0/2rm9P9s5vX/b+f1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo - 9f946PX/eej2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3Xo9f9z5/X/cuf1/3Hn9f9v5/X/bOb1/2rm - 9P9o5vT/ZeX0/2Pl9P9g5PT/XuT0/1vj8/9Y4/P/VeLz/1Li8/9P4fP/TeHy/0ng8v9G4PL/O8TV/yN6 - hf883vH/Od7x/1a8yP96kJL/MNzw/yzb8P8p2/D/ZIyR/3Nzc/9Mnab/CNXu/wDU7f8A1O3/Iqq6/11e - Xv9LbXH/AdPs/wDU7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/JMDZ/wCDof8Ag6H/AIOh/wCD - ocgAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBbcFQAXHAyAFVVAwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh/ACDof8Ag6H/AIOh/wCDof8Vnrn/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Vuf4/2Hl9P9l5fT/Z+X0/2nm - 9P9r5vX/bOb1/27n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/dej1/3To9f906PX/c+f1/3Pn - 9f9y5/X/cef1/2/n9f9u5/X/bOb1/2rm9P9o5vT/Z+X0/2Tl9P9i5fT/YOT0/13k9P9b4/P/WOPz/1Xi - 8/9S4vP/T+Hz/03h8v9K4PL/RuDy/0Pf8v8unqz/PNvu/zre8f833fH/SM/f/4GvtP8t3PD/Ktvw/yba - 8P9TvMj/j4+P/3eYnP8A1O3/ANTt/wDU7f8A1O3/Oam2/3Z2dv8nssL/ANTt/wDU7f8h3fT/T+n8/0/p - /P9P6fz/T+n8/y7S6P8Ag6H/AIOh/wCDof8Ag6HsAICiHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAg599AIOh/wCDof8Ag6H/BYqn/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/TuP3/zm+1P8mqMH/QcLW/2fl9P9p5vT/aub0/2zm9f9s5vX/buf1/2/n - 9f9v5/X/cOf1/3Dn9f9w5/X/cOf1/3Dn9f9v5/X/b+f1/27n9f9s5vX/a+b1/2rm9P9o5vT/ZuX0/2Xl - 9P9i5fT/YOT0/17k9P9c5PP/WePz/1fj8/9U4vP/UuLz/0/h8/9M4fL/SeDy/0bg8v9D3/L/QNzu/zvT - 5f863vH/N93x/zTd8f821ej/W6Or/yvb8P8n2/D/JNrw/zfL3P+RkZH/jZmb/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/WKy2/3ebn/8B0+z/JN70/0/p/P9P6fz/T+n8/0/p/P832/D/AYSj/wCDof8Ag6H/AIOh/gCF - oUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEonYAg6H/AIOh/wCDof80x93/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tq/yCsxv8EiKb/AIOh/wCD - of8BhKL/W93u/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2vm9f9r5vX/bOb1/2zm9f9s5vX/a+b1/2rm - 9P9q5vT/aeb0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/17k9P9d5PT/W+Pz/1jj8/9W4/P/VOLz/1Hi - 8/9O4fP/TOHy/0ng8v9G4PL/Q9/y/0Hf8v8+3vH/O97x/zfd8f813fH/Mtzx/y7c8P89qrf/KNvw/yXa - 8P8h2u//F9br/21xcv9tenz/ANTt/wDU7f8A1O3/ANTt/wDU7f8G0en/fpuf/0jJ2f9P6fz/T+n8/0/p - /P9P6fz/PN/0/wOKqP8Ag6H/AIOh/wCDof8Ag6GCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAhaEuAIOh+ACDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9L5Pf/L8DX/xCYs/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weKp/9c4vH/YOT0/2Ll9P9j5fT/ZOX0/2Xl - 9P9i4fD/Wtnq/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Ll9P9h5PT/YOT0/17k - 9P9c5PP/W+Pz/1nj8/9W4/P/VOLz/1Li8/9Q4fP/TeHy/0vh8v9I4PL/ReDy/0Pf8v9B3/L/Pt7x/zve - 8f833fH/Nd3x/zLc8f8v3PD/K9vw/yvL3v8m2vD/Itrv/xPX7v8B1O3/Rlxe/0JrcP8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wLU7f9Axtb/aJuh/0/p/P9P6fz/T+n8/zrg9P8Di6n/AIOh/wCDof8Ag6H/AIOipAD/ - /wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDodMAg6H/AIOh/wmPrP9K4vb/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1On/H6vF/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ - p/8MqMP/RNPm/1vj8/9c5PP/XuT0/1/k9P9g5PT/W9/x/wyPq/8Ag6H/NLfN/2Ll9P9i5fT/YuX0/2Ll - 9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k9P9b4/P/WuPz/1nj8/9W4/P/VOLz/1Pi8/9R4vP/T+Hz/0zh - 8v9K4PL/SODy/0Xg8v9C3/L/QN/y/z3e8f863vH/N93x/zXd8f8y3PH/L9zw/yzb8P8p2/D/Jtrw/yHa - 7/8L1u7/ANTt/wDU7f8jTVL/HHN+/wDU7f8A1O3/ANTt/wDU7f8G1e7/POT5/0/p/P9RipH/T9/w/0/p - /P843vT/Ao6r/wCDof8Ag6H/AIOh/wCDobkAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgqGNAIOh/wCDof8BhKL/O8/l/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9v8vv9f/D5ey/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCXs/8At9H/ANLr/wDU7f8F1e3/Mdzw/1jj8/9Z4/P/W+Pz/1vj - 8/9Dydz/AIOh/wCDof8SlbH/XuT0/17k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1fj - 8/9W4/P/VOLz/1Li8/9R4vP/T+Hz/03h8v9K4PL/SODy/0bg8v9D3/L/Qd/y/z/f8f883vH/Od7x/zfd - 8f813fH/Mtzx/y/c8P8s2/D/Kdvw/yXa8P8X2O//BdXt/wDU7f8A1O3/ANTt/wg6QP8Emqv/ANTt/wDU - 7f8A1O3/Etnw/0Xn+v9P6fz/T+n8/0rM2/9Fo6//MNnv/wKKqP8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGhQQCDof4Ag6H/AIOh/ySxyv9P6fz/T+n8/0/p - /P9P6fz/T+n8/z7U6f8fqsT/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqo/wWpxP8Ax+H/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Dtbu/zve8f9W4/P/V+Pz/z7H2/8Ag6H/AIOh/w6Vsf9U5/f/WOT0/1nj - 8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9Q4fP/TuHz/03h8v9K4PL/SeDy/0fg - 8v9F4PL/Qt/y/0Df8v8+3vH/O97x/zne8f823fH/NN3x/zHc8P8u3PD/K9vw/yjb8P8V2O7/A9Xt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/AEZO/wDN5f8A1O3/AtTt/yjf9P8yw9v/IK3G/0LZ7f9P6fz/Tuj8/x2L - m/8AhaP/AIOh/wCDof8Ag6H/AIOiygCGoRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH - pREAg6HiAIOh/wCDof8OlbH/Tef5/0/p/P9P6fz/SuP2/y6+1v8PlrH/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/E5y3/zPF2/9N5/n/TOj7/yTe9P8B1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W - 7v823fH/O8fb/wCDof8Ag6H/DpWx/0/p/P9P6Pv/UuX4/1Pj9P9U4vP/VOLz/1Pi8/9S4vP/UeLz/0/h - 8/9P4fP/TeHy/0zh8v9K4PL/SODy/0fg8v9F4PL/Q9/y/0Hf8v8+3vH/PN7x/zre8f833fH/Nd3x/zLc - 8f8w3PD/Ldzw/yvb8P8b2e//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Afoz/ANTt/xPZ - 8P9C5vr/SeL1/wCDof8Ag6H/AIOh/0jn+v8VwNn/AGZ+/wCBnv8Ag6H/AIOh/wCEobwAgKoMAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhpQCDof8Ag6H/AoWk/0HX7P9P6fz/PtPo/x6q - xP8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xe50v9M6Pv/T+n8/0/p/P9P6fz/T+n8/z7l - +f8N1/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Gvtf/AIOh/wCDof8OlbH/T+n8/0/p - /P9P6fz/T+n7/0/l+P9P4fT/TuHz/03h8v9N4fL/S+Hy/0rg8v9J4PL/SODy/0bg8v9E3/L/Q9/y/0Hf - 8v8+3vH/Pd7x/zve8f853vH/Nt3x/zTd8f8y3PH/L9zw/y3c8P8h2u//Cdbu/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/weyx/8y4fb/T+n8/0/p/P9D2u7/AIOh/wCDof8BhaL/B6fC/wCD - of8AgqD/AHWP/wCDof8Ag6GrAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - olgAg6H/AIOh/wCDof8ntc7/Lr3V/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4AgqLrAIOh/wCD - of8Ag6H/AIOh/w6nwf9C5fj/T+n8/0/p/P9P6fz/T+n8/07o/P8v4fb/CNfv/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wC91v8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o+/9M4/f/SeDy/0jg - 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Df8v8+3vH/PN7x/zve8f853vH/N93x/zXd8f8y3PH/MNzw/y7c - 8P8f2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v8q3/X/TOj7/0/p - /P9P6fz/T+n8/03n+v8BhaL/AIOh/wCDof8Ag6H/AIOh/wCDof8Af5z/AIKgjwCqqgMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wKFpP8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDoekAgqGLAIahJgCqqgMAgqCJAIOh/gCDof8Ag6H/AIOh/wKNqv8q0un/Tuj8/0/p - /P9P6fz/T+n8/0/p/P9M6Pv/K+D1/wbV7v8A1O3/ANTt/wDU7f8A1O3/AL3W/wCDof8Ag6H/DpWx/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/SOP2/0Pf8v9B3/L/QN/y/z/f8f8+3vH/PN7x/zre - 8f853vH/N93x/zXd8f8y3PH/K9vw/x7Z7/8R1+7/BdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wfW7/8o3/T/Suf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCD - of8Ag6H/AIOh8wCCoFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD - oM0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDob0AhKFXAJKSBwAAAAAAAAAAAAAAAAAA - AAAAd5ZcAH+c/wCDof8Ag6H/AIOh/wCDof8QqsX/QeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+j7/y7g - 9f8O1+//ANTt/wDU7f8Avdb/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/R+f6/yfe8/8S0un/CanE/wqvyf8S1uz/Etfu/w7W7v8M1u7/BtXt/wHU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y7/8x4fb/Ten8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDodYAhp8oAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFsAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCC - oIkAgKMkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabn8AXnP/AHeS/wCDof8Ag6H/AIOh/wCD - of8ChqT/J8ff/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P895Pn/Hdzz/wK91/8Ag6H/AIOh/w6V - sf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKeuP8Ag6H/AIOh/wC5 - 0/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/B9bv/yPd - 9P8/5Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8jsMr/AIOh/wCD - of8Ag6HYAICqDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD - ocMAg6H/AIOh/wCDof8Ag6H+AIOhuwCEolUAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgIACAFpw4ABab/8AW3D/GZiw/wOGpP8Ag6H/AIOh/wCDof8Ag6H/BpGu/yzP5f9M6Pv/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/Ncvg/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P8uvtb/AIOh/wCDof8Ag6H/AL7Z/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8O2O//JN70/zvk+P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun8/03o - +/9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDofwAg6H/AIOhmACDoCMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabkoAWm//AFpv/wJdcv9I3fD/RNrv/xeh - vP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmSr/8vz+X/TOj7/0/p/P9P6fz/T+n8/0/p/P84y+H/AIOh/wCD - of8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wOHpf8Ag6H/AIOh/wCd - uv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1u7/FNrw/yTe9P814vf/R+b6/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/z7T6f8ltM3/Qdrv/0Di9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD - of8Ag6H/AIOh4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAImdDQCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFpvrQBab/8AWm//Gomd/0/p/P9P6fz/T+n8/znM4v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCD - of8Fi6n/Ir/Z/0Lj9v9P6fz/T+n8/zjL4f8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/07o+/8SmrX/AIOh/wCDof8EjKn/Jdju/yfe9f8s4PX/MOH2/zbi9/8+5fn/Sej7/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/A4el/wCDof8Ag6H/Qdfs/0/p - /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G3vH/AIOh/wCDof8Ag6H/AICqDAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVahgAWm/7AFpv/wBab/85wtX/T+n8/0/p - /P9P6fz/T+n8/03n+v8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xOivP8wz+X/Mcje/wCD - of8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KrnR/wCDof8Ag6H/AIOh/zfK - 4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9D5fn/KdXq/w2uyf8Ag6L/AIOh/wCDof8Jj6v/Rt3y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P8Dh6X/AIOh/wCDof8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFlveABab/8AWm//CmyB/07o+/9P6fz/T+n8/0/p/P9P6fz/T+n8/znA1P8IaX7/AGqE/wB+ - mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0HX7P8ChaT/AIOh/wCDof8eqsT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/0/p/P9P6fz/SOf6/zbf9P8gy+P/CqnF/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8OlbH/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAWm/dAFpv/wBab/8ppLj/T+n8/0/p - /P9P6fz/T+n8/0zj9v8gk6j/AFtw/wBab/8AWm//AFpv/wBrgtIAgp/MAIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/r/D5ax/wCDof8Ag6H/Co+s/0vj - 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/PuP4/zPc8f8p0uj/GsHa/wqlwf8Aiaj/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbT/TOX4/0/p/P9P6fz/T+n8/0/p - /P9P6fz/GqW//wCDof8Ag6H/AIOiewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFtuQwBab/8AWm//AV1x/0fa7f9P6fz/T+n8/0/p/P89yNz/C26D/wBab/8AWm//AFpv/wBa - b/YAW3BwAAAAAQAAAAAAg58lAISifgCCodcAg6H/AIOh/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p - /P9P6fz/T+n8/ya0zf8Ag6H/AIOh/wCDof8Knbn/EKzF/xCuyP8Sr8j/EavG/wymwP8InLn/A5Sy/wCK - p/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HMAIShegCD - oYoAg6H/AIOh/wCDof8Yor3/Tuj7/0/p/P9P6fz/T+n8/0/p/P8ltM3/AIOh/wCDof8Ag6GiAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+oAFpv/wBab/8YhZr/T+n8/0/p - /P9N5fj/JZ2x/wFccf8AWm//AFpv/wBab/8AWm/LAFdwKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC - oTEAg6GrAIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+n/AYSi/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIKg/wB6lv8AeZOgAICfIAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8eq8T/T+n8/0/p - /P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDocYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAFlzFABab/kAWm//AFpv/ze/0v9P6fz/QM/i/w90if8AWm//AFpv/wBab/8AWm/7AFpwgABA - gAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/w6Vsf9P6fz/T+n8/0/p - /P9P6fz/TOb5/wySr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP8Xobz/I67H/wBke/8AXHH/AFpv/gBddBYAAAAAAAAAAAAA - AAAAAAAAAKqqAwCDoK8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/PdHn/wCDof8Ag6H/AIOh7QAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29zAFpv/wBab/8Jan//Teb5/yqm - uv8CXnP/AFpv/wBab/8AWm//AFpv1wBYcTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIWgSwCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCDof8Ag6H+AIKhxgCD - ocAAg6HMAISh2wCDoeUAg6HjAISh2wB+m+YAepX/AHiT/wZ+mP8ot8//M8Xc/z7T6P9I4PT/T+n8/0/p - /P8xssb/AFpv/wBab/8AWm/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqBgCDob0Ag6H/AIOh/wCD - of8uv9b/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAQBab9cAWm//AFpv/xyNof8Se4//AFpv/wBab/8AWm//AFpv/gBacJAAVXEJAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaBLAIOh/wCDof8OlbH/T+n8/0/p - /P9P6fz/O8/l/wCDof8Ag6H/AIOh/wCCooMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdtIwBa - b/4AWm//AFpv/0DN4f9P6fz/T+n8/0/p/P9P6fz/T+n8/x2Po/8AWm//AFpv/wBabpQAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDotIAg6H/AIOh/wCDof8zxdv/T+n8/0/p/P8Giqj/AIOh/wCD - of8AgqE5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXHE9AFpv/wBab/8AWm//AFpv/wBa - b/8AWm//AFpv/wBab+EAWXBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAACCoj8Ag6H/AIOh/wyTsP9P6fz/T+n8/0vj9/8Jj6z/AIOh/wCDof8Ag6HQAICqBgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvwQBab/8AWm//Ipis/0/p/P9P6fz/T+n8/0/p - /P9P6fz/CWqA/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCE - odsAg6H/AIOh/wKFo/87z+X/T+n8/xKatf8Ag6H/AIOh/wCEol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABbb6MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXCgAFV3DwAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/0/p - /P9P6fz/HqrE/wCDof8Ag6H/AIOh+ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAWnBgAFpv/wBab/8GZHr/Teb5/0/p/P9P6fz/T+n8/0TW6f8AWm//AFpv/wBab/4AWXMUAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICjJACDoeoAg6H/AIOh/wWJp/8/1On/HajC/wCD - of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWmkRAFpv/wBab/8AWm//AFpv/wBa - b/8AWm/pAFpwUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Jj6z/T+n8/zfK4P8Ag6H/AIOh/wCDof8Ag6F3AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVagwAWm/zAFpv/wBab/81us7/T+n8/0/p - /P9P6fz/MLLF/wBab/8AWm//AFpv1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIKiNwCDofMAg6H/AIOh/weLqf8eqsP/AIOh/wCDof8Ag6GqAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABacGkAWm//AFpv/wBab/8AWm//AFlvsQBZbxcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wmP - rP9J4fX/B42q/wCDof8Ag6H/AIOhxQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABab58AWm//AFpv/xeFmP9P6fz/T+n8/0/p/P8djqP/AFpv/wBab/8AWm6UAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiQgCDofkAg6H/AIOh/wCD - of8Ag6H/AIOh/wCDodEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/wBb - cGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/xqkv/8Ag6H/AIOh/wCDofQAgKMkAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AV1x/0fb - 7v9P6fz/T+n8/wlqgP8AWm//AFpv/wBbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISgWwCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh9QD//wEAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAVW0VAFpupABab6UAWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCD - of8Ag6H/AIOh/wCDof8Ag6H/AIOiawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAABAFpv3ABab/8AWm//KqW5/0/p/P9E1un/AFpv/wBab/8AWm/+AFlzFAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCD - of8Ag6H/AIOh/wCDof8Ag6H/AISeHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA//8BAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBa - b/8McIX/T+n8/zCyxf8AWm//AFpv/wBZb9MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACD - of8Ag6H/AIOh/wCDof8Ag6HvAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABebx4AWm/9AFpv/wBab/89yNz/HY6i/wBab/8AWm//AFpulAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAA//8BAIOhowCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Ag6H/AIOh/wCCoF4AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa - b7oAWm//AFpv/x+Sp/8JaoD/AFpv/wBab/8AW3BUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiygCDof8Ag6H/AIOgRgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE - ojwAg6H/AIOh/wCDof8Ag6GtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvWgBab/8AWm//AFpv/wBab/8AWm//AFpv/gBZ - cxQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAgKoGAIOhZwCDo1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOjJwCDof8Ag6H/AIOh/wCAohYAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVXEJAFpv8ABab/8AWm//AFpv/wBab/8AWm/SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIShcgCDoKcAhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+XAFpv/wBab/8AWm//AFpv/wBa - b5MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////4///4H///////////////+H - //+B///8f///////////g///gf//+H///////////4H//wH///B///////////+A//8B///gf/////// - ////wH/+AP//wH///////////8A//gD//4D////////////AH/AAA/8A////////////wA4AAAA+AP// - ///////8f8AAAAAAAAH//////////B/AAAAAAAAB//////////wP4AAAAAAAAf/////////+A+AAAAAA - AAH//////////gDAAAAAAAAB//////////8AAAAAAAAAAP//////////AAAAAAAAAAA//////////4AA - AAAAAAAAH//////5//+AAAAAAAAAAAf/////8H//wAAAAAAAAAAD//////Af/4AAAAAAAAAAAP/////4 - B/8AAAAAAAAAAAB/////+AH+AAAAAAAAAAAAP/////wA/AAAAAAAAAAAAB/////+ADgAAAAAAAAAAAAP - /////gAAAAAAAAAAAAAAB/////8AAAAAAAAAAAAAAAP/////AAAAAAAAAAAAAAAB/////4AAAAAAAAAA - AAAAAP////+AAAAAAAAAAAAAAAB/////wAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAB/////gAAAA - AAAAAAAAAAAP//n/8AAAAAAAAAAAAAAAB//wH/AAAAAAAAAAAAAAAAP/8AfwAAAAAAAAAAAAAAAD//AD - 8AAAAAAAAAAAAAAAAf/wAeAAAAAAAAAAAAAAAAD/8AHgAAAAAAAAAAAAAAAAf/AAwAAAAAAAAAAAAAAA - AD/wAMAAAAAAAAAAAAAAAAA/8ACAAAAAAAAAAAAAAAAAH/AAgAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAA - AAAAAAAP8AAAAAAAAAAAAAAAAAAAB/AAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAD4AAAAAAA - AAAAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAIAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAA - AAAAAAAAAAAAAQA4AAAAAAAAAAAAAAAAAAMAeAAAAAAAAAAAAAAAAAADAPwAAAAAAAAAAAAAAAAAAwH8 - AAAAAAAAAAAAAAAAAAcH/AAAAAAAAAAAAAAAAAAHD/wAAAAAAAAAAAAAAAAADx/wAAAAAAAAAAAAAAAA - AA9/4AAAAAAAAAAAAAAAAAAP/4AAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAB/8AAAAAAAAAAAA - AAAAAAAf8AAAAAAAAAAAAAAAAAAAH+AAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAA/gAAAAAAA - AAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///+AAAAAAAAAAAAAAAAAf/// - gAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAA - Af//+AAAAAAAAAAAAAAAAAP///AAAAAAAAAAAAAAAAAD///wAAAAAAAAAAAAAAAAB///4AAAAAAAAAAA - AAAAAAf//8AAAAAAAAAAAAAAAAAP//+APAAAAAAAAAAAAAAAH///g/4AAAAAAAAAAAAAAB//////AAAA - AAAAAAAAAAA//////4AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAH//////AAAAAAAAAAAAAAD///// - /gAAAAAAAAAAAAAB//////wAAAAAAAAAAAAAA//////8AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAP - //////AAAAAAAAAAAAAAH//////wAAAAAAAAAAAAAD//////4AGAAAAAAAAAAAD//////8AP4AAAAAAA - AAAB///////AP+AAAAAAAAAAA///////gf/AAAAAAAAAAAP//////8f/wAAAAAAAAAAD/////////4AA - AAAAAAAAA/////////+AAAAAAAAAAAP/////////gAAAAAAAAAAD/////////wAAAAAAAAAAA/////// - //8AB8AAAAAAgAH////////+AA/wAAAAA8AB/////////gAf+AAAAAfgAf////////4Af/gAAAAH8AH/ - ///////8AP/4AP8AB/gB/////////AP/+AH/AA/8Af////////gH//gD/4AP/gD////////4H//4B/+A - D/8A////////+D//+Af/gA//gP////////D///gP/8Af/8D////////5///4H//AH//g//////////// - +B//4B//4P////////////g//+Af//D////////////4f//gP//4////////////+H//8D////////// - //////j///A////////////////9///wP///////////////////+H///////ygAAAAwAAAAYAAAAAEA - IAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKHfAIOh/wCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAACXXSnAFpv/wBcb44AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAgp81AAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6CMAIOh/wOHpMQAgJ8IAAAAAAAA - AAAAAAAAAAAAAABVcRIBXHH6G4uf/wRhdssAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAoajoACD - of8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYBoqo+QuT - sPcDh6S3AKqqAwAAAAAAgIACAIakKgBrhZYId475I6zF/wFshvsAgp9aAIOiQgCAphQAAAAAAAAAAACJ - nQ0Dh6S/AoWj/gCDof8AgJ0aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJcb3UAW2+BAAAAAQAA - AAAAgp8tAYSh/Ubd8f8Jj6z4AoWj0QCGpLcAhaL0AIOh/wCDof8AhKL/Aouo/wCHpf8AhKL+AIOh/wCD - of8AhaTgAIOhqQKGpOIPl7P4KrnR/wOIptwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb - cMgAWm//A15zyQBbbSoAjo4JAYSh/UHX7P9F3PD/Boyo/w2fu/4p0Ob/M9jw/yfA1/8cqsP/Qub5/z/k - +f875Pj/Md/0/yXU6v8Qssv/Aoik/xiivf9M5vn/GaS+/ACFoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABecRsAWm//DnOH/QFkefwAgJzmA4ek/zTG3f9M6Pr/RN7x/0Xc8P8kts7/BoSf/zSe - tf8Yi6T/FrzT/yHS6P8q1ur/Ndzv/0bk9/8Mk6//I7DK/03o+/9O6Pv/BIim/wCEou0AhKFXAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKgMwCD - oZAAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAADXnaiC4We/AGEov8UqcP/FaK9/ye1zv9B4/n/HtHo/wiW - sf8smrP/qd/w/8jw/v98xNb/BJSv/wDA1/8AwNf/AMDX/wDA1/8Kxt3/ItPo/0Di9/9B2O3/A4el/xu6 - 0/4AgqH/AISirACAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIOiUgCDof8Ag6H/AISgbgCAqgYAAAAAAAAAAACImQ8Ag6DHAoem/yzS5/9P6fz/LMjf/w+8 - 1f8AxNv/BZiy/z6mvv/A7f7/tev+/6no/v+u5vX/Doai/wC50P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB - 2P8Syt//LtHm/0/p/P8+4PX/DZq3+wCFo+UAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOHpcMBhKP/CI6r9wKGo+gAgqJgAIiiHgCFpNcFkq76PeH2/03p - /P8i2/L/AMjg/wDF3P8AtMz/CIah/7Dm+f+v6v7/oub+/4nj/P+b6vz/SbHG/wCdt/8AxNv/AMTb/wDE - 2/8Aw9r/AMPa/wDD2v8Aw9r/AMPa/xPN5P9B4/b/TOj7/yC40v0AhaL2AISiTQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCny0BhKL8P9Tp/y6+1v8Giqj4Aoek+QWS - r/xA4/j/Sej7/xHV7f8AyeH/AMjg/wDI4P8Bn7r/RKrD/7js/v+g5f3/h+P8/23g+/9r4/r/edrs/wOF - oP8AxNv/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG3f8DyN//Ldrv/0/p/P8ryuD/AISi/ACC - oFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChKOgFJy3+E/p - /P9M5fj/KrnR/wWJp/8Xo73/DdTs/wDM5P8AzOT/AMvj/wDL4/8GmbT/YrvT/67q/f+E4/z/a+D6/1Le - +f862/j/d+j6/yeft/8AqsP/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/T - 6f9O5/v/Ls7k/wCDof0AhKFXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAgJ8YAYWh+DjL4f9P6fz/T+n8/0ni9v8Nx9//AM7m/wDO5v8Azub/AM7m/wDO5v8En7n/UbPN/5no - /f9p4Pr/T935/zbb9/8n2vf/VuL5/17O4v8Bi6b/AMzk/wDN5f8AzeX/AM3l/wDN5f8AzOT/AMzk/wDM - 5P8AzOT/AMzk/yPT6P995fL/r/T9/4/j7/8nl7D8AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfSACD - of8Ag6KhAISjOgAAAAAAAAAAAoWijgqQrf9O5/v/T+n8/yPd8/8A0ur/ANLq/wDS6v8A0ur/ANHp/wDR - 6f8AsMn/CYej/3jg9v9r4/r/QN34/yfa9/8n2vf/Mtz3/33o+f8Qjqj/ALfQ/wDP5/8Az+f/AM/n/wDP - 5/8Az+f/AM/n/wDP5/8W0uj/VNbn/1HE1f8rqb//Ip63/1C6zf+R1+P/Sae99wCAoyQAAAAAAAAAAAAA - AAAAAAAAAIKhagCDof8AlLH7AIOh/QCDokoAAAAAAISiugOIpv8uvtb/QOX5/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8Azef/BJSw/waHo/8yu9T/VuH3/07h+P8w3Pf/J9r3/2bl+f9GvdT/ApSv/wDS - 6/8A0uv/ANLr/wDS6v8A0ur/ENTp/zDF2v8lts3/AKjD/wC2z/8At8//AK3H/w6ZtP8Bgp//W7XI/iaW - r9gAkpIHAAAAAAAAAAAAAAAAAIKgVgCDof8A1O3/AKK9/gCFo9oAhKE2AIOh/x250v8Gi6n/BtTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wO50/8Ci6f/DJGr/0HK4/9h5Pn/TuH4/2rl - +f985PX/AoCc/wDD3f8A1O3/ANTt/wTR6P8byuH/FcLb/wDO5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV - 7v82z+X/CpCt/zKdtf0AhaKWAAAAAAAAAAAAAAAAAIOiZQCIpfoA1O3/ALnU/wCDof8AhKGeCZq3+03p - /P8Nqsb/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/BtXt/wnW7v8M1u7/Dtbu/xDX7v8R1ez/Cq/J/wKF - oP8an7n/W9nu/2jh9f8orsf/AH+b/wO2z/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8P2PD/SOP2/xGduP8KiKT/AISiVQAAAAAAAAAAAIShmwCSsPkA0+z/ALHM/wCD - of8Ag6H/Kc/l/0fn+v8B1e3/ANTt/wDU7f8A1O3/BNXt/wnW7v8O1u7/Edfu/xXY7v8Y2O//Gtjv/xzZ - 7/8d2e//Htnv/xzS6P8MpL//AYCb/wKAnP8Im7X/EMvi/xDX7v8M1u7/B9Xt/wLU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Id30/03n+v8No77+AISi7wCAnxAAjqoJAIWi8ACu - yf8A0en/AMbf/wCfu/4Aj6z8R+b6/yrf9f8A1O3/ANTt/wPV7f8J1u7/Dtbu/xTX7v8Y2O//Hdnv/yHa - 7/8k2vD/Jtrw/ynb8P8p2/D/Ktvw/yrb8P8p2/D/Iczi/xvB2f8i2u//H9nv/xrY7/8V2O7/ENfu/wvW - 7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk+f853vP/AIqm+gCC - oYUAg6FnAIWj/ADL5P8A0en/ANPs/wDT7P8P1+//FZ65/wKLqP8Cob3/AqTA/waqxf8Krsj/DrLL/xO2 - z/8XvNT/Icvi/yzb8P8w3PD/Mtzx/zXd8f813fH/Nt3x/zbd8f813fH/Mtzx/zDc8P8t3PD/Kdvw/yXa - 8P8f2e//Gtjv/xTX7v8O1u7/B9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXZ - 8f9P6fz/DbjS/wCFo+oAhaLuAKbB/gDT7P8A0+z/ANLq/wDS6/8i3fT/GKO9/wCDof8ChaP/Ia7H/x2p - w/8ZpL//FJ66/xGatv8OlrL/C5Wx/zfd8f873vH/Pt7x/0Hf8v9B3/L/Qt/y/0Lf8v9B3/L/Pt7x/zze - 8f843fH/NN3x/y/c8P8p2/D/JNrw/x3Z7/8X2O//ENfu/wjV7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f9N6fz/IdPq/wCDof8Aiqf6ANDo/wDT7P8A0ur/ANHp/wDS6v8u3/X/Qeb6/wC+ - 2P8Diqj/GaS//0vj9/9P6fz/T+n8/0/p/P9P6fz/P9/y/0Pf8v9H4PL/SuDy/03h8v9O4fP/TuHz/07h - 8/9M4fL/SuDy/0fg8v9D3/L/P97x/0/i8/9c4/T/ZOX0/2Lk9P9X4/P/Qd/y/yDa7/8H1e3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9E5vr/Ktft/wCDof8Awdv/ANHp/wDN5v8AyOD/AMzk/wDP - 5/864/f/OOP4/wDU7f8OzeX/CJaz/wuRrv9B2O3/T+n8/0/p/P9M5/r/SeDy/07h8/9S4vP/VuPz/1nj - 8/9a4/P/WuPz/1nj8/9Y4/P/VeLz/1Li8/9W4/P/duj1/3Th9v9gyPT/Xrby/1qw8f9ZufL/Vczz/1ni - 9P883vH/Cdbt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9L6Pv/JM3k/wCEovcA0ur/ANLq/wC3 - 0v8Ag6H/AISi/gCHpf074ff/M+L3/wDU7f8T1+7/Jtru/xSpw/8Dh6X/MsTb/0/p/P9O5ff/U+Lz/1nj - 8/9e5PT/YeT0/2Tl9P9m5fT/ZuX0/2Xl9P9j5fT/YOT0/2Ll8/9/6vb/Z8vy/06d7f9tqeH/krTT/5m3 - 0P+Ksdb/WaPn/06j7P9K1fH/Od7x/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wnW7/9P6fz/GLPM/ACE - orIA0uv/ALvV/wCEov0Ag6CvAIKiWACDof832u//LtTp/wDN5/8U1+7/Ltzw/zfd8f8qwNb/A4il/yCs - xv9S4/P/XeT0/2Pl9P9p5vT/beb1/3Dn9f9y5/X/cuf1/3Hn9f9v5/X/a+b1/33p9v9lxen/SZfl/6S7 - zv/Dycn/wcjI/8LIyP/Fy8v/ys/Q/5C22f9Dl+T/Otfw/xrZ7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c - 8/9O6Pz/BYuo/QCEoFkAp8P/AISi/gCEoo8A//8BAAAAAACFo/AVpsL/AYWi/we91v8P1+7/Nd3x/z3e - 8f9G4PL/Q9Pm/w6Trv8xts3/ZuX0/23m9f9z5/X/eOj1/3zp9v9+6fb/fun2/33p9v966fb/fOn1/3ne - 9/80gL//qsDS/8vQ0P++xcX/vcTE/7m1rf+0oY7/vbeu/9jc3P+Br9v/OKTi/yTa7/8C1O3/ANTt/wDU - 7f8A1O3/ANTt/zji+P882u//AISj6wCAqgYAhaPxAIShXwAAAAAAAAAAAIGjRQKHo/oNlLH/P9Tq/zfj - 9/8H1e3/Od7x/0Pf8v9M4fL/VeLz/1zj8v9l4/L/b+f1/3bo9f996fb/g+r2/4fr9/+K6/f/iuv3/4nr - 9/+E6vb/hur2/1S27v86mt7/wcTE/8DHx//Ax8f/vLCj/6JlNv+IVzH/omU2/8Gwof/a4uj/MILX/xvQ - 7P8H1e3/ANTt/wDU7f8A1O3/B9bv/03o/P8htMz8AIKikQAAAAAAgJwSAAAAAACAnBIChaSlAYWj/Cm5 - 0f5N5/r/T+n8/0/p/P8X2/H/M93x/0jg8v9R4vP/W+Pz/2Tl9P9t5vX/duj1/37p9v+H6/f/juz3/5Pt - 9/+W7fj/lu34/5Pt9/+O7Pf/iev3/0Ko6v8+r/z/gpek/8TKyv/O09P/p3lU/0suGP8AAAD/QSgV/6Nw - SP/4+fn/O4fU/xG95f8C1O3/ANTt/wDU7f8A1O3/Id30/03o/P8Gi6j/AIOgRgAAAAAAAAAAAIKiWAKG - o+8RmbX4Q9nu/0/p/P9P6fz/T+n8/0/p/P9B4fX/INXt/0vh8v9U4vP/XuT0/2jm9P9x5/X/e+n2/4Tq - 9v+O7Pf/lu34/53u+P+i7/j/ou/4/53u+P+W7fj/juz3/0Go6v9Is/z/MlVt/3Z5ef/a3t7/sIZk/3RS - Of+DgoL/uKqf/7GIZ///////LX3M/wm65P8A1O3/ANTt/wDU7f8A1O3/PuX5/zfc8P8Ag6L8AIiZDwAA - AAAAgqJgAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYel/0nf8P9W4/P/YeT0/2vm - 9f916PX/f+n2/4nr9/+S7ff/nO74/6bw+f+t8fn/rfH5/6Tw+f+a7vj/kez3/1C38P9LpuP/PnWa/yw2 - Pf/Lzs7/5NzV/6lzSP/Xu6b/1Lmk/9zSyf/n7fH/Fm3D/wHO6v8A1O3/ANTt/wDU7f8L1/D/T+n8/xu5 - 0/4AhaLDAAAAAAAAAAAAgJkKAIWgSwCCoDMAgqAzAIKgMwCCoDMAhKHJC6XB/0Tb7/8PzOX/AMzm/yvb - 8P9X4/P/YeT0/2zm9f926PX/f+n2/4nr9/+T7ff/nO74/6bw+f+u8fn/rPH5/6Tw+f+a7vj/kOz3/3rg - 9v9auPn/Tomx/zJNXv8qLTD/4+Tk//b19P/j29T/6erp//39/f9imc3/EZTQ/wDU7f8A1O3/ANTt/wDU - 7f8q3/X/S+f7/wOJp/sAg6JjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8BaoTdAISi/zfh - 9v834/f/ANTt/wXV7f9O4fP/YOT0/2rm9P9z5/X/fen2/4fr9/+P7Pf/mO74/5/v+P+i7/j/oe/4/5zu - +P+V7fj/i+v3/4Lq9v9cve3/b7zw/0Zof/8xQUz/JSkr/6usrP/4+Pj/+vv8/3qn0v8PcL3/Cc/r/wDU - 7f8A1O3/ANTt/wfW7v9L6Pv/LdHn/wCFo/IAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgNf - dMYPdor4Dpe0/w2wzP9O6Pz/Ftrx/wC81v8EjKr/XuT0/2fl9P9w5/X/eOj1/4Hq9v+J6/f/j+z3/5Tt - 9/+X7fj/le34/5Lt9/+M7Pf/hOr2/33p9v905/X/Xrfk/1+Rs/9mk7L/KD1M/w8VG/8AAQH/AQUJ/wc2 - Vf8axuH/AdTt/wDU7f8A1O3/ANTt/y/h9v9K5/v/BpGv+QCEoXoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAVYAGA11zyABab/8glKn+C3qR9wCEof8s2u//QNzx/wKKqP8tvdT/Q+T3/2Hl9P9q5vT/cuf1/3rp - 9v+A6vb/her2/4nr9/+L6/f/iev3/4fr9/+C6vb/fOn2/3Xo9f9t5vX/ZeX0/2DZ8f8+d5P/PG2M/y1T - a/8jUmv/ECsz/wkzOP8EOD//AM7m/wDU7f8A1O3/Fdnx/07o/P8ixt3+AIaj5ACAmQoAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBbcP0DX3WvAFlubQCBnq4Dk7D4FKC7/xagu/9P6fz/T+n8/0rm - +v9g5fX/aub0/3Hn9f926PX/e+n2/33p9v9/6fb/fen2/3vp9v936PX/cuf1/2zm9f9l5fT/XeT0/1Xi - 8/9Bvsz/Lpaj/0fBz/9BtMP/R4GI/yq0xP8jjJj/HoKO/wDU7f8L1/D/Sej7/zbc8f8Ag6H9AIOgRgAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXMUAFhxNAAAAAAAAAAAAAAAAACApA4AhKLsBImm/0ff - 8/9P6fz/T+n8/0/p/P9N6Pv/StTm/1bV5/9s5vX/b+f1/3Hn9f9y5/X/cef1/2/n9f9s5vX/Z+X0/2Ll - 9P9b4/P/VOLz/03h8v9F4PL/OM3f/zvW6P9Dydn/RMnZ/1mzvv8A1O3/RKy4/xnN4/9E5vr/QeP4/wOO - q/kAhKKRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG - oSYBhKP5Ncfe/0/p/P9P6fz/S+T4/y/A1/8Nl7P/AYWk/zC6z/9h5PT/YuTy/0jJ2/9m5fT/ZeX0/2Tl - 9P9g5PT/XeT0/1jj8/9S4vP/TOHy/0Xg8v893vH/Nt3x/y3c8P8l0OX/D8bc/yiPm/8A1O3/Edfu/1TB - z/9B5Pn/CJez+gCFo8IAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIC/BAKIpM8cqMH7T+n8/z/U6v8eqsT/BIil/wqRrv8GsMz/AM3n/wbV7f8z3fH/Qszg/wWJ - p/9X5fX/WePz/1jj8/9V4vP/UuLz/03h8v9I4PL/Qt/y/zze8f813fH/LNvw/xbY7/8E1e3/ANDo/wGa - q/8j2PD/Ncrg/zW/0f8Fkq76AISj0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAISigQSJp/squtL/DZSw9gGEovwChqP5Cpm1/kTk+P9M6Pv/Jd70/wPV - 7v8A1O3/BcLb/wSIp/9P6fz/Tuf6/03j9P9J4PL/RuDy/0Lf8v893vH/N93x/y/c8P8a2O//BdXt/wDU - 7f8A1O3/Fdnx/zzX6/85zeP/Aoek/wKIpfwAgZ7FAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCDof8Dh6ThAIOicwCAphQAgKEmAH2a/ASK - qP8sz+X/Tuj8/0fm+v8k3vT/BcPc/wOIp/9P6fz/T+n8/0/p/P9A5fn/Fr3V/xLK4v8Q1+7/Ctbu/wHU - 7f8A1O3/A9Xu/xrb8v8+5fn/T+n8/0nm+v9F3PD/AIOh/wCDopYAmZkFAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh+gCCoj8A//8BAAAAAAAA - AAAAW3BUBWR6+Ci3z/8DiKX/DZu3/zLU6v9M6Pv/O9Hn/wSJp/9P6fz/T+n8/0/p/P87z+X/AIOh/wi/ - 2f8L1/D/Ftrx/yPd9P814vf/TOj7/0fe8v8uyeD/R+H1/0/p/P9O6Pv/AYWi/QCCoDMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAADX3bBJZ2x/k/p/P9C1ur/DYmj/QCCoP8Gjar9GLDK/gSIp/9P6fz/T+n8/0vk - 9/8Kj6z/KbjP/0/p/P9P6fz/T+n8/0rn+/874fX/I8vj/wiat/0BhKL/N8rg/0/p/P9P6fz/CpCt9QCE - oFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbCEAW3D9RNXo/zvF2f8IaXz4AmB00ABogDYAg6BpAISj0gOH - pf9P6fz/T+n8/x2pw/8Gjqz/GLbQ/xWzzv8Ur8r/C566/wGJpv8Ag6H/AIKg/wCCoKwAgqKPAoWj+z7T - 6P9P6fz/F5+7+ACEon4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcb4UQd4z4I5mt/AFccfsCXHB/AECABAAA - AAAAAAAAAISiPAOHpfpP6fz/N8vh/wGEov0Ag6K3AISjtwCBnMIAdZH+I67H/zDC2f8pq8H/A2B13AAA - AAAAAAAAAoajegOHpfpB1+z/JbPM/wKGpKkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABab/8AWm//Al913QBc - cDIAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfpJ4vX/BYuo+ACEonAAAAAAAAAAAAAAAAADYHXfLq7C/0/p - /P8cjaL7AlxwlgAAAAAAAAAAAAAAAAKEopcGjKn4KLbO/wSIptQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhuOgBa - b/8CXnKWAGBgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfoao735A4ikxQD//wEAAAAAAAAAAAAA - AAAAW252D3SI9k/p/P8HaHz2AFtwVAAAAAAAAAAAAAAAAAD//wEDhqSpAIOh/wCDof8AAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAGZmBQBccSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8BhKP3AISlHwAA - AAAAAAAAAAAAAAAAAAAAXHAZAVxw/Ti+0/8AWm/+AFlzFAAAAAAAAAAAAAAAAAAAAAAAgKoGAYSj2gCD - of8Ahp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACD - of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA190uwZlef8DYHXcAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAIiZDwCDoqEAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICfCACEoFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpwUgBab/8CXHGZAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAD//n8f7/8AAP/+Px/P/wAA//8eH4//AAD/9wAAD/8AAP/jAAAP/wAA//AAAA//AAD78AAAA/8AAPnw - AAAB/wAA+GAAAAD/AAD8AAAAAH8AAPwAAAAAPwAA/gAAAAAfAADOAAAAAA8AAMYAAAAABwAAwgAAAAAD - AADAAAAAAAMAAIAAAAAAAQAAgAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAACAAAAAABAAAYAAAAAAEAAHgAAAAAAQAA4AAAAAADAADAAAAAAAMAAIAAAAAAAwAA/AAAAAAH - AAD8AAAAAAcAAPgAAAAADwAA8AAAAAAPAADhAAAAAB8AAP+AAAAAHwAA/4AAAAA/AAD/AAAAAH8AAP4A - AAAA/wAA/jgAAAH/AAD++AAAA/8AAP/wAAAD/wAA//BgAAP/AAD/4fAA4f8AAP/j8eDh/wAA/+fx8fH/ - AAD///Px+f8AAP//9/H9/wAA////+f//AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDof8Giql2AAAAAAAA - AAAAAAAAAAAAAAVjeegGZXrcAAAAAAAAAAAAAAAAAAAAAAD//wEBhqS4AIKjPQAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACY2qrwWJ - pvoGi6daAAAAAACOqgkAb4haFIif8Qhzie4AhJ46AIWmFwAAAAAAgKoGB4ypsgCDof8AhKUfAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXnJyA2F1swBV - gAYFiaZwLsDX/g2UsfYBiqfhAYqo8Amct/oTq8b/D6vF/gehvPEBj6ztAIin6geNqOstu9T5CI2r4AAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABc - bycAWm//B2qA4gCEoM8grsf+RN/y/irC2f8ru9P/JZu0/xaSrP8z2u7/O9/z/0Xl+P8Rnbj/N8vh/zvQ - 5f8GjKrnAIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhKLkAIKiNwAA - AAAAAAAAAAAAAAV1jdIIkq7/Ksri/xijv/8l1Ov/Ep23/3jG2//E7v3/W7bL/wCzzP8AwNf/AMDX/w7I - 3v8m1ev/L8Tb/yTD2v8KlbPxAIakcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASJ - psMBhKP+CI2qwwCFoiwAiad3DJ659UPk+P8p3PH/A8fe/waqwv9rwNf/ser+/5bk/f+Q3vD/CZu2/wDD - 2v8Aw9r/AMPZ/wDD2f8DxNv/ItPp/0bm+P8as8z2AIimqQCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAICmFAqRrug+1On/Epu28gqUsPs+3PD/F9ft/wDK4v8AyeH/D5ax/6vl+f+T5P3/beD6/2zk - +f8qpL3/AMDY/wDI3/8AyN//AMfe/wDH3v8Ax97/Csvi/z/h9v8qyd/6AImmtQCqqgMAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAABoupfyq60vhP6fz/O9Dn/xHA2f8Azeb/AM3m/wDN5v8QmLP/muX5/2ng - +v9D3Pj/L9v3/17P5P8CpcD/AMzk/wDM5P8AzOT/AMvj/wDL4/8Ay+P/E8/m/3Pp9/902ur8F5SvsAD/ - /wEAAAAAAAAAAAAAAAAAg6H/AIumwQCAoyQAi6ILB42q+0ri9v8x4fb/ANLr/wDS6/8A0ur/ANLq/wio - w/88s8v/XuH3/zXc+P8n2vf/YuP4/xWXsv8AzeX/ANDo/wDQ6P8A0Oj/CNHo/zLM4P87vdH/G5+4/zOm - vP90ydj7Lp+2lwAAAAAAAAAAAAAAAACHpPIAvNb/AImm3ACGpWkQp8L5ILHK/wDU7f8A1O3/ANTt/wDU - 7f8A1O3/ANLr/wOtyP8LlrH/QMri/1Hh+f9W4vn/TsTa/wGyzf8A1O3/AdLr/xXK4f8YxN3/AMni/wDT - 7P8A0+z/Csvj/xunwf82pbz5AIOkRgAAAAAAgIACAIim8QDU7f8Ah6T1AIin3z/i9/8Lw93/ANTt/wDU - 7f8A1O3/BtXt/wzW7v8Q1+7/E9fu/xPR5/8KpL//G6G7/0jF3P8SlbH/BrHN/wLU7f8A1O3/ANTt/wDU - 7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/yvA1/8Kj6ruAIaeFQCEojwAmLTtANLq/wCTsPUMnLj6P+X5/wDU - 7f8A1O3/CNXu/xDX7v8X2O//Hdnv/yHa7/8l2vD/Jtrw/yfb8P8gy+L/EKfB/xvQ5/8Y2O//Etfu/wrW - 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/x3B2/wAiqejAIqnwgC71v8A0ur/ANLr/yDP - 5/8Ag6H/B5az/wqduP8NoLz/EafB/xSrxP8q1er/M93x/zfd8f853vH/Od7x/zfd8f803fH/L9zw/ynb - 8P8h2u//GNjv/w7W7v8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u//QeX6/wCJp+4AlbHwANPs/wDS - 6v8A0uv/N+P4/w650/8LlrL/QNXr/0Tb7/9B1+z/Nszi/z3d7v9F4PL/SeDy/0vh8v9L4fL/SeDy/0Xg - 8v8/3/H/Qd/y/07h8v9M4fP/P9/y/x/a7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f9I5/v/AY6s7QDL - 5f8AzuX/AK3I/wC81v9A5fr/Ctbv/xHI3/8Ml7P/N8vh/0/p/P9K5Pf/T+Hz/1bj8/9b4/P/XeT0/13k - 9P9a4/P/VeLz/2rm9f9v2fX/Y7fu/1+w7f9ete3/Vc/y/znd8f8B1O3/ANTt/wDU7f8A1O3/AdXt/0jn - +v8AiaXvAM/n/wCVsvEAh6TCAIOh/zve8/8Mzuf/G9nv/zDX7P8VoLv/JbTM/1Tk9P9f5PT/Z+X0/2zm - 9f9v5/X/b+f1/2vm9f9y5/X/Z8jr/2io3/+0wsr/wcjI/73HzP93r93/R7np/yDa7/8A1O3/ANTt/wDU - 7f8R2PD/O9zx/wCKp58Aka3wAIimgQAAAAAAiaamD5i0/x2+1/8Z2O//PN7x/0rg8v8vuM//TM/i/27n - 9f936PX/fun2/4Lq9v+A6vb/fOn2/3TZ9P9EisL/y9HS/77Fxf+yo5X/oYBl/8rEvf9tq97/Jszs/wLU - 7f8A1O3/ANTt/yrf9f8lu9P2AIWfMACCoz0AjqoJB4ypkg+WsvRD2u7/Tun8/xfZ8P9E3/L/UuLz/2Dk - 9P9u5/X/e+n2/4br9v+P7Pf/k+33/5Lt9/+L6/f/Vbfq/0ag3/+5vr7/x8W//4pcOP8mFwz/mmxH/8ra - 5/8freD/A9Xt/wDU7f8B1O3/Reb5/wqUsOwAAAAAA4SiTQCDof8Unbj8NMbd/zXH3v81x97/JcPb/zzW - 6v9Y4/P/Z+X0/3Xo9f+E6vb/kez3/57v+P+m8Pn/oe/4/5Xt+P9UuOr/RJzY/0RRWf/b2dX/mm1J/8S9 - t/+7nIP/z93q/xOp3P8A1O3/ANTt/xTa8P873vL/AIqorAAAAAAChKJuA4immAaLp4AGi6eAA4mn8yW6 - 0v8HtND/H8/m/1rj8/9p5vT/eOj1/4fr9/+V7fj/pPD5/7Ly+v+n8Pn/mO74/3ba9v9Xr+n/N1Vo/3l+ - gP/r4Nf/zLWi/+zo5P9bm8//BMXm/wDU7f8A1O3/MuH2/x+80/YAg6BOAAAAAAAAAAAAAAAAAAAAAABc - cSQFepPxHMLZ/zDh9v8C0ur/QNPm/2fl9P926PX/g+r2/5Ds9/+b7vj/oO/4/5vu+P+Q7Pf/hOr2/1q2 - 5f9WgqD/OUxY/01UWf+Xmp3/YI61/xik1f8A1O3/ANTt/wvX8P9F5vn/BI2q5wCAgAIAAAAAAAAAAAAA - AAAAW20cAVxy+iGZrfwDi6j8PuP4/wyqxf8iuNH/W+X1/27n9f966fb/hOr2/4vr9/+O7Pf/i+v3/4Pq - 9v946PX/bOb1/1m73f9Og6T/JkRY/wsfK/8HKC//CIub/wDU7f8A1O3/OeP4/x+91vYAhaRiAAAAAAAA - AAAAAAAAAAAAAAFdccwDYHbfAl52bAB2lGYJmrb1GKK9/0/p/P9K6Pv/W+X2/23m9f916PX/eun2/3vp - 9v956Pb/c+f1/2vm9f9g5PT/VeLz/0HH1/87s8H/Qr7M/z+eqf8ol6T/CMPZ/yrf9f8z2u/+AIuqvwAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQiNq/JI4PT/T+n8/z/U6v8bq8X/Ja/H/2Xl - 9P9Z2On/aub0/2fl9P9i5fT/W+Pz/1Pi8/9I4PL/Pt3w/zLa7/8i0OT/LaGu/wDT7P9FxdT/O9/0/wON - qegAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjKm4M8Xa+y291v0NlbH+GqfB/xTG - 3/8D1e3/KNvw/xObtf9T5ff/VOLz/1Li8/9L4fL/Q9/y/zve8f8t3PD/Etfu/wHU7f8JtMn/Kcbe/yrB - 1v8CiqjoAICeIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIWlggCDof8IjqrfBoundwCA - mpwQnbj+O9/0/zvk+P8V2fH/CJi0/0/p/P9K5/r/M9vx/xzL4v8b2e//D9fu/wLU7f8M2PD/L+H2/0nn - +/8outH+AIek4gCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChqNnAIOfSAAA - AAAAAAAACWp+sjG70f8PnLf/HLLL/jfc8f8Rm7b/T+n8/0/p/P8cq8T/FbvU/yLd9P8v4fb/Qeb6/zjX - 7f8ludL9T+n8/zfL4f8HjKmMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAABVdw8Lb4LpRtns/xF6j+wEcIqvAIuosgmOq/1P6fz/N8vg/xGfuv4qz+X/J8jg/x26 - 0/8MmLX/AIej6geLquE1yN75Q9nu/wqQrboAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAABWJ5cBB3i/oIaH3hAFxyOgAAAAAAAAAACY+s3Eni9f8JjqvtAIOhdQB+ - m4AKe5TwOc3j/xeHm+sAW3EtAICZCgeMqNU4zOP9CI2q3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm//BmV6tABddAsAAAAAAAAAAAAAAAAJj6rcF6C77QOI - o0sAAAAAAAAAAAlpf8dF1+n/CWp/5wAAAAEAAAAAAIaeFQiNquEBhaP7AAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVcRIAAAAAAAAAAAAAAAAAAAAAAAAAAACD - of8IjKupAAAAAAAAAAAAAAAAA19yUxuKnvUJa4DEAAAAAAAAAAAAAAAAAISeHQCDof8AiJkPAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAoOjoQCAqgwAAAAAAAAAAAAAAAAAVYAGAFpv/wRidnUAAAAAAAAAAAAAAAAAAAAAAJmZBQAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/fPv//zzz//2AA//8AAP/vAAB/4wAAH/AAAA/4AAAHmAAAA4gA - AAOAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABwAAAAYAAAAGAAAAD8AAAA+AAAAfMAAAH/AAAD/gA - AB/xAAA//wAAP/8AAD//OIY//nnHP//557//++//KAAAABAAAAAgAAAAAQAgAAAAAAAABAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4yp0wuVqhgAcI8ZCnKI5QCAnwgA//8BCI2rtwCG - nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX3UjC2yAwRunw9khtc7vKLfQ+ia91f0nyeDxGarF8Ruo - wegAgJ8IAAAAAAAAAAAAAAAAAAAAAAeNqWsSmLSaAIOoIxmyy/IjyuD/K7bP/5/e8f9AutH/AMHY/w7J - 3f8gyeD/HLrU2gCMsCoAAAAAAAAAAAAAAAAAmZkFIa7H0ia40vgc0ej/AMvj/0++1f9r4Pr/Qcvj/wG/ - 2f8AyeH/AMnh/xbR5v9GzuHtFJuvMwAAAAAAlLCSAJu4qAyducYv0+v/ANTt/wDU7f8VttH/MMfg/03g - +P8ct9H/AdLr/wzM4/8cxt3/G7fP/z+2y/AAh6URAJu5pACuyegmyOHuBNLr/wTV7f8R1+7/Gtjv/xnK - 4v8hts7/ErrU/wfV7f8A1O3/ANTt/wDU7f8i1Oz/C6S8uwCtydwA0uv/F7XQ/xOmwf8mvtb/LMzh/z7e - 8f9C3/L/Pt7x/zbd8f803fH/G9nv/wDU7f8A1O3/Bdbu/yPG3uoAwNr1AJq21ibZ8f8cxd3/L8HZ/1Lj - 9P9h5PT/ZuX0/2Pl9P9wzev/fbvd/2+/4f8o0+7/ANTt/wfW7v8jw9vhAJWyexCatZ8sw9v/J9rw/0vY - 6f9p4vH/g+r2/4rr9/911vL/c6PF/6WYi/+NdWH/a77h/wHU7f8c2/L/EanHpwaKqaQapb/EJLfP+xrG - 3f9h5PT/f+n2/5zu+P+t8fn/e9rz/0Jzk/+mnpb/wbKm/2W42v8A1O3/KNXt+wCJqUEAAAAAEHiNYBGh - uvgbz+b/Ptbp/3jo9f+P7Pf/le34/4Tq9v9XoMP/O1Zo/zVVZv8IvNj/Edjw/x+/19cAAAAAAAAAAApq - gGoFdIs3HrHJ90nj+P81yN3/bOXz/3Hn9f9n5fT/VOLz/zzP4P9GxNP/J8XX/yfQ6PEAkbA6AAAAAAAA - AAAAAAAACpGsrB2pxNMWqcP3HtXs/xi40P9C5Pf/Ntrv/yfb8P8T2O//ItPq/x+40PIAmLZNAAAAAAAA - AAAAAAAAAAAAAACJoycAQIAEJJ6x1ROju+wbrsb2SeL1/x250f4n0+n/IsTa+Sa3z+4ks83FAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAABWZ8axJ6j7oAW20OFJ24jB+qw8wAhJw+Ip+13hqJn5oWmrdHD5ey4QAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVVQMAAAAAAAAAAAqProQPlLMyAAAAABJ7kbQMcodVAAAAAAiK - qGYAqqoDAAAAAAAAAAD7bwAA8A8AANAHAADAAwAAAAEAAAAAAAAAAAAAAAAAAIAAAAAAAQAAwAEAAOAD - AADABwAA8AcAAPSXAAD9vwAA - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACm + LAAAAk1TRnQBSQFMAgEBBAEAARQBAAEUAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg + AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A + /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ + AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABtgH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ + AoEBuAH/Al8BcAHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK + AwgBCwMHAQoDBwEJAwUBBwMBAQIgAAMPARQDDwEUAxIBGAMeASsDJgE5AygBPAMoATwDJgE4AyIBMQMY + ASIDEAEWAxABFQMRARcDSAGDAisBpAH/AisBpAH/AisBpAH/AisBpAH/AisBpAH/AisBpAH/AisBpAH/ + A0YBfggAAw8BFAMPARQDEgEYAx4BKwMmATkDKAE8AygBPAMmATgDIgExAxgBIgMQARYDEAEVAxEBFwMH + AQkDAgEDAwsBDwMWAR4DFgEfAwsBDwMBAQIQAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGz + A1YBtANVAbUDVQG1A1UBtQJbAV0ByANqAfkCgQHTAf8CgQHTAf8CgQHTAf8CgQHTAf8CgQHTAf8CgQHT + Af8CgQHTAf8CgQHUAf8CZAFyAfEEAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGzA1YBtANV + AbUDVQG1A1UBtQNVAbUDVgG0Az0BaAMOARMDAwEEHAABgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ + AYEBhAGCAf8BgQGEAYIB/wIrAaQB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIr + AcwB/wIrAaQB/wgAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ + AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DGQEj + AxQBGwM1AVYDYQHaAXoCewH6A10ByAMeASsQAAGoAasBqgH/AeQC5QH/AeEB5AHjAf8B3wLhAf8B3wHh + AeAB/wHfAuEB/wHfAuEB/wHfAuEB/wHfAuEB/wHhAeIB4QH/Ad4B4QHfAf8B4QHiAeEB/wGYAZoBzwH/ + AoEBzAH/AmkBzAH/AoEB0QH/AoEB0AH/AmkBzAH/AmkBzAH/AoEB1AH/Am0BzQH/AoEB0wH/AoEBuAH/ + BAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHgAf8B3wLhAf8B3wLhAf8B3wLhAf8B3wLh + Af8B4QHiAeEB/wHeAeEB3wH/AeEB4gHhAf8B4wHlAeQB/wHhAeMB4gH/A38B/gMYASIDBgEIHAABgQGE + AYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8CKwGk + Af8CKwHMCf8CKwHMCf8CKwHMAf8CKwGkAf8IAAGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/ + A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGEAYIB/wOBAf8DgQH/A4EB/wGcAp0B/wO2Af8BrAKtAf8DWgHA + EAADQAFuA2UB6gG6ArsB/wG2ArcB/wGtAq4B/wGqAasBqgH/AaoCqwH/AaoCqwH/AaoCqwH/A7gB/wGu + Aq8B/wHIAskB/wKBAaEB/wKBAcwB/wJpAcwB/wKBAd8B/wLBAfMB/wKBAdYB/wKBAd8B/wLNAfUB/wKB + AdMB/wKBAdMB/wKBAbgB/wQAA0ABbgNlAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGq + AqsB/wGqAqsB/wO4Af8BrgKvAf8ByALJAf8BnQGfAZ4B/wNlAe8DSQGIAyABLgMEAQUcAAGBAYQBggH/ + A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/A8UB/wPoAf8BgQGEAYIB/wIrAaQB/wIr + AcwV/wIrAcwB/wIrAaQB/wgAAYEBhAGCAf8D8wH/A/AB/wPRAf8D7AH/A+sB/wPrAf8D6wH/A8gB/wPF + Af8DxQH/A+gB/wGBAYQBggH/A5AB/wOzAf8DgQH/A7YB/wPrAf8D1QH/A2oB+RAAAwgBCwM4AV0DfwH+ + AZYBmgGYAf8BlQGZAZgB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGa + AZkB/wGYAZwBmwH/AoEBlAH/AoEBzAH/AmkBzAH/AnkBzwH/AogB5wH/AtoB+AH/As0B9QH/AoEB3wH/ + AmkBzAH/AoEB0wH/AoEBuAH/BAADCAELAzgBXQN/Af4BlgGaAZgB/wGVAZkBmAH/AZIBlQGUAf8BkgGV + AZQB/wGSAZUBlAH/AZIBlQGUAf8BkQGUAZMB/wGXAZoBmQH/AZgBnAGbAf8DYgHVAz8BbQNMAZIDRwGA + AwABARwAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9AisBpAH/AisBzAH/AisBzA3/ + AisBzAH/AisBzAH/AisBpAH/CAADTwGXAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNFAX0DgQH/ + A4EB/wOBAf8BowKkAf8D1QH/A9cB/wNZAb8UAAMBAQIDBAEFAZUBmQGYAf8BywHPAc4B/wHbAd0B3AH/ + AdgB2gHZAf8BywHPAc4B/wHAAcQBwwH/AbcBuwG6Af8DXAHNAYEBhAGCAf8CVAFWAasCgQHMAf8CaQHM + Af8CaQHMAf8CgQHfBf8C2gH4Af8CgQHWAf8CaQHMAf8CgQHTAf8CgQG4Af8IAAMBAQIDBAEFAZUBmQGY + Af8BywHPAc4B/wHbAd0B3AH/AdgB2gHZAf8BywHPAc4B/wHAAcQBwwH/AbcBuwG6Af8DXAHNAYEBhAGC + Af8DSgGJA1sBxANeAc4DGQEjIAABgQGEAYIB/wPWAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/ + A+0B/wPtAf8D1AH/AYEBhAGCAf8CKwGkAf8CKwHMFf8CKwHMAf8CKwGkAf8IAAGBAYQBggH/A9YB/wH3 + AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHr + Af8B9wHxAesB/wPUAf8BgQGEAYIB/wQAAwUBBwMmATgDgQH/A4EB/wOBAf8DGgElEAADHwEsA2IB1QGh + AaYBpAH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaEBpQGjAf8BpAGoAacB/wGlAaoBqAH/AaUBqgGo + Af8BpQGpAacB/wGlAakBpwH/AoEBpgH/AoEBzAH/AmkBzAH/AoEB2wH/As0B9QH/AoEB3wH/AocB5wH/ + AsEB8wH/AnsBzwH/AoEB0wH/AoEBuAH/BAADHwEsA2IB1QGhAaYBpAH/AaEBpQGjAf8BoQGlAaMB/wGh + AaUBowH/AaEBpQGjAf8BpAGoAacB/wGlAaoBqAH/AaUBqgGoAf8BpQGpAacB/wGlAakBpwH/AaABpQGj + Af8DWwHDAwcBCQMHAQoDBwEKAwcBCgMIAQsDBwEKAwcBCQMEAQYDAQECBAABgQGEAYIB/wPzAf8BgQFz + AWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/ + AYEBcwFhAf8D7QH/AYEBhAGCAf8CKwGkAf8CKwHMCf8CKwHMCf8CKwHMAf8CKwGkAf8IAAGBAYQBggH/ + AfoB9gHyAf8BpAGBAWcB/wGoAYEBbQH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFtAf8BqAGB + AW0B/wGoAYEBbQH/AaQBgQFnAf8B9wHxAesB/wGBAYQBggH/AwsBDwMMARADDAEQA4EB/wPTAf8DgQH/ + FAADNQFVAb8BwwHBAf8B1wHcAdkB/wHiAeYB5AH/AeIB5gHkAf8B4AHkAeIB/wHGAcsByAH/AcUBygHI + Af8B3QHiAd8B/wHfAeQB4QH/Ad8B4wHhAf8B3wHjAeEB/wGPAZIBxgH/AoEBzAH/AmkBzAH/AoEB1wH/ + AoEB2wH/AmkBzAH/AnkBzwH/AoEB4QH/An0B0AH/AoEB0wH/AoEBuAH/BAADNQFVAb8BwwHBAf8B1wHc + AdkB/wHiAeYB5AH/AeIB5gHkAf8B4AHkAeIB/wHGAcsByAH/AcUBygHIAf8B3QHiAd8B/wHfAeQB4QH/ + Ad8B4wHhAf8B3wHjAeEB/wHWAdoB2AH/AZwBoQGfAf8DVgG0A1YBtANVAbUDVQG1A1UBtQNVAbUDVgGz + AzEBTgMLAQ8EAAGBAYQBggH/A+4B/wGBAXMBYQH/AZMBgQF3Af8BkwGBAXcB/wGUAYEBdwH/AZMBgQF3 + Af8BkwGBAXcB/wGTAYEBeAH/AZQBgQF3Af8BgQFzAWEB/wPuAf8BgQGEAYIB/wIrAaQB/wIrAcwB/wIr + AcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAaQB/wgAAYEBhAGCAf8B+AHyAewB/wGo + AYEBbQH/AboBgQFxAf8BugGBAXEB/wG7AYEBcQH/AboBgQFxAf8BugGBAXEB/wG6AYEBcgH/AbsBgQFx + Af8BqAGBAW0B/wH4AfIB7AH/AYEBhAGCAf8DOAFeAzgBXgM2AVgDgQH/AccCyAH/A4EB/wMDAQQDBQEH + DAADNQFVAcIBxQHEAf8DUgGjA4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8BnQKBAf8BkwGW + AcoB/wKBAcwB/wJuAc0B/wJpAcwB/wJpAcwB/wJpAcwB/wJpAcwB/wJpAcwB/wJpAcwB/wKBAdMB/wJA + AbYB/QQAAzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/ + AdsB4AHeAf8BngGjAaEB/wHfAuEB/wHgAeIB4QH/Ad8B4gHgAf8B3gHhAd8B/wHiAeMB4gH/AeMB5QHj + Af8B4QHjAeIB/wNXAbgDFQEdBAABgQGEAYIB/wPuAf8BgQFzAWEB/wGrAYoBgQH/AZ0CgQH/AY8BgQF0 + Af8BkAGBAXQB/wGQAYEBdAH/AZABgQF0Af8BjwGBAXQB/wGBAXMBYQH/A+4B/wGBAYQBggH/AWsBbQGS + Af8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8DRgF+CAABgQGEAYIB/wH4 + AfIB7AH/AagBgQFtAf8B0AGeAYEB/wHEAYsBgQH/AbYBgQFvAf8BtwGBAW8B/wG3AYEBbwH/AbcBgQFv + Af8BtgGBAW8B/wGoAYEBbQH/AfgB8gHsAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AwEBAgwAAzUBVQO9Af0DgQH/AaACgQH/AZ8CgQH/ + AZoCgQH/AZgCgQH/AZcCgQH/AZgCgQH/AZgCgQH/AZgCgQH/A4EB/wG2AbkB1gH/AoEBuwH/AoEBzAH/ + AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/A18B+wNWAbMEAAM1AVUDvQH9A4EB/wGg + AoEB/wGfAoEB/wGaAoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8B3wHiAeEB/wGe + AaIBoAH/AaoCqwH/AbECsgH/Aa4CrwH/AbICswH/AboCuwH/AZwBngGdAf8DZgHnA0ABbgMWAR8EAAGB + AYQBggH/A+4B/wGBAXMBYQH/AakBhwGBAf8BqQGHAYEB/wGoAYcBgQH/AZoCgQH/AZMBgQGAAf8BiwGB + AXEB/wGMAYEBcQH/AYEBcwFhAf8D7wH/AYEBhAGCAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGE + AYIB/wMBAQIMAAGBAYQBggH/AfgB8gHsAf8BqAGBAW0B/wHOAZsBgQH/Ac4BmwGBAf8BzgGbAYEB/wHC + AYYBgQH/AboBgQF4Af8BsgGBAWwB/wG0AYEBbAH/AagBgQFtAf8B+AHzAe0B/wGBAYQBggH/A9kB/wPZ + Af8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8DAQECDAADNQFVA18B+wOBAf8BrAGLAYEB/wGrAYkBgQH/ + AaUCgQH/AZkCgQH/AZUCgQH/AZMCgQH/AZMCgQH/AZQCgQH/A4EB/wHaAt4B/wGCAYYBpQH/AoEBkgH/ + AoEBkQH/AoEBkQH/AoEBkQH/AoEBkQH/AlQBVgGrA0UBfAJXAVgBugM1AVYEAAM1AVUDXwH7A4EB/wGs + AYsBgQH/AasBiQGBAf8BpQKBAf8BmQKBAf8BlQKBAf8BkwKBAf8BkwKBAf8BlAKBAf8DgQH/Ad0B4QHf + Af8BnAGgAZ8B/wGSAZUBlAH/AZIBlQGUAf8BkAGTAZIB/wGbAZ4BnQH/AZQBlwGWAf8DVgGrAzsBZANS + AaEDKwFCBAABgQGEAYIB/wPwAf8BgQFzAWEB/wGmAYMBgQH/AaUBhAGBAf8BpgGEAYEB/wGlAYQBgQH/ + AaYBhAGBAf8BpgGEAYEB/wGQAYEBfAH/AYEBcwFhAf8D8AH/AYEBhAGCAf8D6wH/A+sB/wPIAf8DxQH/ + A8UB/wPoAf8BgQGEAYIB/xAAAYEBhAGCAf8B+AH0Ae4B/wGoAYEBbQH/AcwBlwGBAf8BywGYAYEB/wHM + AZgBgQH/AcsBmAGBAf8BzAGYAYEB/wHMAZgBgQH/AbcBgQF2Af8BqAGBAW0B/wH4AfQB7gH/AYEBhAGC + Af8D6wH/A+sB/wPIAf8DxQH/A8UB/wPoAf8BgQGEAYIB/xAAAzUBVQN9AfgDgQH/AasBiQGBAf8BqQGH + AYEB/wGpAYcBgQH/AagBhQGBAf8BmgKBAf8BkAKBAf8BkAKBAf8BkAKBAf8DgQH/Ab0BwQHAAf8BlwGb + AZkB/wHIAcwBywH/Ab4BwgHBAf8BtQG5AbgB/wNWAbMDYgHVA00BkQNfAdkDUAGbAw0BEQQAAzUBVQN9 + AfgDgQH/AasBiQGBAf8BqQGHAYEB/wGpAYcBgQH/AagBhQGBAf8BmgKBAf8BkAKBAf8BkAKBAf8BkAKB + Af8DgQH/Ab0BwQHAAf8BlwGbAZkB/wHIAcwBywH/Ab4BwgHBAf8BtQG5AbgB/wNWAbMDYgHVA00BkQNf + AdkDUAGbAw0BEQQAAYEBhAGCAf8D8wH/AYEBcwFhAf8BowKBAf8BowKBAf8BowKBAf8BowKBAf8BowKB + Af8BogKBAf8BowKBAf8BgQFzAWEB/wPwAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wNFAX0QAAGBAYQBggH/AfoB9gHyAf8BqAGBAW0B/wHJAZUBgQH/ + AckBlAGBAf8ByQGUAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AagBgQFtAf8B+AH0 + Ae4B/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ + A0UBfRAAAzUBVQNiAfYDgQH/AaoBiAGBAf8BqAGHAYEB/wGnAYYBgQH/AacBhAGBAf8BpgGCAYEB/wGZ + AoEB/wGPAoEB/wGMAoEB/wOBAf8B0wHWAdQB/wGZAZ4BnAH/AaUBqgGoAf8BpQGqAagB/wGlAaoBpwH/ + AaUBqQGnAf8BpQGpAacB/wGcAaABngH/A1QBpgwAAzUBVQNiAfYDgQH/AaoBiAGBAf8BqAGHAYEB/wGn + AYYBgQH/AacBhAGBAf8BpgGCAYEB/wGZAoEB/wGPAoEB/wGMAoEB/wOBAf8B0wHWAdQB/wGZAZ4BnAH/ + AaUBqgGoAf8BpQGqAagB/wGlAaoBpwH/AaUBqQGnAf8BpQGpAacB/wGcAaABngH/A1QBpgwAAYEBhAGC + Af8D7QH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFh + Af8BgQFzAWEB/wGBAXMBYQH/A/AB/wGBAYQBggH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D1AH/AYEBhAGC + Af8QAAGBAYQBggH/AfcB8QHrAf8BpAGBAWcB/wGoAYEBbQH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/ + AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AaQBgQFnAf8B+AH0Ae4B/wGBAYQBggH/AfcB8QHrAf8B9wHx + AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wPUAf8BgQGEAYIB/xAAAzUBVQNmAfQDgQH/AagBhgGB + Af8BpwGFAYEB/wGmAYQBgQH/AaUBggGBAf8BpAKBAf8BowKBAf8BnQKBAf8BlQKBAf8DgQH/AdMB1gHV + Af8BmQGeAZwB/wHFAcoByAH/Ad4B4gHgAf8B3wHjAeEB/wHfAeMB4QH/AdwB4AHeAf8B1gHZAdgB/wGB + AYQBggH/DAADNQFVA2YB9AOBAf8BqAGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGj + AoEB/wGdAoEB/wGVAoEB/wOBAf8B0wHWAdUB/wGZAZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/ + Ad8B4wHhAf8B3AHgAd4B/wHWAdkB2AH/AYEBhAGCAf8MAAGBAYQBggH/AccCyAH/A+0B/wPtAf8D7QH/ + A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/AdkC2gH/AYEBhAGCAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFh + Af8BgQFzAWEB/wGBAXMBYQH/A+0B/wGBAYQBggH/EAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/AfcB8QHr + Af8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wHZ + AtoB/wGBAYQBggH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFtAf8BpAGBAWcB/wH3AfEB6wH/ + AYEBhAGCAf8QAAM1AVUDagHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh + AoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/A4EB/wNZAcIB3AHf + Ad4B/wGBAYQBggH/DAADNQFVA2oB8AOBAf8BpwGFAYEB/wGlAYQBgQH/AaQBgwGBAf8BpAKBAf8BogKB + Af8BoQKBAf8BoAKBAf8BnwKBAf8DgQH/AcABwwHBAf8BlgGZAZcB/wOBAf8DgQH/A4EB/wOBAf8DWQHC + AdwB3wHeAf8BgQGEAYIB/wwAA1QBrAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BhwKBAf8BkwGB + AXcB/wGTAYEBdwH/AZMBgQF4Af8BlAGBAXcB/wGBAXMBYQH/A+4B/wGBAYQBggH/EAADVAGsAYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wG6AYEBcQH/AboBgQFxAf8BugGBAXIB/wG7AYEBcQH/ + AagBgQFtAf8B+AHyAewB/wGBAYQBggH/EAADNQFVA2YB9ANUAawBlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGbAZ0BmwH/AZcCgQH/AZgCgQH/ + AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/wwAAzUBVQNmAfQDVAGsAZYCgQH/AZYCgQH/ + AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZ0CgQH/AdQB1gHUAf8BmwGdAZsB/wGX + AoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGbAoEB/wHcAd8B3QH/AYEBhAGCAf8oAAGBAYQBggH/A+4B/wGB + AXMBYQH/AasBigGBAf8BnQKBAf8BjwGBAXQB/wGQAYEBdAH/AZABgQF0Af8BkAGBAXQB/wGPAYEBdAH/ + AYEBcwFhAf8D7gH/AYEBhAGCAf8sAAGBAYQBggH/AfgB8gHsAf8BqAGBAW0B/wHQAZ4BgQH/AcQBiwGB + Af8BtgGBAW8B/wG3AYEBbwH/AbcBgQFvAf8BtwGBAW8B/wG2AYEBbwH/AagBgQFtAf8B+AHyAewB/wGB + AYQBggH/EAADIQEwA18B2QGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe + AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGT + AoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO9Af0BgQGEAYIB/wwAAyEBMANfAdkBmgGeAZ0B/wGaAZ4BnQH/ + AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe + AZ0B/wGbAZ8BnQH/AYkBhwGBAf8BlAKBAf8BkwKBAf8BlAKBAf8BlAKBAf8BmwKBAf8DvQH9AYEBhAGC + Af8oAAGBAYQBggH/A+4B/wGBAXMBYQH/AakBhwGBAf8BqQGHAYEB/wGoAYcBgQH/AZoCgQH/AZMBgQGA + Af8BiwGBAXEB/wGMAYEBcQH/AYEBcwFhAf8D7wH/AYEBhAGCAf8sAAGBAYQBggH/AfgB8gHsAf8BqAGB + AW0B/wHOAZsBgQH/Ac4BmwGBAf8BzgGbAYEB/wHCAYYBgQH/AboBgQF4Af8BsgGBAWwB/wG0AYEBbAH/ + AagBgQFtAf8B+AHzAe0B/wGBAYQBggH/LAADUwGpA28B9QGJAoEB/wGqAYgBgQH/AakBhwGBAf8BqQGG + AYEB/wGoAYUBgQH/AZMCgQH/AZACgQH/AZACgQH/AZACgQH/AY4CgQH/A30B/AGBAYQBggH/KAADUwGp + A28B9QGJAoEB/wGqAYgBgQH/AakBhwGBAf8BqQGGAYEB/wGoAYUBgQH/AZMCgQH/AZACgQH/AZACgQH/ + AZACgQH/AY4CgQH/A30B/AGBAYQBggH/KAABgQGEAYIB/wPwAf8BgQFzAWEB/wGmAYMBgQH/AaUBhAGB + Af8BpgGEAYEB/wGlAYQBgQH/AaYBhAGBAf8BpgGEAYEB/wGQAYEBfAH/AYEBcwFhAf8D8AH/AYEBhAGC + Af8sAAGBAYQBggH/AfgB9AHuAf8BqAGBAW0B/wHMAZcBgQH/AcsBmAGBAf8BzAGYAYEB/wHLAZgBgQH/ + AcwBmAGBAf8BzAGYAYEB/wG3AYEBdgH/AagBgQFtAf8B+AH0Ae4B/wGBAYQBggH/LAADUwGpA3IB8QGJ + AoEB/wGpAYcBgQH/AagBhwGBAf8BpwGGAYEB/wGmAYQBgQH/AaYBggGBAf8BlgKBAf8BjgKBAf8BiwKB + Af8BlwKBAf8DagH5AYEBhAGCAf8oAANTAakDcgHxAYkCgQH/AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/ + AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGOAoEB/wGLAoEB/wGXAoEB/wNqAfkBgQGEAYIB/ygAAYEBhAGC + Af8D8wH/AYEBcwFhAf8BowKBAf8BowKBAf8BowKBAf8BowKBAf8BowKBAf8BogKBAf8BowKBAf8BgQFz + AWEB/wPwAf8BgQGEAYIB/ywAAYEBhAGCAf8B+gH2AfIB/wGoAYEBbQH/AckBlQGBAf8ByQGUAYEB/wHJ + AZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAW0B/wH4AfQB7gH/AYEBhAGC + Af8sAANTAakDbAHuAYgCgQH/AacBhgGBAf8BpwGFAYEB/wGmAYQBgQH/AaQBggGBAf8BpAKBAf8BogKB + Af8BmwKBAf8BkgKBAf8BlwKBAf8DbQH3AYEBhAGCAf8oAANTAakDbAHuAYgCgQH/AacBhgGBAf8BpwGF + AYEB/wGmAYQBgQH/AaQBggGBAf8BpAKBAf8BogKBAf8BmwKBAf8BkgKBAf8BlwKBAf8DbQH3AYEBhAGC + Af8oAAGBAYQBggH/A+0B/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFz + AWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wPwAf8BgQGEAYIB/ywAAYEBhAGCAf8B9wHxAesB/wGk + AYEBZwH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFt + Af8BpAGBAWcB/wH4AfQB7gH/AYEBhAGCAf8sAANTAakDZwHpAYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGk + AYIBgQH/AaMCgQH/AaICgQH/AaECgQH/AaACgQH/AZ4CgQH/AZACgQH/A2oB+QGBAYQBggH/KAADUwGp + A2cB6QGIAoEB/wGmAYQBgQH/AaUBhAGBAf8BpAGCAYEB/wGjAoEB/wGiAoEB/wGhAoEB/wGgAoEB/wGe + AoEB/wGQAoEB/wNqAfkBgQGEAYIB/ygAAYEBhAGCAf8BxwLIAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt + Af8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/ywAAYEBhAGCAf8BxwLIAf8B9wHxAesB/wH3AfEB6wH/ + AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B2QLa + Af8BgQGEAYIB/ywAA1MBqQNsAe4DXQHIAZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/ + AZYCgQH/AZYCgQH/A2AB1gNyAfEBgQGEAYIB/ygAA1MBqQNsAe4DXQHIAZYCgQH/AZYCgQH/AZYCgQH/ + AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/A2AB1gNyAfEBgQGEAYIB/ygAA1QBrAGBAYQBggH/ + AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8DTwGXLAADVAGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNP + AZcsAAM4AV4DfwH+AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ + AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8DXwH7A10ByCgAAzgBXgN/Af4BmgGeAZ0B/wGa + AZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGd + Af8BmgGeAZ0B/wNfAfsDXQHI/wCJAAFCAU0BPgcAAT4DAAEoAwABYAMAATADAAEBAQABAQUAAUABAhYA + A///ACIABv8BgAIAAYABAAF/AYABAAEBAYABAAEHAYACAAGAAQABPwGAAQABAQGAAQABBwGAAgABgAEA + AT8BgAEAAQEBgAEAAQcBgAIAAYABAAE/AYABAAEBAYABAAEHAYACAAGAAQABPwGAAQABAQGAAQABBwHA + AgABwAEAAX8BgAEAAQEBgAECAQcBgAIAAYACAAGAAQABAQGAAQABDwGAAgABgAIAAYABAAEBAYABAAED + AYACAAGAAgABgAEAAQEBgAEAAQMBgAIAAYACAAGAAQABAwGAAQABAwGAAgABgAIAAYABAAEHAYABAAEH + AYACAAGAAgABgAEAAQcBgAEAAQcBgAEAAQMBgAEAAQMBgAEAAQcBgAEAAQcBgAEAAQMBgAEAAQMBgAEA + AQcBgAEAAQcBgAEAAQMBgAEAAQMBgAEAAQcBgAEAAQcBgAEAAQMBgAEAAQMB/wEAAQcB/wEAAQcBgAEA + AQMBgAEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEA + AQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEA + AQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMM/ws= + + + + + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA3UAAAN1AHvkboVAAAAhElE + QVRIS2MYBaNgFAxXYJK65AwQ/ycRn4FqJwxAGg5cfvF/98Xn//cA8fZzz7DibWef/d96BkgDMUgPVDth + AFK85czT/1O33/4/Zdvt/5O23Po/YfOt/70bb/7vWn/zf9vam/+bV934X7/i+v/aZdf/z9/3gGQLaBtE + o2AUjIIhBRgYAJgulRaaJZA7AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6 + JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA3UAAAN1AHvkboVAAAA+0lE + QVRIS+2UTwsBQRjGF1/JV3IlH0AOykEpiZs/FyQlseweSJQTS9gDy0XEcvIRHvMybUIh40D7q2dnetv3 + eWZ22pFsbCzcnpzGBC6Nl8VBxg1tizoTzXlZHP8TIIsIYAbXB2qJVi8PLgEP9PrBU0NH36M5MdHiauuH + s3mtv0VzbEId7aAMd2xXbGSiHt7+HHqZVptUl0goS8TrC8TkBaJVA5GKgXDZQKg0R7A4Q6AwQ7a9ejvg + 4SfKddfwZ6Z3da7P/g0yyV8F8LI4yLShmfClvxkwMOFNiQ9w0YNM9dURpd7mNsDBx89gpl+97GiVTiba + DYnmNj+BJJ0AUzYC0bmj8Z8AAAAASUVORK5CYII= + + + + 116, 17 + + + 246, 17 + + + 353, 17 + + + + AAABAAUAAAAAAAEAIAAoIAQAVgAAAICAAAABACAAKAgBAH4gBAAwMAAAAQAgAKglAACmKAUAICAAAAEA + IACoEAAATk4FABAQAAABACAAaAQAAPZeBQAoAAAAAAEAAAACAAABACAAAAAAAAAABAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDovAAg6H/AIOh/wCDof8Ag6H/AIOhwQCA + mQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbiwAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1sIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AgqK0AICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+TAFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBablgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8IAICeIgCG + oRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JKAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoaYAqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYKAFpv7wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm+OAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJaAIOh5gCD + of8Ag6H5AIOijgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfIACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AISglwD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtwYgBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvxQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6F/AIOh/gCD + of8Ag6H/AIOh/wCDof8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + bskAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/YAQIAEAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6GgAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOhsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiygCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEongAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABY + bTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpuMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACImQ8AhKG8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oaAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW2+YAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Q9Pn/wRhdv8AWm//AFpv/wBab/8AWm//AFpv/wBa + cGkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoh4Ag6HTAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6JzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJ2AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAgqJaAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVWoMAFpv8wBab/8AWm//AFpv/wBab/8AWm//FYGV/0/p/P8Uf5P/AFpv/wBab/8AWm//AFpv/wBa + b/8AWm+fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoTEAg6HlAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIWgSwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCE + ok0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtuaABab/8AWm//AFpv/wBab/8AWm//AFpv/za7z/9P6fz/JZyx/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv1wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoUkAg6HzAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoiEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H1AIOfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABab88AWm//AFpv/wBab/8AWm//AFpv/whoff9O5vr/T+n8/za8z/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/0AVXcPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoWcAg6H8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoZ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAA//8BAIOh9ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDovAAhKI0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFpv/wBab/8mn7T/T+n8/0/p/P9G2ez/AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpxRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCDoYgAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKFXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDocsAg6H/AIOh/wCDof8Ag6H/AIOh/wKFo/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh6gCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+eAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Rtnr/0/p/P9P6fz/T+n8/wlq + f/8AWm//AFpv/wBab/8AWm//AFpv/wBab3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDoagAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AICcEgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KhAIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/JrPN/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAYHAQAFpv9QBab/8AWm//AFpv/wBab/8AWm//F4WY/0/p/P9P6fz/T+n8/0/p + /P8Zh5z/AFpv/wBab/8AWm//AFpv/wBab/8AWW+xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICcEgCDoMIAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhyAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdwCDof8Ag6H/AIOh/wCDof8Ag6H/C5Kv/0/p + /P8hrcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhaYXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFlubQBab/8AWm//AFxx/wBedf8AYXf/AGR7/yeuxP81x97/M8bc/y/A + 1/8vwNf/GJev/wBqg/8AaoP/AGqD/wBqg/8AaIH/AGmA7wCDoUQAhKE2AIShGwCAvwQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgIwCDodgAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoYIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoUwAg6H/AIOh/wCDof8Ag6H/AIOh/wGE + ov9N5vn/T+n8/xynwv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKEbAIKiPwCDomMAgqGFAIOioQB5lfQAfJj/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIOh5gCD + ocwAgqCyAISglwCEon4Ag6BhAISiNACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiNwCDoekAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x6qxP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqA7AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/QNbs/0/p/P9N5/r/GKK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDn0AAg6FvAIShnQCD + ocwAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIKh1wCDoakAg6B5AIWgSwCAnRoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShUQCDofYAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/BImm/zrN5P8mtM3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAKqqAwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCDofQAg6H/AIOh/wCD + of8Ag6H/AIOh/zPF3P9P6fz/T+n8/0zl+P8Unbj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + obsAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKgKwCCoWQAhKGdAIOh1gCDof4Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh3ACD + oZoAgqJYAICiFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCDofwAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/CY+r/0HX7P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKirAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAF10FgBbcGIAWm5dAGBwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6HMAIOh/wCD + of8Ag6H/AIOh/wCDof8mtM3/T+n8/0/p/P9P6fz/SuP2/xGZtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKhrgCqqgMAAAAAAICkHACComAAg6KkAIOh5wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof4Ag6LSAIOhkACFoEsAkpIHAAAAAAAAAAAAqqoDAIOhkACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/D5ax/0fe8v9P6fz/SeH1/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + omUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFttRgBab/UAWm//AFpv/wBab+8AW3BrAICAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhogCD + of8Ag6H/AIOh/wCDof8Ag6H/GaO9/0/p/P9P6fz/T+n8/0/p/P9I4fT/DpSw/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HQAISh0ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACDoqEAhKFRAIKhsACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/FqC7/0vk9/9P6fz/T+n8/zPF3P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AgKIeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBab+gAWm//AFpv/wBab/8AWm//AFpv/wBab8sAXHAyAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + ongAg6H/AIOh/wCDof8Ag6H/AIOh/wySr/9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8v8Lka7/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/H6vF/07n+/9P6fz/T+n8/0/p/P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCD + of8AgqHXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXbSMAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+wBa + cJAAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJNAIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/Tef5/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wiO + q/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6L/AImn/wCOrP8Ak7D/AJi0/wCZtf8AmbX/AJ26/wCeuv8Anrr/AJ66/wCeuv8Anbn/AJm1/wCV + sv8Akq//AI2q/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0/p/P9P6fz/T+n8/0/p/P9P6fz/CI6r/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFlv5ABbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICeIgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/0HX7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9B1+z/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCLqf8Al7T/AKK+/wCuyf8At9H/AL/a/wTJ + 4v8H0uv/C9fv/w3X7/8Q2fD/FNrw/xXZ8f8V2fH/Fdnx/xHY8P8Q2fD/ENnw/xDZ8P8L1/D/B9bu/wLV + 7f8A1O3/ANTt/wDU7f8A1O3/AM7m/wDG3/8AvNf/ALXQ/wCsyP8AoL3/AJKv/wCEo/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ChaT/M8Xc/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab6gAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFtvtABdbCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACAgAIAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8zxtz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/z7T6f8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhaP/AJWy/wCkwP8Ft9H/EMri/xvZ7/8m3vX/MOH2/zfj+P8+5fn/Ruf6/0zo + +/9P6fz/T+n8/0/p/P9O6Pv/RNru/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/0fn+v9B5vr/OOL4/zDh9v8n3vX/Hdzz/xTZ8f8J1u//ANTt/wDU7f8A0uv/AMXf/wC2 + 0f8AqMP/AJm1/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Fiqf/O8/m/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yu60v8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDofgAjqoJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWG0xAFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/2AFlveABVgAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOgzQCDof8Ag6H/AIOh/wCDof8Ag6H/JrTN/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/O8/l/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ai6j/AaC9/w261P8d0Oj/K9/0/zvk+P9J6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/SN/z/zDA1v8aorv/Boei/wGAnP9E2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8/5fn/MuH2/yXe + 9f8W2vH/BtXu/wDU7f8A1Oz/AMPd/wCuyf8AmbX/AIak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8KkK3/Q9nt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Vnrn/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb7cAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/UAFlvPAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaMAg6H/AIOh/wCDof8Ag6H/AIOh/xmjvv9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83yuD/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSP + rP8n1+7/O+T4/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DV + 6/8pts3/EZaw/wGAnP8Af5v/AH+b/wB/m/8Af5v/Iq3F/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9E5vn/M+L3/yLd9P8Q2PD/AdXt/wDR6f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8RmLT/SOD0/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/AoWk/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AISgdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW0/AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/4AWm+cAF5rEwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Mk6//T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLE2/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8ChaP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I4PP/KrjP/wuO + qf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWGof9L5Pf/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fi9/8Pnrr/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Yor3/TOX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OMvh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b8YAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b+sAW25fAAAAAQAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh2ACDof8Ag6H/AIOh/wCDof8Ag6H/AYWi/03n + +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Lb3V/wCDof8Ag6H/AIOh/wCD + of8Ag6H/D5ay/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+j8/zvP5P8bo73/AoKe/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/LbvS/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8Pl7L/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ir8j/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKv + yf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofEAkrYHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWm5PAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab8EAV3ApAAAAAACEojQAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of9B1+z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puM//AIOh/wCD + of8Ag6H/AoWj/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N8ne/w6SrP8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wuOqf9O6Pv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl+P8ChaP/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wGEov8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8Mk6//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAQBab9UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AGB1+gB6l9UAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/NMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7U + 6f8ltM3/J7XO/0Ta7v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P87zuT/Epew/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/D4eh/xOJov8Af5v/AH+b/wB/m/8Af5v/NcTa/03k + 9/9N5Pf/TeT3/07l+P9O5vn/Tuf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8yxNv/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wOGpP82yN//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9F3fH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOivwCCoTEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm9eAFpv/wBab/8AWm//AFpv/wBab/8AWm//IJSo/x2PpP8AW3D/AFpv/wBa + b/8AWm//AFpv/wBab/8AXHH/AG6I/wCBn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/ya0zv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8f8Xnrf/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wKAnP8kkan/Tqa6/2ezw/9Mpbn/AH+b/wB/m/8Af5v/AH+b/wGQ + qv8Fwtj/BcLY/wXC2P8Jwtn/DcTZ/xHF2f8Wxdr/Gsjd/x7K3/8nzuP/L9Po/znY7P9B3fD/S+L0/03k + 9/9P6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KLfP/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/weLqf8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/L8DX/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIShnQCF + phcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAECABABab+EAWm//AFpv/wBab/8AWm//AFpv/wVjeP9L4vX/O8XZ/wxx + hv8AWm//AFpv/wBab/8AZ3//AH+b/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ZpL7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLB2P8FhqH/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/CoSf/zSZr/94v9D/oNbl/7zp9/+Y0uH/Z7PD/w6GoP8Af5v/AH+b/wB/ + m/8Af5v/ALPM/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8Iwtn/FMXa/yDM4f8s0uf/ONns/0fh8/9O5/r/T+n8/0/p/P9P6fz/T+n8/znM4/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuSr/9E2+//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xmkvv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HtAIShXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW5tAFpv/wBab/8AWm//AFpv/wBab/8AWm//K6e7/0/p + /P9N5vn/KaS3/wNieP8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/DJOw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SN/z/xefuP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8MhaD/SKS5/5HO3v+55/X/yPD+/8jw/v/I8P7/wOv6/2u1xv83m7H/AH+b/wB/ + m/8Af5v/AH+b/wCYsv8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/CsPZ/xrJ3v8p0+f/O9vv/0vk9/9P6fz/IK3G/wCD + of8Ag6H/AIOh/xWeuf9J4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07n+/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhKG4AICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFVxCQBab+sAWm//AFpv/wBab/8AWm//AFpv/who + ff9N5vn/T+n8/zDC2f8EiKb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGFov9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/854/j/G8Xc/wGHov8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wiDnv9Pqr//pNrp/8Xu/f/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v+Ny9v/X6/A/wKA + nP8Af5v/AH+b/wB/m/8Agp7/ALvS/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8CwNf/Esfb/ybS + 5v8uyeD/Lr7W/zvQ5f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P880ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAg6FtAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//K6zA/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCF + o/8Prcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Qdfs/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9K5/v/M+L3/xrZ8P8EzeX/AKzF/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/zqetv+g2Of/x/D+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/t+X0/2ez + w/8kkan/AH+b/wB/m/8Af5v/AH+b/wChuf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wTB2P8Vy+D/Ldbr/0bk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/J7XO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wCIpv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq + qgMAgqCBAIOh5gCDoewAg6CnAIakKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBwEABab/MAWm//AFpv/wBa + b/8AWm//AGd//wGCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wql + wf823/T/Tuj7/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/zTH3f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/zPi9/8W2vH/AtDn/wDH3v8Av9f/AKC6/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/GY2n/4/P4f/F7/3/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw + /v9/wtL/Taa5/wB/m/8Af5v/AH+b/wB/m/8AhqH/AL7W/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/EMje/yjV6v9B4/b/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xijvv8s3fL/B6rG/wCE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh8QCEol0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqKDAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6KhAIOiIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBa + b/8AXHL/AHSO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5Ku/yfT + 6v9M6Pv/T+n8/0/p/P8PlrL/AIOh/wCDof8Ag6H/AIOh/wCDof8ntc7/T+n8/0/p/P9P6fz/T+n8/0/p + /P895Pn/INzz/wXV7f8Ay+P/AMPZ/wDA1/8AwNf/AJq0/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/S6vB/7vp+P/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw + /v/I8P7/qt3s/2ezw/8Qh6H/AH+b/wB/m/8Af5v/AH+b/wCowf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/w7J3v8q2Oz/R+b5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8vwNf/T+n8/0vo + +/8n1+7/BJi1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOipACA + pA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOh5ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKGTAIWjGQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhsGgBa + b/kAYXf/AHyZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaT/GL/Y/0Tm + +f9P6fz/T+n8/0/p/P9P6fz/HKfB/wCDof8Ag6H/AIOh/wCDof8Ag6H/GqS//0/p/P9P6fz/TOj7/y7g + 9f8M2PD/ANLr/wDJ4f8Awdj/AMDX/wDA1/8AwNf/AKK7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8IhKD/hczg/8bw/v/G8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw + /v/I8P7/yPD+/8Xv/f90u8z/OZyx/wB/m/8Af5v/AH+b/wB/m/8AjKb/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHD + 2f8VzuT/Mt7z/03p+/9P6fz/T+n8/0/p/P9P6fz/DZSw/wCDof8Ag6H/AIOh/wCDof8ChaT/Rt3y/0/p + /P9P6fz/T+n8/0jn+v8hyOD/Aoim/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKLdAIKiNwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAP//AQCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgqGHAICfEAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAbYa/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Gm7j/Nd3z/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKvyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xGZtf9G5vr/Jd70/wXW + 7v8A0uv/AMnh/wDC2f8Awdj/AMHY/wDB2P8Awdj/AKnC/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Ui6b/otzu/8Xv/v/F8P7/xvD+/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8Hu/v+u6f7/oub+/8Du + /v/I8P7/yPD+/8Xw/v/D8P7/mtXl/1+vwP8DgZz/AH+b/wB/m/8Af5v/AH+b/wCwyP8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8Gxdz/JNfs/0Xm+v9P6fz/T+n8/zPF2/8Ag6H/AIOh/wCDof8Ag6H/KbjQ/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8XsMn/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDofsAg6F1AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6GpAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oe8Ag6B5AIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq + qgMAg6GQAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCGpP8Xwtv/SOf6/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puND/AIOh/wCDof8Ag6H/AIOh/wCDof8Lor7/AtTt/wDT + 7P8AyuL/AMPZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/ALXN/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8gk63/reP1/8Pv/v/E7/7/xe/+/8bw/v/G8P7/x/D+/8jw/v/I8P7/xO/+/7Dq/v+i5v7/oub+/6Lm + /v+x6v7/yPD+/8Xw/v/C8P7/wO/+/7ns+v9stsf/JZKq/wB/m/8Af5v/AH+b/wB/m/8Ala//AMHY/wDB + 2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Bw9n/F9Hp/zni9/9P6fz/N8rg/xihvP8Vn7n/Lr7W/07n + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfZ7v8KlLH/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoagAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIafKACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoecAg6FtAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA + nxAAhKG4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSZtf8w3fL/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/w+Xsv8Ag6H/AIOh/wCDof8Ag6H/ALrW/wDM + 5P8Aw9n/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMDX/wCKpf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8dkKv/suX3/8Lv/v/D7/7/xO/+/8Tv/v/F7/7/xvD+/8bw/v/H8P7/uOz+/6Tn/v+i5v7/oub+/6Lm + /v+i5v7/pOb+/8Lv/v/C8P7/v+/+/7zv/f+57v3/i87e/0+nuv8Af5v/AH+b/wB/m/8Af5v/AICc/wC5 + 0f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Awtn/Ds3j/zTg9f9O6Pz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/ye/2P8ChqT/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QCDoCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKGTAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEod8AhKFfAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + nyUAg6HYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/w+20P9E5vn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3vL/D6S//wCJp/8Aj63/ALTO/wDF + 3P8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wCdtv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8QiaT/qOL0/8Hu/v/B7v7/wu/+/8Pv/v/E7/7/xe/+/8Xw/v/C7/7/q+j+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oub+/53l/f+x6/3/v+/+/7zv/f+57v3/tu79/6nm9f9os8T/EYii/wB/m/8Af5v/AH+b/wB/ + m/8Anbf/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awtn/Cs3l/zHf + 9P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qt/z/xCZ + tP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HuAIOfSAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICmFACDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoNUAg6JSAP//AQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + ojcAg6HqAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIuo/x3P5/9M6Pv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P864/j/Ddfw/wDU7f8A0uv/AMnh/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wC40P8AgJz/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Gg5//mNjs/8Du/v/A7v7/we7+/8Lv/v/C7/7/w+/+/8Tv/v+97f7/pef+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oeX9/5zl/f+X5f3/m+b9/7zv/f+57v3/tu79/7Pu/f+x7v3/fcXV/zudsv8Af5v/AH+b/wB/ + m/8Af5v/AIOf/wC/1v8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awtn/DM3l/zfh9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9O6Pv/Kb3V/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAhKJ4AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6F1AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDosoAg6FEAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oUwAg6H0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Apq2/y/d8/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bn+v8a2/L/ANTt/wDU7f8Azub/AMXc/wDE + 2/8AxNv/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8AlbD/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/bcDY/77u/v+/7v7/wO7+/8Hu/v/B7v7/wu/+/8Pv/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oeX9/5zl/f+X5P3/k+T9/47k/P+u7P3/tu79/7Pu/f+w7v3/re39/5rf7v9hsMH/BIGd/wB/ + m/8Af5v/AH+b/wB/m/8Apr7/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Aw9n/EdDm/zzj+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9B3PH/D5ey/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oZoAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDod4Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ob0AhJ84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oGEAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/B7DK/z7l+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDS6/8AyeH/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8Ats//AICb/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/MZ64/7rs/v++7f7/vu7+/7/u/v/A7v7/we7+/8Hu/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43k/P+J4/z/l+f8/7Pu/f+w7v3/re39/6rt/f+n7fz/c77O/yeT + qv8Af5v/AH+b/wB/m/8Af5v/AImk/wDC2f8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8AxNv/GtTq/0fm+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o+/8isMr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIShuACJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqBWAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDobEAhaIsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oWoAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhqT/DMHa/0Xn+v9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOf6/xja8v8A1O3/ANTt/wDQ6P8Ax97/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AJOu/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/CoWh/6Lg9v+87f7/ve3+/77t/v+/7v7/wO7+/8Du/v+87f7/pOb+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43j/P+I4/z/g+P8/4Di/P+t7f3/re39/6rt/f+n7fz/pO38/4zX + 5/9Qp7r/AH+b/wB/m/8Af5v/AH+b/wB/m/8Arsb/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Ex97/Ktvw/03p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbL4f8Fiqf/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HQAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD + ocUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ru9L/Iq/J/wKFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCDoaMAgJ4iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + onMAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/Eszk/0no+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/NeL3/wbV7v8A1O3/ANTt/wDM5P8Axt3/AMbd/wDG + 3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/ALnR/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/1i3z/+67P7/vO3+/73t/v+97f7/vu7+/7/u/v++7v7/puf+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/n+X9/5rl/f+V5P3/keT9/4zj/P+I4/z/g+P8/37i+/954vv/lej8/6rt/f+n7fz/pO38/6Hs + /P+d6/v/bLnJ/xOJov8Af5v/AH+b/wB/m/8Af5v/AJOt/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8MzOT/O+L2/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rd7y/xGZtf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeMAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhJ84AIOh/gCDof8Ag6H/AIOh/wCDof8Ag6H/CY+r/0zl+P9G3fH/HqvE/wGFov8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDopYAhaMZAAAAAAAAAAAAAAAAAAAAAACD + oXUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak6//GNXt/0zo+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6fz/Id3z/wDU7f8A1O3/ANLr/wDJ4f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wCguf8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wyHov+o5fv/uu3+/7zt/v+87f7/ve3+/77t/v++7v7/run+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/n+X9/5rl/f+V5P3/kOT9/4zj/P+H4/z/guL8/33i+/954vv/dOH7/3rj+/+n7fz/pO38/6Hs + /P+e7Pz/m+v7/4HQ4P89nrP/AH+b/wB/m/8Af5v/AH+b/wCAm/8AutL/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDE + 2/8g1er/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/H6vF/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACEojwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoagAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8puM//T+n8/0/p/P9D2e3/GqW//wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9gCCoIkAgJwSAAAAAACD + oW0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/INrx/07o/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/Edjw/wDU7f8A1O3/ANHp/wDI4P8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDG3f8Ah6L/AH+b/wB/ + m/8Af5v/AH+b/wB/m/9Eq8X/tuv+/7rs/v+77f7/vO3+/7zt/v+97f7/uez+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/nuX9/5nl/f+V5P3/kOT8/4vj/P+H4/z/guL8/33i+/944fv/dOH7/2/h+/9q4Pr/ler8/6Hs + /P+e7Pz/m+v7/5jr+/+R5ff/YrHC/wWCnf8Af5v/AH+b/wB/m/8Af5v/AJu1/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wnK4P863/X/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8tvdX/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIOgRgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgJ8gAIOh9wCDof8Ag6H/AIOh/wCDof8Ag6H/BIil/0jf8/9P6fz/T+n8/0/p + /P8/1Or/FqC6/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wCE + oa4Ag6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AoLz/Jd3z/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P875Pj/B9bu/wDU7f8A1O3/AM/n/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Atc7/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/h9Pr/7ns/v+57P7/uu3+/7vt/v+87f7/ve3+/6vp/v+i5v7/oub+/6Lm + /v+i5v7/nuX9/5nl/f+U5P3/j+T8/4vj/P+G4/z/geL8/33i+/944fv/c+H7/2/h+/9q4Pr/ZeD6/3fk + +/+d7Pz/m+v7/5jr+/+V6/v/k+v7/3bH2P8plKv/AH+b/wB/m/8Af5v/AH+b/wCCnv8AwNj/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AcTb/yHU6f9N6Pr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/znN4/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKFRAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoIkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8fqsT/T+n8/0/p + /P9P6fz/T+n8/0/p/P880Ob/Epu2/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ApsH/Jd70/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u4PX/AdXt/wDU7f8A1O3/AM3l/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AKW+/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/GY+r/63p/v+47P7/uez+/7rs/v+67f7/vO3+/7rt/v+j5v7/oub+/6Lm + /v+i5v7/neX9/5jl/f+U5P3/j+T8/4rj/P+G4/z/geL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/2Df + +v9d3/n/lur7/5jr+/+V6/v/kuv7/4/q+/+F3/D/Uqi7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AKS9/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/Dcrg/0Lh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Qtru/wqQrf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACE + ol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQOAIOh6gCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0HX + 7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83y+H/D5ax/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqMT/Jd71/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A0+z/AMzk/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCU + rv8Af5v/AH+b/wB/m/8Af5v/AH+b/0Oqxf+y6v7/uOz+/7js/v+57P7/uuz+/7vt/v+y6v7/oub+/6Lm + /v+i5v7/neX9/5jl/f+T5P3/juT8/4rj/P+F4/z/gOL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/1/f + +v9a3/n/Vt75/3jl+v+U6/v/kuv7/4/q+/+M6vv/iur7/2/A0f8ViqP/AH+b/wB/m/8Af5v/AH+b/wCH + ov8Axd3/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Ex97/Mdnt/0/o+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9H4PT/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWoAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Vnrn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8zxtz/C5Ku/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqsX/Jt71/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8d3PL/ANTt/wDU7f8A0+z/AMzk/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AhJ//AH+b/wB/m/8Af5v/AH+b/wB/m/9rw9z/tuv+/7fs/v+47P7/uez+/7ns/v+67P7/q+j+/6Lm + /v+h5f3/nOX9/5fl/f+T5P3/juT8/4nj/P+F4/z/gOL8/3vi+/924fv/ceH7/23g+/9o4Pr/ZN/6/1/f + +v9a3/n/Vt75/1He+f9X3/n/kev7/47q+/+M6vv/ier7/4bq+v972On/Pp6z/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AK3H/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8c0eb/S+T3/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9/8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAIOh1gCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/zjM4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8vwNf/CY+r/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CJWy/znN4/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8V2fH/ANTt/wDU7f8A0+z/AMvj/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AxNz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/lNzz/7br/v+37P7/uOz+/7js/v+57P7/uuz+/6Xn + /v+h5f3/nOX9/5fk/f+S5P3/jeT8/4nj/P+E4/z/f+L8/3vi+/924fv/ceH7/23g+v9o4Pr/Y9/6/17f + +v9Z3vn/Vd75/1De+f9L3fn/R934/3vn+v+L6vv/ier7/4bq+v+D6fr/gOj6/2a4yf8Ggp3/AH+b/wB/ + m/8Af5v/AH+b/wCPqv8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/w7K + 4f9E3/L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/xegu/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDoWcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Nk7D/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zm + +f8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/FJ23/z3S + 6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fn+v8P2O//ANTt/wDU7f8A0uv/AMvj/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/ALzU/wB/m/8Af5v/AH+b/wB/m/8Af5v/CIWg/6fn/v+16/7/tuv+/7fs/v+47P7/uOz+/7js + /v+g5f3/m+X9/5bk/f+S5P3/jeP8/4jj/P+E4/z/f+L8/3ri+/914fv/cOH7/2zg+v9n4Pr/Y9/6/17f + +v9Z3vn/VN75/1De+f9L3fn/Rt34/0Hc+P9Y4fn/iOr7/4Xq+v+D6fr/gOn6/37p+v900uL/KpSr/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/ALjR/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//CMnh/zvb7v9O5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/GaO+/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOiYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0vj9/8nts//BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/GKG8/0Xc8P9P6fz/T+n8/0fn+v8M2PD/ANTt/wDU7f8A0uv/AMzk/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wC0zf8Af5v/AH+b/wB/m/8Af5v/AH+b/xuRrP+n5/7/tev+/7br/v+26/7/t+z+/7js + /v+z6v3/m+X9/5bk/f+R5P3/jOP8/4jj/P+D4/z/fuL8/3ri+/914fv/cOH7/2zg+v9n4Pr/Yt/6/13f + +v9Y3vn/VN75/0/d+f9L3fn/Rt34/0Hc+P883Pj/Otv3/3/o+v+C6fr/f+n6/37p+v9+6fr/feX3/1Ws + v/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCYs/8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8CyOD/MNbp/07l+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o + +v8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4Ag6BhAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF + oS4Ag6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Hi6n/S+P3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nh9f8jsMr/Aoak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Jjqz/Rdzw/0fn+v8M2PD/ANTt/wDU7f8A0+z/AMzk/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8As83/AH+b/wB/m/8Af5v/AH+b/wB/m/8wnrj/qOj+/7Tr/v+16/7/tuv+/7fs + /v+16/3/q+n9/5Xk/f+R5P3/jOP8/4fj/P+D4/z/fuL7/3ni+/904fv/cOH7/2vg+v9m4Pr/Yt/6/13f + +v9Y3vn/U975/0/d+f9K3fn/Rd34/0Dc+P883Pj/N9v3/zPb9/9b4vn/f+n6/37p+v9+6fr/fun6/37p + +v9xytv/F4uk/wB/m/8Af5v/AH+b/wB/m/8AgJ3/AMDZ/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8l0uj/TeP1/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Tef6/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACEoFsAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIShnQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8fq8X/AYWi/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xilv/8M1/D/ANTt/wDU7f8A0+z/AM3l/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/ALDK/wB/m/8Af5v/AH+b/wB/m/8Af5v/OaO+/6jo/v+06/7/tev+/7Xr + /v+z6/3/sev9/6fp/f+Q5P3/i+P8/4fj/P+C4vz/feL7/3ni+/904fv/b+H7/2rg+v9m4Pr/Yd/6/1zf + +v9Y3vn/U975/07d+f9K3fj/Rdz4/0Dc+P873Pj/Ntv3/zLb9/8t2vf/NNz3/37p+v9+6fr/fun6/37p + +v9+6fr/e+Hy/0Cgtf8Af5v/AH+b/wB/m/8Af5v/AH+b/wChu/8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/R5v9L4PL/T+j7/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9M5vr/FJ23/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIOgTgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACFoxkAg6HzAIOh/wCDof8Ag6H/AIOh/wCDof8ChqT/Rd3x/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pa7v8bpcD/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ckq//ANTt/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wCxy/8Af5v/AH+b/wB/m/8Af5v/AH+b/zmjvv+o6P7/s+v+/7Tr + /v+x6v3/r+v9/6zq/f+k6f3/i+P8/4bj/P+C4vz/feL7/3jh+/9z4fv/b+H7/2rg+v9l4Pr/Yd/6/1zf + +v9X3vn/Ut75/07d+f9J3fj/RNz4/z/c+P872/j/Ntv3/zLb9/8t2vf/KNr3/yfa9/9n5fn/fun6/37p + +v9+6fr/fun6/37p+v9qwtT/BoKd/wB/m/8Af5v/AH+b/wB/m/8AhJ//AMff/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/GtDm/0ne8P9O5vn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rl+P8RmbT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofUAhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISifgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xumwP9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DW + 6/8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKK+/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8Assv/AH+b/wB/m/8Af5v/AH+b/wB/m/8rmrb/p+f+/7Pr + /v+v6v3/rer9/6rq/f+n6v3/oOj9/4bj/P+B4vz/feL7/3jh+/9z4fv/buD7/2ng+v9l4Pr/YN/6/1vf + +v9X3vn/Ut75/03d+f9J3fj/RNz4/z/c+P862/j/Ndv3/zHb9/8s2vf/J9r3/yfa9/8n2vf/RN/4/37p + +v9+6fr/fun6/37p+v9+6fr/eNvt/yyVrP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCsxf8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8V0Ob/SN3v/03k + 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOP3/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zzR5/8NlLD/AIOh/wCDof8Ag6H/AI2q/wDM5v8A1O3/AM/n/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8AzeX/AM3l/wDN5f8AzeX/ALbP/wB/m/8Af5v/AH+b/wB/m/8Af5v/HZGt/6bn + /v+u6v3/q+r9/6np/f+l6f3/oun9/5/p/f+A4vz/fOL7/3fh+/9y4fv/buD7/2ng+v9k4Pr/YN/6/1vf + +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z/c+P862/j/Ndv3/zHa9/8s2vf/J9r3/yfa9/8n2vf/J9r3/yna + 9/925/r/fun6/37p+v9+6fr/fun6/37p+v9btsj/AH+b/wB/m/8Af5v/AH+b/wB/m/8AjKf/AMvk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xLP + 5f9I3e//TeP1/0/p/P9Q6fz/YOv8/3Ht/f967v3/eu79/3Ht/f9Y4vP/Bomn/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HpAICjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiYACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKb + tv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zjM4/8Lscz/ALTP/wDR6v8A1O3/ANDo/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wC81v8Af5v/AH+b/wB/m/8Af5v/AH+b/w2H + o/+e5v3/qen9/6bp/f+j6f3/oOj9/53p/f+b6P3/gOP7/3fh+/9y4fv/beD7/2jg+v9k4Pr/X9/6/1rf + +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/VeL5/37p+v9+6fr/fun6/37p+v9+6fr/ddXl/xiLpP8Af5v/AH+b/wB/m/8Af5v/AH+b/wC3 + 0P8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8By+P/ONfr/47q9v+28/r/z/n+/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/8Py+f+Cw9L/RKS6/weG + pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6DNAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8c2/L/ANTt/wDU7f8A1O3/ANLq/wDP5/8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Axd3/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/idr0/6To/f+h6f3/nuj9/5vo/f+Y6P3/luj8/4Hj+/9x4fv/beD6/2jg+v9k3/r/X9/6/1rf + +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/zLc9/996fr/fun6/37p+v9+6fr/fun6/37n+P9Epbr/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8AlbD/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/xLQ + 5v9c3u7/qO72/73y+P/C8/n/0Pf8/9H3/P/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5/v/R+f7/yvL4/73f + 5/+u2OH/YLLE/wyJpf8Ag6H/AIOh/wCDof8Ag6H/AIOhyACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuRrv9N5/n/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM3l/wCDn/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/2jH4f+b5/z/nOj8/5ro/f+X5/3/lOj8/5Hn/P+E5fz/bOD6/2fg+v9j3/r/Xt/6/1ne + +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z3c+P842/j/M9v3/y/a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/ZuX5/37p+v9+6fr/fun6/37p+v9+6fr/cMze/wiDnv8Af5v/AH+b/wB/ + m/8Af5v/AICb/wDA2v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/IdPn/3vl + 8f+58fj/vfL4/73y+P+98vj/vfL4/8Pz+f/Q9/z/0ff8/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5 + /v/H7vT/vd/n/73f5/+u2OH/Sqe8/wGDof8Ag6H/AIOh/wCDof8Ag6GxAKqqAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGwAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/K7vS/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8w4fb/ANTt/wDU7f8A1O3/ANPs/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Akqz/AH+b/wB/ + m/8Af5v/AH+b/wB/m/9Is8z/kuX8/5fo/P+U5/3/kef8/47n/P+L5/z/ieb7/3Tj+v9i3/r/Xt/6/1ne + +f9U3vn/T935/0vd+f9G3fj/Qdz4/z3c+P842/f/M9v3/y7a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Lf+P9+6fr/fun6/37p+v9+6fr/fun6/3zk9f8vmK//AH+b/wB/ + m/8Af5v/AH+b/wB/m/8An7n/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8e0+j/feby/7zy + +P+98vj/vfL4/73y+P+98vj/vfL4/73y+P+98vj/xfT6/9H3/P/R9/z/0fn9/9H5/v/R+f7/0fn+/9H5 + /v/R+f7/0fn+/8Lp8P+93+f/vd/n/73f5/+IxdP/E4yo/wCDof8Ag6H/AIOh/wCCopEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKEbAIShTwCBo0UAg6IhAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIahJgCD + ofoAg6H/AIOh/wCDof8Ag6H/AIOh/wWKp/9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5vn/AtTt/wDU7f8A1O3/ANTt/wDR6f8A0en/ANHp/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AKK8/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/IJay/4jk/P+S5vz/kOf8/43m/P+K5vz/h+b7/4Tm+/+C5vv/eOT7/2Dg + +f9U3vn/T935/0rd+f9G3fj/Qdz4/zzc+P832/f/M9v3/y7a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8p2vf/def6/37p+v9+6fr/fun6/37p+v9+6fr/YMDU/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AIKe/wDI4P8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/xPR6P905PH/vPL4/73y + +f+98vn/vfL5/73y+f+98vn/vfL4/7Dw9/+X6/T/g+fy/3Tj8P+G5PH/leby/5/p8/+t7/j/xPj+/9H5 + /v/R+f7/0fn+/9H5/v/Q+f7/v+Tr/73f5/+93+f/vd/n/6rW4P8smLH/AIOh/wCDof8Ag6H/AIOh7AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGFAIOh+QCDof8Ag6H/AIOh/wCDofkAg6HYAIOhrQCDoHEAhKI0AKqqAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Iq/I/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdfv/wDU7f8A1O3/ANTt/wDS6v8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wC3 + 0P8Af5v/AH+b/wB/m/8Af5v/AH+b/wKBnP912/T/jOb8/4rm/P+I5vz/heX7/4Lm+/9/5fv/feX7/3vl + +v945fr/aOL6/07e+P9F3fj/QNz4/zzc+P832/f/Mtv3/y3a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/1Pi+f9+6fr/fun6/37p+v9+6fr/fun6/3re + 8P8ajKX/AH+b/wB/m/8Af5v/AH+b/wB/m/8AqcP/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/Cc/n/2Lg8P+28fj/vfL5/73y + +f+98vn/vfL5/5jr9f9o3+7/Osne/xayyv8AnLf/AI2p/wCFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wOE + of8YmbH/Ub3Q/4rd6f/C9fv/0fn+/833/f++4Oj/vd/n/73f5/+93+f/ud3l/0Skuv8Ag6H/AIOh/wCD + of8Ag6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKFfAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe8AhKKZAISiNAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIahEwCDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9D2e7/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/FNnw/wDU7f8A1O3/ANTt/wDS6/8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8AzeX/AIKd/wB/m/8Af5v/AH+b/wB/m/8Af5v/RrjT/4Hk+/+F5vz/guX7/3/l+/995fv/euX7/3jk + +v915fr/c+T6/3Dk+v9s5Pr/VuD5/z3c+P822/f/Mtv3/y3a9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8x3Pf/fen6/37p+v9+6fr/fun6/37p + +v9+6fr/SrLG/wB/m/8Af5v/AH+b/wB/m/8Af5v/AIik/wDO5f8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Bzub/Sdzt/6rv9/+98vn/vfL5/73y + +f+Y6/X/V9fo/xi0zP8AlrH/AIOf/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/A4Og/0ezx/+d5e//yPT6/73f5/+93+f/vd/n/73f5/+83ub/VazA/wCD + of8Ag6H/AIOh6QCEoRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HJAISiTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKN0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/GKK9/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LeD1/wDU7f8A1O3/ANTt/wDT7P8A0uv/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wCWsf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/9j0+3/fuT7/33l+/965Pv/eOX7/3Xk + +v9z5Pr/cOT6/27k+f9s4/n/aeT6/2jj+v9f4vn/Qd74/yza9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/2Tk+f9+6fr/fun6/37p + +v9+6fr/fun6/3XX6P8Ig57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Atc7/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8v2Ov/mez1/73z+f+98/n/rO/3/2Pd + 6/8atMz/AI+r/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zCiuf+M2eX/vd/n/73f5/+93+f/vd/n/73f + 5/9YrsH/AIOh/wCDof8Ag6HEAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCodkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhKC6AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKfcACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof87z+X/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/wHU7f8A1O3/ANTt/wDU7f8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8Atc7/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/y2oxP9n3PP/deT6/3Pj + +v9w5Pr/buP6/2vk+f9p4/n/Z+P5/2Tj+v9i4/n/YeP5/17j+f9P4fj/MNz3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9A3vj/fun6/37p + +v9+6fr/fun6/37p+v9+6Pn/NKC3/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJGs/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/xbU6v+A5/P/vfP5/73z+f+T6vT/RMnb/wWZ + tP8AgZ//AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zyovv+o1d//vd/n/73f + 5/+93+f/vd/n/1Gqv/8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAhaFJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIahEwCDoewAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5ey/07o + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/wzX8P8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/AM/p/wCFof8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BoWh/zW1 + 0P9k4Pj/auP6/2jj+f9m4vn/ZOP5/2Hi+f9f4vn/XeL5/1ri+f9a4/n/XOP5/1zj+f9I4Pj/K9v3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/KNr3/3Pn + +v9+6fr/fun6/37p+v9+6fr/fun6/2fM3/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Av9j/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/CdLp/2Li8f+28vj/vPP5/4Lk8P8pts3/AIml/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/Coej/33A + zv+93+f/vd/n/73f5/+23OT/FY2p/wCDof8Ag6H/AISiVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhmgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCopEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8Apb//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/C4yo/zvD3P9e4fn/YeL5/17i+f9c4vn/WuL5/1ji+f9W4vn/WOL5/1rj+f9c4/n/XuP5/1zj + +f9A3vj/KNr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/9R4fn/fun6/37p+v9+6fr/fun6/37p+v995ff/HZCq/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJu2/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8D0un/Sd7v/6rw+P+68/n/edzp/yCowP8AgqD/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCFov8AhqP/AIyo/wCIpv8AhKH/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/UKm9/7nd5f+93+f/vd/n/3m+zv8Ag6H/AIOh/wCDofIAg6MnAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAICjJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoCMAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/CI6r/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/AMnj/wCLpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/EZay/0DO6P9X4fj/V+H5/1Xh+f9U4vn/VuL5/1fi+f9Z4/n/W+P5/1zj + +f9e4/n/YOT5/1ji+f843fj/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/L9v3/3vo+v9+6fr/fun6/37p+v9+6fr/fun6/1G+0/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCA + nf8AyOH/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wHS6v8/3e//oO73/7fx+P9v0uH/GJqz/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIKg/wCTrv8AoLv/AKzG/wC40v8Axd3/AM7m/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDP + 5/8AxN3/ALrS/wCqxf8Am7X/AIek/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8wmrD/s9rj/73f5/+83ub/KJaw/wCDof8Ag6H/AIOh1ACAmQoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCi + vv8Aj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodAAgJkKAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ots7/T+n8/0/p/P9P6fz/T+n8/0/o/P8L1u//ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0uv/AK3I/wCEoP8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJz/F6S//0DX8P9R4fn/UuH5/1Ti+f9W4vn/V+L5/1nj + +f9c4/n/XeP5/1/k+f9h5Pn/YuT5/1Hh+f8v2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/9h5Pn/fen6/37p+v9+6fr/fun6/37p+v954fL/CoWg/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AKfB/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/zbc7v+Y7ff/s+31/2bH1/8Sjqn/AIGe/wCBnv8AgZ7/AISg/wCX + sv8AqsT/AL3X/wDQ6P8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDQ6P8Awdr/AKnD/wSPqv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ySTrP+r1uD/vd/n/5LK1/8Bg6H/AIOh/wCDof8Ag6KkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShNgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCM + qf8A1O3/ANTt/wDI4f8Ap8P/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOghAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaIsAIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/A4el/0ff8/9P6fz/T+n8/0/p/P8s4PX/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Azeb/AKK+/wCA + nf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Cg6D/H7LN/0jc9f9T4vn/VeL5/1bi + +f9Y4vn/WuP5/1zj+f9e4/n/YOT5/2Hk+f9j5Pn/Y+T5/0ng+P8q2/f/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/Pt74/3zp+v9+6fr/fun6/37p+v9+6fr/fun6/zuvxv8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wCGov8A0On/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8C0uv/QN3w/5js9v+r5+//WrnM/wyHo/8AgZ7/AIGe/wCJpv8AoLz/ALnT/wDP + 6P8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/KMrh/x6owv8Cg6H/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/G46o/6zX4P+93+f/QKK5/wCDof8Ag6H/AIOh/wCC + oWoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGoSYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ah6b/ANTt/wDU7f8A1O3/ANTt/wDL5P8AmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofUAgKQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKirACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8eq8T/T+n8/0/p/P9I5/v/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8AxuD/AJey/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Fiqb/K7/a/1Hg + +f9W4vn/V+L5/1nj+f9b4/n/XOP5/17j+f9g5Pn/YuT5/2Tk+f9m5fn/YeT5/0De+P8o2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yja9/9w5vr/fen6/37p+v9+6fr/fun6/37p+v9s1+r/AYCb/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/ALLM/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wfU7f9N4PL/o+v0/57d5/9Jqb7/BYOg/wCBnv8AjKj/AKfC/wDC3P8A0+z/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wbT7P9O5/r/R93x/yay + y/8EhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8hkav/r9ji/6bU3v8GhqP/AIOh/wCD + of8Ag6H5AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wCeuv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKhiwAAAAAAAAAAAAAAAAAAAAAAAAAAAIakKgCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ak7D/A4uo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0DW7P9P6fz/ENjw/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8AvNb/AI2o/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8MlLD/OMzm/1Xi+f9X4vn/WeP5/1vj+f9d4/n/X+T5/2Hk+f9i5Pn/ZOT5/2bl+f9n5fn/W+P5/zbd + +P8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/TuH4/3vo+v996fr/fun6/37p+v9+6fr/fuj6/yKb + tP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCNqP8A0+3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/I9nv/23l8/+r6PD/esnX/zGZsf8BgZ7/AI6r/wCpxP8Aw93/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Etfu/0zn + +v9O5/r/Rt3w/yCrxP8Bg6D/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ymWrv+23OT/Wq/C/wCD + of8Ag6H/AIOh/wCDoeEAhqETAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAImdDQCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8A0On/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ev/AI2q/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoe8AjqoJAAAAAAAAAAAAAAAAAAAAAACDoqEAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/B8Ld/y2/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/NeL3/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AdTt/wHU7f8C1O3/AtTt/wLT6/8Bscr/AIai/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wCAm/8Xor3/Rdfv/1ji+f9a4/n/XOP5/13j+f9g5Pn/YeT5/2Pk+f9l5fn/Z+X5/2jl + +f9q5fn/U+L5/y7b9/8n2vf/J9r3/yfa9/8n2vf/J9r3/y/b9/966Pr/fOn6/33p+v9+6fr/fun6/37p + +v9Yyt//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/ALzW/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W + 7v9M4fL/lOXw/5DX4/9RrsH/DYej/wCSrf8ArMf/AMfg/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8e3PL/T+j7/0/o+/9P6Pv/QdXr/xGXsv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/PaC2/7Pa + 4/8SjKj/AIOh/wCDof8Ag6H/AIKitAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AMzm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC30v8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOiUgAAAAAAAAAAAAAAAACAoh4Ag6H7AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AJe0/y/h9v9O5/v/DZOw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wK91/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU + 7f8C1O3/AtTt/wPV7f8D1e3/BNXt/wTV7f8E1e3/BNXt/wTQ6P8CpsD/AIGd/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGDn/8isMv/UN71/1vj+f9c4/n/XuP5/2Dk+f9h5Pn/ZOT5/2bl + +f9n5fn/aeX5/2vm+v9p5fn/SuD4/yra9/8n2vf/J9r3/zLc9/9g5Pn/eOj6/3vo+v996fr/fun6/37p + +v9+6fr/fOb4/w6Lpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wCXsv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//VOLz/3Tf + 7v900N//QKm//w2MqP8AnLj/ALTP/wDM5v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yzg9f9P6fz/T+j7/0/o+/9O5/r/Lr3U/wOFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv9cr8L/dLvM/wCDof8Ag6H/AIOh/wCDof8Ag6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDN5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/AI2q/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoJ8AAAAAAAAAAAAAAAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wrD3v9O6Pz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak7D/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/A9Xt/wTV + 7f8E1e3/BNXt/wXV7f8F1e3/BtXt/wbV7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wbJ4v8Cm7X/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWIpP8wv9n/V+H4/1zj+f9f5Pn/YeT5/2Lk + +f9k5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9m5fn/Qd74/03h+P9y5/r/duf6/3fo+v956Pr/e+j6/33p + +v9+6fr/fun6/37p+v9CvNP/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJv/AMbg/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDR6v8Xwdn/IbPM/wme + uf8Aob3/ALTO/wDF3/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1e3/OeP4/0/p/P9P6fz/T+n8/0/p/P9C2ez/D5Sv/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AYGe/37Az/8kla7/AIOh/wCDof8Ag6H/AIOh+gCCoTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCs + yP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LSAAAAAAAAAAAAgKoMAIOh7wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCTsP8v4fb/T+n8/0/p/P9P6fz/FZ+5/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDL + 5f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wXV + 7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wjV7v8I1e7/Cdbu/wnW7v8J1u7/Cdbu/wnW7v8J1u7/Ctbu/wfA + 2f8CkKv/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wyTrv8/y+X/XOP5/1/k + +f9h5Pn/Y+T5/2Xl+f9n5fn/aOX5/2rl+f9s5vr/beb6/2/m+v9y5/r/c+f6/3Xn+v936Pr/eOj6/3ro + +v986fr/fen6/37p+v9+6fr/Wdzy/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCkvv8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT6/8AyeL/ANDp/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXW7v9E5vn/T+n8/0/p/P9P6fz/T+n8/03m+f8hrcb/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8MiKT/dbzM/wCDof8Ag6H/AIOh/wCDof8Ag6HgAICcEgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA + pBwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8AyOL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCSkgcAAAAAAIKiaACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Jv9n/Ten8/0/p/P9P6fz/T+n8/0HX7P8BhKL/AIOh/wCDof8Ag6H/AIOh/wCD + of8AzOb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV + 7f8I1e7/CNXu/wnW7v8J1u7/Cdbu/wnW7v8K1u7/Ctbu/wvW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/wzW + 7v8M1u7/DNXt/we1z/8BiKP/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/xae + uP9M1ez/YOT5/2Hk+f9j5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9u5vr/cOb6/3Ln+v9z5/r/def6/3fo + +v946Pr/euj6/33p+v9z5/r/Qd32/xWxzv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ag5//AM/o/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/zDA + 1/8Cg6D/AIGe/wCBnv8AgZ7/AIGe/yqXr/84nrb/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6MnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIem/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wCHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAACCoNUAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Aj6v/LuD1/0/p/P9P6fz/T+n8/0/p/P9P6fz/D5ey/wCDof8Ag6H/AIOh/wCD + of8AmbX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AdTt/wLU7f8C1O3/A9Xt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW + 7v8J1u7/Ctbu/wrW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/w3W7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W + 7v8O1u7/Dtbu/w7W7v8P1+7/DtPq/weqw/8Bg57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AYCd/yatyP9Y3fT/YuT5/2Tk+f9m5fn/Z+X5/2nl+f9r5vr/bOb6/27m+v9x5/r/cuf6/3Tn + +v925/r/d+j6/3fo+v9R4fn/IMHd/waMqP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGu + yP8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vL/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/PNDm/weJpv8AgZ7/AIGe/wCBnv8AgZ//PqG4/wSFov8Ag6H/AIOh/wCDof8Ag6H/AIKgZgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKiPwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCQrf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Ah6b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKfLQCEoD4Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/CLjR/03p+/9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8jsMr/AYak/wCE + ov8An7v/AM7m/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU + 7f8C1O3/AtTt/wPV7f8E1e3/BNXt/wXV7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8J1u7/Ctbu/wvW + 7v8M1u7/DNbu/wzW7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W7v8P1+7/D9fu/xDX7v8Q1+7/ENfu/xDX + 7v8Q1+7/ENfu/xDX7v8R1+7/Edfu/xHX7v8R1+7/D83k/waeuP8AgJv/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/BIah/zS71f9g4vj/ZeX5/2fl+f9o5fn/auX5/2zm+v9t5vr/b+b6/3Hn + +v9z5/r/def6/1/k+f8szun/C5m1/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8BlrD/BNXt/wPV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yrf9f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9D2e7/Co6q/wCBnv8AgZ7/AIGe/wKDof8smLH/AIOh/wCDof8Ag6H/AIOh/wCD + ofQAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACComgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AnLj/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A0uv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoRsAhKCfAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIek/ybb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/POT5/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU + 7f8D1e3/BNXt/wTV7f8F1e3/BtXt/wfV7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/wzW + 7v8N1u7/Dtbu/w7W7v8O1u7/D9fu/w/X7v8Q1+7/ENfu/xDX7v8Q1+7/Edfu/xHX7v8S1+7/Etfu/xPX + 7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/D8Tc/wSSrf8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpCr/0TJ4f9l5fn/Z+X5/2nl+f9r5vr/bOb6/27m + +v9w5vr/Z+X5/zvY8v8QpcL/AYCd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGE + oP8Ersj/B9Pr/wbV7f8E1e3/BNXt/wPV7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/O+T4/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fe8v8Nkq7/AIGe/wCBnv8AgZ7/FI2o/wqIpf8Ag6H/AIOh/wCD + of8Ag6H/AIOhywCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqGNAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKnF/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AMTe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgKQOAIOh8wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKlwf9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ + 8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV + 7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8J1u7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W + 7v8O1u7/D9fu/xDX7v8Q1+7/ENfu/xHX7v8R1+7/Etfu/xPX7v8T1+7/E9fu/xPX7v8U1+7/FNfu/xTX + 7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8W2O//Ftjv/xbY7/8W1+//DrnS/wKJ + pf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/G523/1TV7P9n5fn/aeX5/2vm + +v9q5fn/St/3/xmz0P8ChaD/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wSj + vv8Iz+f/Cdbu/wjV7v8H1e3/B9Xt/wbV7f8F1e3/BNXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW + 7/9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/w+VsP8AgZ7/AIGe/wCCn/8Oiqb/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhuQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC10P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCzzv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HDAIKgXgCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8d0en/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7l + +f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wTV + 7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8K1u7/C9bu/wzW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX + 7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xTX7v8V2O7/Fdju/xXY7v8V2O7/Ftjv/xbY + 7/8X2O//F9jv/xfY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY + 7/8X1Oz/DK3H/wGDn/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8BgJz/Kq3F/2De + 8/9W4vn/JsPe/waOqv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BJey/wvI + 4f8M1u7/DNbu/wvW7v8K1u7/Cdbu/wnW7v8I1e7/B9Xt/wfV7f8G1e3/BNXt/wTV7f8D1e3/AtTt/wHU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/E9nw/03o/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/DJOv/wCBnv8AgZ7/AYOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCCoy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAJmZBQCDoPMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Axd7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOijgCE + obwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aka//PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8b3PL/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wPV7f8E1e3/BdXt/wbV + 7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8O1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX + 7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8W2O//F9jv/xfY7/8Y2O//GNjv/xjY + 7/8Y2O//Gdjv/xnY7/8Z2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY + 7/8a2O//Gtjv/xrY7/8Xz+f/CqG7/wCAnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8CgJz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ci6f/C7vT/xDX + 7v8P1+7/Dtbu/w7W7v8N1u7/DNbu/wzW7v8L1u7/Ctbu/wnW7v8J1u7/B9Xt/wfV7f8G1e3/BdXt/wTV + 7f8D1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8m3vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jh9P8Ika3/AIGe/wCB + oP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HLAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAgqD/AIGf/wCBn/8AgqD/AIOh/wCDof8AiKX/ANTs/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJi0/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D7rU/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9G5/r/AdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wTV7f8F1e3/B9Xt/wfV + 7f8I1e7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W7v8P1+7/ENfu/xDX7v8R1+7/Etfu/xPX + 7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xnY7/8a2O//Gtjv/xrY + 7/8a2O//G9nv/xvZ7/8b2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ + 7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8Xx9//B5ex/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGFoP8Lr8n/E9Xs/xPX + 7v8S1+7/Edfu/xDX7v8Q1+7/D9fu/w7W7v8O1u7/Ddbu/wzW7v8M1u7/Ctbu/wnW7v8J1u7/CNXu/wfV + 7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AdXt/z3k+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R9/0/wKN + qf8AgZ7/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGFAIKg/wCBn/8AgZ7/AICe/wCAnv8AgZ//AJu4/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCNqv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi/yzd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Jt71/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BNXt/wXV7f8H1e3/B9Xt/wjV + 7v8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX7v8Q1+7/Edfu/xLX7v8T1+7/E9fu/xTX + 7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8b2e//HNnv/xzZ + 7/8c2e//HNnv/x3Z7/8d2e//Htnv/x7Z7/8e2e//Htnv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z + 7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x7Z7/8WwNj/BY6o/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wqkvv8V0ej/Ftjv/xXY + 7v8V2O7/FNfu/xPX7v8T1+7/Etfu/xHX7v8Q1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8M1u7/C9bu/wnW + 7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1+//S+j7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P801uz/AIWi/wCCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H0AICfIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOh4ACCoP8AgZ//AIGe/wCAnv8AgJ3/AH+d/wCs + xv8A0Oj/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AhaP/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGbtv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/wbV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wTV7f8E1e3/BdXt/wfV7f8H1e3/Cdbu/wnW + 7v8K1u7/DNbu/wzW7v8N1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8U1+7/Fdju/xXY + 7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Hdnv/x7Z + 7/8f2e//H9nv/x/Z7/8f2e//H9nv/yDZ7/8g2e//INnv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa + 7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8g2e//INnv/x/Y7f8TtMz/Aoai/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/B5Ww/xXH4P8Z2O//Gdjv/xjY + 7/8Y2O//F9jv/xbY7/8V2O7/Fdju/xTX7v8T1+7/E9fu/xLX7v8Q1+7/ENfu/w/X7v8O1u7/Dtbu/wzW + 7v8M1u7/C9bu/wrW7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xfJ4f8Agp//AIOg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/AIOh/wCCoP8AgZ//AICe/wB/ + nf8AwNj/AM3l/wDM5P8Azeb/ANLq/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJOv/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ywtv/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zLh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Cdbu/wrW + 7v8M1u7/DNbu/w3W7v8O1u7/D9fu/xDX7v8R1+7/Etfu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Ftjv/xjY + 7/8Y2O//GNjv/xnY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/x3Z7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ + 7/8g2e//Idrv/yHa7/8h2u//Idrv/yLa7/8i2u//Itrv/yPa7/8j2u//I9rv/yPa7/8j2u//JNrw/yTa + 8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/I9rv/yPa7/8j2u//I9rv/yLa7/8i2u//Itrv/x/U + 6v8PqMH/AYGd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8EjKf/FLzU/xzZ7/8c2e//G9nv/xvZ + 7/8a2O//Gtjv/xnY7/8Y2O//GNjv/xfY7/8W2O//Fdju/xXY7v8T1+7/E9fu/xLX7v8R1+7/ENfu/w/X + 7v8O1u7/Dtbu/w3W7v8M1u7/C9bu/wnW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wTV7f8C1O3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/OOP4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9I5/r/A7bQ/wCDoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKhOQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ai6n/ANHp/wDO5v8AzOT/AMvj/wDJ4f8AyuL/AM/n/wDT7P8A1O3/ANTt/wC10P8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/Nd/1/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8U2fH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8J1u7/Ctbu/wzW + 7v8M1u7/Dtbu/w7W7v8P1+7/ENfu/xHX7v8S1+7/E9fu/xTX7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY + 7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa + 7/8i2u//Itrv/yPa7/8j2u//JNrw/yTa8P8k2vD/JNrw/yTa8P8l2vD/Jdrw/yXa8P8m2vD/Jtrw/yba + 8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8l2vD/Jdrw/yXa8P8l2vD/JNrw/yTa + 8P8k2vD/JNrw/x7N4/8LnLb/AH+b/wB/m/8Af5v/AH+b/wKFoP8SsMn/H9ft/x/Z7/8f2e//Htnv/x3Z + 7/8c2e//HNnv/xzZ7/8b2e//Gtjv/xrY7/8Z2O//GNjv/xjY7/8W2O//Fdju/xXY7v8U1+7/E9fu/xLX + 7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/C9bu/wnW7v8J1u7/B9Xt/wfV7f8F1e3/BNXt/wPV + 7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX7/9N6fv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yrf9f8Anrn/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ocUA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgwAg6HvAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AKjE/wDU7f8A1O3/ANLr/wDQ6P8AzOT/AMjg/wDI3/8AyN//AMzk/wDS6v8A1O3/AKzI/wCE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5q3/0vo+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9G5/r/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BdXt/wbV7f8H1e3/CNXu/wnW7v8K1u7/DNbu/wzW + 7v8O1u7/Dtbu/w/X7v8Q1+7/Edfu/xPX7v8T1+7/FNfu/xXY7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY + 7/8a2O//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yLa7/8i2u//I9rv/yTa + 8P8k2vD/JNrw/yXa8P8l2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yfb8P8o2/D/KNvw/yjb + 8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yfb8P8n2/D/J9vw/yba + 8P8m2vD/Jtrw/yba8P8m2vD/Jdrw/xzD2/8Hkav/AICc/w6iu/8g0+j/Itrv/yLa7/8h2u//Idrv/yDZ + 7/8f2e//H9nv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8Z2O//GNjv/xjY7/8X2O//Ftjv/xXY + 7v8U1+7/E9fu/xPX7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wbV + 7f8F1e3/BNXt/wLU7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/KN/0/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/Bdbu/wCMqf8Ag6D/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOiUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFqAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wDF3v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8Az+f/AMvj/wDH3v8Axdz/AMjg/wDP + 5/8AxN//AJ67/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISj/xjP5/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Kd/0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BtXt/wfV7f8I1e7/Cdbu/wrW7v8M1u7/DNbu/w7W + 7v8O1u7/D9fu/xDX7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xnY7/8a2O//Gtjv/xvZ + 7/8c2e//HNnv/x7Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa + 8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yjb8P8o2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb8P8p2/D/Ktvw/yrb + 8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb + 8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8n2/D/Jtnv/yHO5f8m2vD/Jdrw/yTa8P8k2vD/JNrw/yPa + 7/8i2u//Idrv/yHa7/8g2e//INnv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8a2O//Gdjv/xjY + 7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xPX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8L1u7/Ctbu/wnW + 7v8I1e7/B9Xt/wXV7f8E1e3/A9Xt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU + 7v9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8AwNv/AIOg/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoccAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKg1QCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCRr/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANDo/wDL + 4/8Axdz/AMXc/wDM5P8A0On/AMHb/wCyzv8ApcH/AIOh/wCDof8Ag6H/AImn/wC10P8y4fb/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wzX8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W + 7v8P1+7/ENfu/xHX7v8T1+7/E9fu/xTX7v8V2O7/Ftjv/xjY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/xzZ + 7/8d2e//Htnv/x/Z7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa8P8m2vD/Jtrw/yfb + 8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/ynb8P8q2/D/Ktvw/yvb8P8r2/D/K9vw/yvb8P8r2/D/LNvw/yzb + 8P8s2/D/LNvw/yzb8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/LNvw/yzb8P8s2/D/LNvw/yvb + 8P8r2/D/K9vw/yvb8P8r2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8m2vD/Jtrw/yba + 8P8l2vD/JNrw/yTa8P8j2u//Itrv/yHa7/8h2u//INnv/x/Z7/8f2e//Htnv/x3Z7/8c2e//HNnv/xrY + 7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW + 7v8L1u7/Cdbu/wnW7v8H1e3/BtXt/wXV7f8E1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J6Pv/A9Xu/wCeuv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AISfOAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKg/wCDof8Ar8v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDQ6P8AyuL/AMTb/wDI3/8A0Oj/ANTt/wDU7f8AyOL/AMTe/wDQ6f8B1e3/Suf7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Dl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8G1e3/B9Xt/wjV7v8J1u7/C9bu/wzW7v8N1u7/Dtbu/w/X + 7v8Q1+7/Edfu/xLX7v8T1+7/FNfu/xXY7v8W2O//GNjv/xjY7/8Z2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z + 7/8f2e//H9nv/yDZ7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/Jtrw/yba8P8n2/D/KNvw/ynb + 8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb8P8s2/D/LNvw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ltzw/y7c + 8P8u3PD/Ltzw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y7c8P8u3PD/Ltzw/y7c + 8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/ynb + 8P8o2/D/J9vw/yba8P8m2vD/Jdrw/yTa8P8k2vD/I9rv/yLa7/8h2u//Idrv/yDZ7/8f2e//H9nv/x3Z + 7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8Q1+7/ENfu/w7W + 7v8O1u7/DNbu/wzW7v8K1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f9B5fr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yLd9P8AyuP/AISi/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAACDobsAg6H/AIOh/wCD + of8Ag6H/AIOh/wCCoP8AhqP/AM3m/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ur/AMri/wDI4P8AzeX/ANTt/wDU7f8A1O3/Etnw/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8n3vX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8K1u7/DNbu/wzW7v8O1u7/Dtbu/xDX + 7v8R1+7/Etfu/xPX7v8U1+7/Fdju/xbY7/8Y2O//GNjv/xrY7/8a2O//G9nv/xzZ7/8d2e//Htnv/x/Z + 7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/yrb + 8P8r2/D/K9vw/yzb8P8s2/D/Ldzw/y3c8P8t3PD/Ltzw/y7c8P8v3PD/L9zw/zDc8P8w3PD/MNzw/zDc + 8P8w3PD/MNzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/MNzw/zDc + 8P8w3PD/MNzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/K9vw/yvb + 8P8r2/D/Ktvw/ynb8P8p2/D/KNvw/yfb8P8m2vD/Jtrw/yXa8P8k2vD/JNrw/yPa7/8i2u//Idrv/yDZ + 7/8f2e//H9nv/x7Z7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Fdju/xXY7v8T1+7/E9fu/xHX + 7v8Q1+7/D9fu/w7W7v8N1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/AdTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5Pn/ANTt/wCe + u/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H4AICkDgAAAAAAAAAAAAAAAACCojcAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqD/AKW//wDS6v8A0en/ANHp/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDN5v8A0Oj/ANPs/yfe + 9f9P6fz/T+n8/zvP5v8ms83/JbPM/yq50f8uvdX/CbfR/wC51P8Avdj/AMDb/wDE3/8AyeL/AM3n/wDQ + 6f8A1O3/AdTt/wLU7f8D1e3/BNXt/wbV7f8H1e3/CNXu/wnW7v8L1u7/DNbu/w3W7v8O1u7/D9fu/xDX + 7v8S1+7/E9fu/xTX7v8V2O7/Ftjv/xfY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/x3Z7/8e2e//H9nv/yDZ + 7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb + 8P8s2/D/Ldzw/y3c8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zDc8P8w3PD/Mdzw/zHc8P8y3PH/Mtzx/zLc + 8f8y3PH/Mtzx/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd + 8f8y3PH/Mtzx/zLc8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c + 8P8t3PD/Ldzw/yzb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/yjb8P8n2/D/Jtrw/yba8P8l2vD/JNrw/yPa + 7/8i2u//Idrv/yHa7/8f2e//H9nv/x7Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xjY7/8W2O//Fdju/xTX + 7v8T1+7/Etfu/xHX7v8Q1+7/Dtbu/w7W7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8F1e3/BNXt/wLU + 7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f9E5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wvX + 8P8AxN7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAAAAAAg6GzAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIKg/wDG4f8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDQ6P8A0ur/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f895Pn/T+n8/yKvyf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCIpf8Ai6j/AY6s/wGUsP8Cl7T/A5q3/wOeuv8Fo77/BabD/weqxf8IsMr/CbPN/wq2 + 0f8Nu9X/Dr/Z/xDE3P8SyOD/FMzk/xbR6P8Y1Ov/Gtjv/xvZ7/8c2e//Hdnv/x/Z7/8f2e//INnv/yHa + 7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/Kdvw/ynb8P8q2/D/K9vw/yvb8P8s2/D/Ldzw/y3c + 8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8y3PH/M93x/zPd8f803fH/NN3x/zXd + 8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd + 8f813fH/Nd3x/zXd8f813fH/Nd3x/zTd8f803fH/M93x/zPd8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc + 8P8w3PD/L9zw/y/c8P8u3PD/Ldzw/y3c8P8s2/D/K9vw/yvb8P8q2/D/Kdvw/ynb8P8o2/D/Jtrw/yba + 8P8l2vD/JNrw/yTa8P8i2u//Idrv/yHa7/8f2e//H9nv/x3Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xfY + 7/8W2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wXV + 7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8m3vX/ANTt/wCXtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GoAAAAAAAAAAAAgqExAIOh/gCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCbuP8A0+z/ANLq/wDR6f8A0Oj/AM/n/wDP5/8Azub/AM3m/wDN + 5v8Az+f/ANLr/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8E1e7/Tuj8/0DW6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8CiKX/A4uo/wSPrP8GlLH/CJi0/wqc + t/8MoLv/DqXA/xCoxP8SrMb/FbLL/xe1zv8ZudL/H8Xd/yva7/8r2/D/LNvw/y3c8P8t3PD/Ltzw/y/c + 8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zXd8f813fH/Nt3x/zbd + 8f833fH/N93x/zfd8f833fH/N93x/zfd8f833fH/ON3x/zjd8f843fH/ON3x/zjd8f843fH/ON3x/zfd + 8f833fH/N93x/zfd8f833fH/N93x/zfd8f823fH/Nt3x/zXd8f813fH/Nd3x/zXd8f803fH/M93x/zPd + 8f8y3PH/Mtzx/zHc8P8x3PD/MNzw/zDc8P8v3PD/Ltzw/y3c8P8t3PD/LNvw/yvb8P8r2/D/Kdvw/ynb + 8P8o2/D/J9vw/yba8P8l2vD/JNrw/yTa8P8i2u//Idrv/yDZ7/8f2e//H9nv/x3Z7/8c2e//G9nv/xrY + 7/8Z2O//GNjv/xfY7/8V2O7/FNfu/xPX7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8L1u7/Cdbu/wjV + 7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v9M6Pv/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/QOX6/wDU7f8AtdD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wAAAAAAAAAAAIOgrwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aw93/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN + 5v8AzeX/AMzk/wDL4/8AzeX/ANDo/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/GNrx/0/p/P8wwdj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fjqr/Isfd/y7c8P8v3PD/MNzw/zDc + 8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f833fH/ON3x/zjd + 8f843fH/Od7x/zne8f853vH/Od7x/zne8f853vH/Ot7x/zre8f863vH/Ot7x/zre8f863vH/Ot7x/zre + 8f863vH/Od7x/zne8f853vH/Od7x/zne8f853vH/ON3x/zjd8f843fH/N93x/zfd8f833fH/Nt3x/zXd + 8f813fH/Nd3x/zTd8f8z3fH/M93x/zLc8f8y3PH/Mdzw/zDc8P8w3PD/L9zw/y7c8P8t3PD/LNvw/yvb + 8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ7/8f2e//Htnv/x3Z + 7/8c2e//Gtjv/xrY7/8Y2O//F9jv/xbY7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8O1u7/DNbu/wvW + 7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/NeL3/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8H1u7/AM7m/wCDov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AAAAAAIKiPwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A0uv/ANHp/wDO5v8AzOT/AMri/wDK4v8AyeH/AM3l/wDR6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yzg9f9P6fz/PdLo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSLqP8v2+7/Mdzw/zLc + 8f8y3PH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Od7x/zre + 8f863vH/O97x/zve8f873vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze + 8f883vH/PN7x/zze8f883vH/PN7x/zze8f873vH/O97x/zve8f863vH/Ot7x/zne8f853vH/Od7x/zjd + 8f843fH/N93x/zfd8f823fH/Nd3x/zXd8f813fH/NN3x/zPd8f8y3PH/Mtzx/zHc8P8w3PD/L9zw/y7c + 8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ + 7/8f2e//Hdnv/xzZ7/8b2e//Gtjv/xnY7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/D9fu/w7W + 7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xrb8v9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/GNrx/wDU7f8AlrP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICAAgCD + oc4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AMDb/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0en/AM7m/wDL4/8AyOD/AMnh/wDN5v8A0uv/ANTt/wDU + 7f8A1O3/ANTt/wDU7f895Pn/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/KMrh/zPd + 8f803fH/Nd3x/zXd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/PN7x/zze + 8f883vH/PN7x/z3e8f893vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e + 8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z3e8f893vH/PN7x/zze8f883vH/PN7x/zve + 8f863vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/N93x/zbd8f813fH/Nd3x/zTd8f8y3PH/Mtzx/zHc + 8P8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa + 7/8h2u//INnv/x/Z7/8e2e//Hdnv/xzZ7/8a2O//Gtjv/xjY7/8X2O//Fdju/xXY7v8T1+7/Etfu/xDX + 7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8A1O3/AKfD/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC + oWIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDS6v8Azub/AMvj/wDM + 5P8A0Oj/ANTt/wDU7f8C1O7/Ten8/0/p/P9P6fz/Tef5/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y/T + 6f813fH/Nt3x/zfd8f833fH/ON3x/zne8f853vH/Ot7x/zve8f873vH/PN7x/zze8f893vH/Pd7x/z7e + 8f8+3vH/Pt7x/z/f8f8/3/H/P9/x/0Df8v9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf + 8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9A3/L/QN/y/0Df8v8/3/H/P9/x/z7e8f8+3vH/Pt7x/z7e + 8f893vH/PN7x/zze8f883vH/O97x/zre8f853vH/Od7x/zjd8f833fH/N93x/zbd8f813fH/Nd3x/zTd + 8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/Jtrw/yba + 8P8k2vD/I9rv/yLa7/8h2u//INnv/x/Z7/8d2e//HNnv/xvZ7/8a2O//GNjv/xjY7/8W2O//Fdju/xPX + 7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k + +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P834/j/ANTt/wC10f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wDE3v8A1O3/ANPs/wDS6/8A0+z/ANPs/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANLr/wDR6f8A0+z/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/M8Xb/wOHpf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP82yd//O8/m/zjM4v81x97/MMHY/y291P8quND/JbPM/yGu + x/8eqsT/GqW//xafuv8Tm7b/D5ey/wuRrv8HjKn/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKi + vP833fH/N93x/zjd8f853vH/Od7x/zre8f873vH/PN7x/zze8f893vH/Pt7x/z7e8f8+3vH/P9/x/z/f + 8f9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Lf8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf + 8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Lf8v9C3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Df + 8v9A3/L/P9/x/z7e8f8+3vH/Pt7x/z3e8f883vH/PN7x/zve8f863vH/Od7x/zne8f843fH/N93x/zfd + 8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yvb8P8r2/D/Kdvw/ynb + 8P8n2/D/Jtrw/yXa8P8k2vD/I9rv/yHa7/8g2e//H9nv/x7Z7/8c2e//G9nv/xrY7/8Z2O//GNjv/xbY + 7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8t4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+f6/wDU7f8AwNr/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wChvf8A1O3/ANTt/wDT7P8A0uv/ANLr/wDS + 6v8A0en/ANHp/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B2O3/AZCt/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Boqo/znM4/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/0fe8v9E2u//P9Tq/zvP + 5v84zOL/Ncfe/zDB2P8tvdT/KrjQ/yWzzP8hrsf/HqrE/xqlv/8Wn7r/Epu2/w+Xsv8Ag6H/CJCt/xuw + yf812e7/Od7x/zne8f863vH/O97x/zze8f883vH/Pd7x/z7e8f8+3vH/P9/x/0Df8v9A3/L/Qd/y/0Hf + 8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9E3/L/RN/y/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg + 8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9E3/L/RN/y/0Tf8v9D3/L/Q9/y/0Pf + 8v9C3/L/Qt/y/0Hf8v9B3/L/Qd/y/0Df8v8/3/H/Pt7x/z7e8f893vH/PN7x/zze8f873vH/Ot7x/zne + 8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y7c8P8t3PD/LNvw/yvb + 8P8q2/D/Kdvw/yjb8P8m2vD/Jtrw/yTa8P8j2u//Itrv/yHa7/8f2e//Htnv/x3Z7/8c2e//Gtjv/xnY + 7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV + 7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/H9zz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8G1e7/AMHb/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCHpP8AzOX/ANTt/wDU7f8A0+z/ANLr/wDS + 6/8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDP5/8A0Oj/ANLq/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8w4fb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sej7/wDQ + 6f8Anrr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/J7XO/07o+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PeDz/zjd + 8f853vH/Od7x/zve8f883vH/PN7x/z3e8f8+3vH/Pt7x/z/f8f9A3/L/Qd/y/0Hf8v9C3/L/Q9/y/0Pf + 8v9D3/L/RN/y/0Tf8v9F4PL/ReDy/0Xg8v9G4PL/RuDy/0bg8v9H4PL/R+Dy/0fg8v9I4PL/SODy/0jg + 8v9I4PL/SODy/0jg8v9I4PL/SODy/0jg8v9I4PL/R+Dy/0fg8v9H4PL/R+Dy/0bg8v9G4PL/ReDy/0Xg + 8v9F4PL/RN/y/0Tf8v9D3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9A3/L/P9/x/z7e8f8+3vH/Pd7x/zze + 8f883vH/O97x/zne8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y7c + 8P8t3PD/LNvw/yvb8P8p2/D/Kdvw/yfb8P8m2vD/KNvw/yrb8P8r2/D/J9vv/yPa7/8f2e//Hdnv/xzZ + 7/8a2O//Gdjv/xjY7/8X2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV + 7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DNjw/wDG3/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ArMj/ANTt/wDU7f8A1O3/ANPs/wDS + 6/8A0ur/ANLq/wDR6f8A0Oj/ANDo/wDP5/8Azub/AM3m/wDN5v8AzeX/AM3m/wDP5/8A0ur/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zvj + +f8A1O3/ANTt/wCxzP8AhKP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Wn7r/SOH0/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/zre + 8f863vH/O97x/zze8f883vH/Pt7x/z7e8f8/3/H/QN/y/0Hf8v9B3/L/Qt/y/0Pf8v9D3/L/RN/y/0Xg + 8v9F4PL/ReDy/0bg8v9H4PL/R+Dy/0jg8v9I4PL/SODy/0jg8v9J4PL/SeDy/0ng8v9K4PL/SuDy/0rg + 8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9J4PL/SeDy/0ng8v9I4PL/SODy/0jg + 8v9I4PL/R+Dy/0fg8v9G4PL/ReDy/0Xg8v9F4PL/RN/y/0Pf8v9D3/L/Qt/y/0Hf8v9B3/L/QN/y/z/f + 8f8+3vH/Pt7x/zze8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8z3fH/Mtzx/zHc + 8P8w3PD/Nd3w/0Lf8f9L4PL/VOLz/13k9P9k5fT/aOb0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuT0/1zj + 8/9T4vP/SODy/zze8f8w3PH/Htnv/xfY7/8V2O7/FNfu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW + 7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xHY8P8Axt//AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/ANLr/wDU7f8A1O3/ANTt/wDU + 7f8A0+z/ANPs/wDS6/8A0ur/ANHp/wDQ6P8Az+f/AM7m/wDN5v8Azeb/AM3l/wDM5P8Ay+P/AMvj/wDL + 4/8AzeX/AM/n/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8u4PX/ANTt/wDU7f8A1O3/AMLc/wCLqf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmP + rP8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nm + +f873vH/PN7x/z3e8f8+3vH/Pt7x/0Df8v9B3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Tf8v9F4PL/ReDy/0bg + 8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9K4PL/SuDy/0rg8v9K4PL/S+Hy/0vh8v9M4fL/TOHy/0zh + 8v9M4fL/TOHy/0zh8v9N4fL/TeHy/0zh8v9M4fL/TOHy/0zh8v9M4fL/TOHy/0vh8v9L4fL/SuDy/0rg + 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Lf + 8v9B3/L/Qd/y/z/f8f8+3vH/Pt7x/z3e8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nt3x/0Pg + 8v9V4vP/ZeX0/27m9P9t5vT/bOb0/2zm9P9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9l5fT/ZeX0/2Pl + 9P9i5PT/YuT0/2Dk9P9g5PT/X+T0/17k9P9R4vP/P9/y/ybb7/8V1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W + 7v8M1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/D9jv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8W2vH/AMLc/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/AMXe/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDN + 5v8AzOT/AMri/wDI4P8AyOD/AMnh/wDN5f8A0Oj/ANPs/wfW7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wDU7f8Azub/AJm1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AoWj/y691f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9F4/f/Pd7x/z7e8f8/3/H/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9E3/L/ReDy/0Xg8v9G4PL/R+Dy/0jg + 8v9I4PL/SeDy/0rg8v9K4PL/SuDy/0vh8v9M4fL/TOHy/03h8v9N4fL/TeHy/03h8v9N4fL/TuHz/07h + 8/9O4fP/TuHz/0/h8/9P4fP/T+Hz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TuHz/07h8/9N4fL/TeHy/03h + 8v9N4fL/TeHy/0zh8v9L4fL/S+Hy/0rg8v9K4PL/SeDy/0ng8v9I4PL/SODy/0fg8v9G4PL/ReDy/0Xg + 8v9E3/L/Q9/y/0Lf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/z3e8f883vH/O97x/zne8f9C3/L/V+Lz/2zm + 9f9x5/X/cef1/3Dn9f9v5vX/bub0/27m9P9s5vT/bOb0/2vm9P9q5vT/aeb0/2nm9P9n5vT/Z+X0/2Xl + 9P9l5fT/Y+X0/2Lk9P9i5PT/YeT0/2Dk9P9f5PT/XuT0/13k9P9c5PT/U+Lz/zrd8f8d2e//Edfu/xDX + 7v8O1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wvX7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/G9zy/wC6 + 1P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AK7J/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN5f8Q0+n/TuX4/0/o+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/xja8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7P8Aq8b/AISi/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/G6XA/0vk+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/QuHz/z/f8f9A3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Xg8v9F4PL/RuDy/0fg8v9I4PL/SODy/0ng + 8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h8v9N4fL/TuHz/07h8/9P4fP/T+Hz/0/h8/9P4fP/UOHz/1Dh + 8/9Q4fP/UOHz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UOHz/1Dh8/9Q4fP/T+Hz/0/h + 8/9P4fP/T+Hz/0/h8/9O4fP/TeHy/03h8v9N4fL/TOHy/0vh8v9L4fL/SuDy/0rg8v9J4PL/SODy/0jg + 8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/0rg8v9l5fT/dej1/3To + 9f9z5/X/c+f1/3Ln9f9x5/X/cef1/2/m9f9u5vT/bub0/23m9P9s5vT/a+b0/2rm9P9p5vT/aeb0/2fm + 9P9n5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Lk9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk9P9b5PP/WuPz/0Tf + 8v8h2u//D9fu/w7W7v8M1u7/C9bu/wnW7v8I1e7/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8N1/D/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xja + 8v8Asc3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8P2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wO91v8Biaf/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Mk7D/Qtnt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Tun8/0Df8v9B3/L/Qt/y/0Pf8v9E3/L/ReDy/0Xg8v9H4PL/SODy/0jg8v9J4PL/SuDy/0rg + 8v9L4fL/TOHy/03h8v9N4fL/TuHz/0/h8/9P4fP/T+Hz/1Dh8/9Q4fP/UeLz/1Li8/9S4vP/UuLz/1Li + 8/9S4vP/UuLz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9S4vP/UuLz/1Li + 8/9S4vP/UuLz/1Hi8/9R4vP/UOHz/0/h8/9P4fP/T+Hz/07h8/9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg + 8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/UeLz/2vm9P946PX/d+j1/3bo + 9f916PX/dej1/3To9f9z5/X/c+f1/3Hn9f9x5/X/cOf1/2/m9f9u5vT/bub0/2zm9P9s5vT/aub0/2nm + 9P9p5vT/Z+b0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuT0/2Hk9P9g5PT/X+T0/17k9P9d5PT/W+T0/1vk + 8/9Z4/P/WePz/0fg8v8j2u//Dtbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8T2fD/AKjE/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AImo/wDK5P8A1O3/ANPs/wDS + 6v8A0en/ANHp/wDP5/8Az+f/AM/n/wDO5v8Azub/AM7m/wDO5v8Azub/AM/n/wDP5/8A0Oj/ANHp/wDS + 6v8A0uv/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yLe9P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/BtXu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8E1e3/Es3l/wSU + sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSIpf8zxdz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0zn+v9C3/L/Q9/y/0Tf8v9F4PL/RuDy/0fg8v9I4PL/SeDy/0rg8v9K4PL/S+Hy/0zh + 8v9N4fL/TeHy/07h8/9P4fP/T+Hz/1Dh8/9R4vP/UuLz/1Li8/9S4vP/U+Lz/1Pi8/9U4vP/VOLz/1Ti + 8/9U4vP/VOLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VOLz/1Ti + 8/9U4vP/VOLz/1Ti8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Hi8/9Q4fP/T+Hz/0/h8/9P4fP/TuHz/03h + 8v9N4fL/S+Hy/0rg8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg8v9M4PP/aeb1/3rp9v966fb/eun2/3jp + 9f946PX/d+j1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn9f9w5/X/b+b1/23m9P9m5fT/YOT0/13k + 8/9a4/P/WuPz/1zk9P9g5PT/ZeX0/2bl9P9l5fT/ZOX0/2Pl9P9i5PT/YeT0/2Dk9P9e5PT/XuT0/13k + 9P9b5PT/W+Tz/1nj8/9Z4/P/V+Pz/z/e8f8X2O//DNbu/wrW7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Dtfv/wCYtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wC/2v8A1O3/ANTt/wDS + 6/8A0ur/ANHp/wDP5/8Azub/AM3m/wDM5P8Ay+P/AMri/wDI4P8AyN//AMfe/wDF3P8AxNv/AMPa/wDC + 2f8Awdj/AMDX/wC+1f8AvtX/AL7V/wC+1f8Av9b/AMDX/wDA1/8Awdj/AMPZ/wDH3v8p1ur/TeT3/0/o + +/9P6fz/T+n8/0/p/P9P6fz/TOj7/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BdXt/xXY + 7v8X1uz/CqbA/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGvyP9N5/n/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9J5Pf/RN/y/0Xg8v9G4PL/R+Dy/0jg8v9J4PL/SuDy/0rg8v9M4fL/TeHy/03h + 8v9O4fP/T+Hz/0/h8/9Q4fP/UeLz/1Li8/9S4vP/U+Lz/1Ti8/9U4vP/VOLz/1Xi8/9V4vP/VuPz/1bj + 8/9W4/P/VuPz/1fj8/9X4/P/V+Pz/1fj8/9X4/P/WOPz/1jj8/9Y4/P/V+Pz/1fj8/9X4/P/V+Pz/1fj + 8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li8/9R4vP/UOHz/0/h + 8/9P4fP/TuHz/03h8v9N4fL/TOHy/0vh8v9K4PL/SeDy/0ng8v9g5PT/e+n2/3zp9v986fb/e+n2/3rp + 9v966fb/een2/3jo9f946PX/duj1/3Xo9f916PX/cuj1/2Ti9f9c1fT/Usjz/0+99P9NsfT/UKv0/1Ko + 9f9TpfX/VKT1/1Ol9f9RqPX/Tqv0/0ux8/9JvPP/S8bz/0/T8/9V4PP/YOT0/2Lk9P9g5PT/YOT0/17k + 9P9e5PT/XeT0/1vk9P9b5PP/WePz/1jj8/9X4/P/VOLz/y7c8P8N1u7/Cdbu/wjV7v8H1e3/BdXt/wPV + 7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8d3PL/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/wjW7v8AiKX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCzzv8A1O3/ANTt/wDU + 7f8A0uv/ANLq/wDR6f8Az+f/AM7m/wDN5f8AzOT/AMvj/wDJ4f8AyOD/AMjf/wDH3v8Axdz/AMTb/wDD + 2f8Awtn/AMLZ/wDD2f8Aw9r/AMXc/wDH3v8AyOD/AMri/wDN5f8Az+f/ANLq/wDU7f8A1O3/NOH3/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pl+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXV + 7f8X2O//Gdjv/xrY7/8Su9P/AYek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0bd + 8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/SOP0/0bg8v9H4PL/SODy/0ng8v9K4PL/S+Hy/0zh8v9N4fL/TuHz/0/h + 8/9P4fP/UOHz/1Hi8/9S4vP/UuLz/1Pi8/9U4vP/VOLz/1Xi8/9W4/P/VuPz/1bj8/9X4/P/WOPz/1jj + 8/9Z4/P/WePz/1nj8/9Z4/P/WePz/1nj8/9a4/P/WuPz/1rj8/9a4/P/WuPz/1rj8/9a4/P/WePz/1nj + 8/9Z4/P/WePz/1nj8/9Z4/P/WOPz/1jj8/9X4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9U4vP/U+Lz/1Li + 8/9S4vP/UeLz/1Dh8/9P4fP/T+Hz/07h8/9N4fL/TOHy/1Di8v9y5/X/f+n2/3/p9v9+6fb/fen2/3zp + 9v986fb/e+n2/3rp9v966fb/eOn1/3Tn9f9j3/T/Vcnz/06x9P9So/T/U6L0/1Oi9P9TovT/U6L0/1Oi + 9P9TovT/U6L0/1Oi9P9TovT/U6L0/1Oi9P9TovT/UqLz/1Ki8/9SovP/UaPz/0ew8v9HxvL/Tdzz/13k + 9P9g5PT/XuT0/17k9P9d5PT/W+Tz/1rj8/9Z4/P/WOPz/1fj8/9W4vP/Qd/y/xLX7v8J1u7/B9Xt/wbV + 7f8E1e3/A9Xt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07o/P8Cwt7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7gCmwv8A1O3/ANTt/wDU + 7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDO5v8AzeX/AM3l/wDM5P8AzeX/AM3l/wDN5v8Azub/AM/n/wDQ + 6P8A0ur/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk + +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8G1e3/GNjv/xrY7/8c2e//Hdnv/xrL4v8FkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Giqj/Oczj/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8v9I4PL/SeDy/0rg8v9L4fL/TOHy/03h8v9O4fP/T+Hz/1Dh + 8/9R4vP/UuLz/1Li8/9T4vP/VOLz/1Ti8/9V4vP/VuPz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WePz/1rj + 8/9a4/P/W+Pz/1vj8/9b4/P/W+Pz/1vj8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk + 8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj8/9a4/P/WePz/1nj8/9Z4/P/WOPz/1fj8/9W4/P/VuPz/1Xi + 8/9U4vP/VOLz/1Pi8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/1rj9P9+6fb/ger2/4Hq9v+A6fb/f+n2/3/p + 9v9+6fb/fOn2/3zp9v976fb/c+f1/1zT9P9PtPL/UqLz/1Kh8/9SofP/UqHz/1Kh8/9RofL/UaHy/1Gh + 8v9RofL/UaHy/1Gh8v9RofL/UaHy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Ch + 8v9GsfL/Rs7y/1fi8/9e5PT/XeT0/1zk9P9b5PP/WuPz/1nj8/9X4/P/V+Pz/1bi8/9P4fP/Gtnv/wnW + 7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zHh9v9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9D5fn/AKvG/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA1Oz/ANTt/wDU + 7f8A1O3/ANPs/wDT7P8A0uv/ANPs/wDT7P8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDP + 6f8Avtn/AKbC/wDH4f8A0er/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/B9Xt/xrY7/8c2e//Hdnv/x/Z7/8g2e//H9Xr/wygu/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wGEov8ntc7/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03n+v9I4PL/SuDy/0vh8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi + 8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj8/9W4/P/V+Pz/1jj8/9Z4/P/WePz/1rj8/9b4/P/W+Pz/1vj + 8/9c5PP/XOTz/13k9P9d5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k + 9P9e5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj + 8/9X4/P/VuPz/1bj8/9V4vP/VOLz/1Ti8/9S4vP/UuLz/2Hl9P+B6fb/g+r3/4Lq9/+B6vb/ger2/3/p + 9v9/6fb/fun2/33p9v946Pb/Xs7z/0+r8f9QoPH/UKDx/1Cg8f9QoPH/UJ/x/1Cf8f9Qn/H/UJ/x/1Cf + 8f9Qn/H/UJ/x/1Cf8f9Qn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/D/T5/w/0+f + 8P9Pn/D/T5/w/0+f8P9IqO//R8nx/1ni8/9c4/T/W+P0/1rj8/9Y4/P/WOPz/1bj8/9V4vP/VOLz/1Hi + 8/8g2vD/CNXu/wfV7f8F1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8+5fn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/NuL3/wCTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqCBANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCz + zf8AiKb/AIOh/wCDof8Ag6H/AIOh/wCKp/8Al7T/AKTA/wCxzP8Avtj/AMrk/wDU7P8A1O3/ANTt/wDU + 7f8A1O3/POT5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zji+P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wbV7f8b2e//Hdnv/x/Z7/8g2e//Idrv/yPa7/8k2vD/FbTO/wGEo/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xafuv9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5ff/SuDy/0zh8v9N4fL/TuHz/0/h8/9Q4fP/UeLz/1Li + 8/9T4vP/VOLz/1Xi8/9W4/P/VuPz/1fj8/9Y4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XeT0/17k + 9P9e5PT/XuT0/1/k9P9f5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk + 9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/W+Pz/1vj + 8/9a4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/2fl9P+C6vb/g+r2/4Lq9v+B6fb/gen2/3/p + 9v9/6fb/fun2/3zp9v9m1vT/Uarw/0+e8P9PnvD/T57w/0+e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e + 8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06d7/9One//Tp3v/06d + 7/9One//Tp3v/06d7/9One//TZ3v/02d7/9Kp+//StDy/1jj8/9Y4/T/V+Pz/1bi8/9U4vP/U+Lz/1Li + 8/9R4vP/T+Hz/yXb8P8H1e3/BtXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S+j7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/ynb8P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgOwDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC2 + 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/AIyq/wCa + t/8AudP/ANLq/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P844vj/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8E1e3/HNnv/x7Z7/8g2e//Idrv/yPa7/8k2vD/Jtrw/yjb8P8gx97/BYyp/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY+s/z7T6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOP0/03h8v9N4fL/T+Hz/0/h8/9R4vP/UuLz/1Pi + 8/9U4vP/VeLz/1bj8/9W4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/XOTz/13k9P9e5PT/XuT0/1/k + 9P9g5PT/YOT0/2Dk9P9h5PT/YeT0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9j5fT/Y+X0/2Pl + 9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k + 9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Y4/P/V+Pz/2bm9P+C6vb/gur2/4Lq9v+B6vb/ger2/3/p + 9v9/6fb/fun2/3jk9f9Xt/H/TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d + 7/9Nne//TJzu/0yc7v9Mne7/VJ/r/1qh6f9couj/WqHp/1Sf6/9Mne7/TJzu/0yc7v9MnO7/TJzu/0yc + 7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9Js+//Ut3z/1Tj8/9U4vP/U+Lz/1Hi + 8/9Q4vP/T+Hz/07h8/9L4fP/Idrw/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Dtfv/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cxd3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCA + gAIA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDA + 2/8AhaP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCZtf885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/A9Xt/x7Z7/8f2e//Idrv/yPa7/8k2vD/Jtrw/yfb8P8p2/D/K9vw/yjU + 6f8Mm7b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ChaP/Lr3V/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03h8v9O4fP/T+Hz/1Hi8/9S4vP/U+Lz/1Ti + 8/9V4vP/VuPz/1fj8/9Y4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Dk + 9P9h5PT/YuX0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl + 9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl9P9k5fT/ZOX0/2Pl9P9j5fT/YuX0/2Ll9P9h5PT/YeT0/2Dk + 9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/2bl9P+D6vb/g+r2/4Lq9v+C6vb/ger2/4Dq + 9v9/6vb/f+r2/27X8/9Po+//TJzu/0yc7v9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0uc + 7f9gouX/fK3b/5i20f+pvcv/usPG/73ExP+9xMT/vcTE/73ExP+9xMT/usPG/6q9y/+XttH/fK3b/2Ci + 5f9LnO3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprs/0ih7P9L0vH/UuLy/1Hi + 8v9Q4vL/T+Ly/03h8v9N4fL/S+Hy/0nh8v8b2O//BtXt/wTV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd + 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DqK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oaAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDI + 4/8Aiaj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Otzx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrk+P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb8P8p2/D/Ktvw/yzb + 8P8t3PD/Ltvv/xivyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8bpsD/S+T4/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/n+v9P4fP/UOHz/1Li8/9S4vP/VOLz/1Ti + 8/9W4/P/VuPz/1jj8/9Z4/P/WuPz/1vj8/9c5PP/XeT0/17k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll + 9P9i5fT/Y+X0/2Tl9P9l5fT/ZeX0/2Xl9P9m5fT/ZuX0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl + 9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2bl9P9l5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Pl + 9P9i5fT/YuX0/2Hk9P9g5PT/YOT0/17k9P9e5PT/XeT0/2Xl9P+C6vb/g+r2/4Pq9v+C6vb/ger2/4Dq + 9v+A6fb/f+n2/2bL8v9Km+3/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/X6Ll/4qy + 1v+xwMj/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/scDI/4uy1f9fouT/SZnr/0mZ6/9Jmev/SZnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/R5rr/0bG + 7/9P4fL/TuHy/03h8v9L4fL/SuDy/0ng8v9H4PL/RODy/xXX7v8F1e3/BNXt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f804ff/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKJVAAAAAADP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDN5v8Azeb/AM3m/wDN5v8Azub/AM3m/wDK + 4f8Ajqz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/zbT6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5fn/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/H9nv/yLa7/8k2vD/Jdrw/yfb8P8p2/D/Ktvw/yvb + 8P8t3PD/L9zw/zDc8P8y3PH/JMPZ/wSJp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wyT + sP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5ff/UeLz/1Li8/9T4vP/VOLz/1Xi + 8/9W4/P/WOPz/1nj8/9a4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Hk9P9i5fT/Y+X0/2Tl + 9P9l5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2jm9P9p5vT/aeb0/2rm9P9q5vT/aub0/2rm + 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9p5vT/aeb0/2jm9P9o5vT/Z+X0/2fl9P9n5fT/ZuX0/2Xl + 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/YOT0/2Pl9P+B6vb/hOr3/4Pq9v+C6vb/ger2/4Hq + 9v+A6vb/fur2/1/A8f9Imev/SJnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/SJnr/0iY6/9iouL/nbjP/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/vsXF/77F + xf++xcX/v8bG/7/Gxv+/xsb/wMbG/6C5z/9ioeL/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX + 6v9Gl+n/RLru/0vg8v9L4fL/SeHy/0jg8v9H4PL/RuDy/0Tg8v893vL/Ddbt/wTV7f8C1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrY7f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H0AICqDAAAAAAAzOT/AMvj/wDK4v8AyOD/AMjf/wDH3v8Ax97/AMjg/wDK4v8AzeX/AM/n/wDN + 5v8Aka//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xy+L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Q+X5/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x3Z7/8k2vD/Jdrw/yba8P8p2/D/Ktvw/yvb + 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f8v0ef/C5Wx/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/BIil/zPF3P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/UeT1/1Li8/9U4vP/VeLz/1bj + 8/9X4/P/WePz/1nj8/9b4/P/W+Pz/13k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl + 9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/a+b1/2vm9f9s5vX/bOb1/2zm + 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/a+b1/2vm9f9r5vX/aub0/2rm9P9q5vT/aeb0/2jm + 9P9n5fT/Z+X0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Pl9P976fb/hOr3/4Pq9/+D6vf/gur3/4Dp + 9v+A6fb/f+n2/13A7/9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Gl+n/Rpfp/1Gb5f+QstL/vMTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xcX/vsXF/77Fxf+/xsb/v8bG/7/Gxv/Ax8f/wMfH/8DH + x//Bx8f/wcjI/8HIyP/CyMj/wsnJ/8LJyf/Dycn/wsnJ/5W21P9SmuX/RZbp/0WW6f9Flun/RZbp/0WV + 6P9Flej/RZXo/0WV6P9Auez/SODy/0fg8v9G4PL/Rd/y/0Pf8v9C3/L/Qd/y/zLc8P8G1e3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Ddfw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8mudL/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhogAAAAAAAAAAAM3l/wDN5f8Azeb/AM/n/wDR6f8A0uv/ANTt/wDU7f8A1O3/ANTt/wDQ + 6P8AlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/LMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn + +v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8c2e//JNrw/yba8P8o2/D/Kdvw/yvb + 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f823fH/N93x/zfc7v8YqcH/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Ia/I/03n+f9P6fz/T+n8/0/p/P9P6fz/T+n8/1Pi8/9U4vP/VuPz/1bj + 8/9Y4/P/WePz/1rj8/9b4/P/XeT0/17k9P9f5PT/YOT0/2Hk9P9i5fT/YuX0/2Tl9P9l5fT/ZeX0/2bl + 9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/23m9f9u5/X/buf1/27n + 9f9u5/X/b+f1/2/n9f9v5/X/b+f1/27n9f9u5/X/buf1/27n9f9t5vX/beb1/2zm9f9s5vX/bOb1/2vm + 9f9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P916PX/hOv2/4Tr9v+C6vb/gur2/4Hq + 9v+B6vb/f+r2/2DF8P9Flun/RZbp/0WW6f9Flun/RZbp/0WV6P9Flej/RZXo/2uk3f+zwcf/vcTE/77F + xf++xcX/vsXF/7/Gxv+/xsb/v8bG/8DGxv/Ax8f/wMfH/8HHx//ByMj/wcjI/8LIyP/CyMj/wsnJ/8PJ + yf/Dycn/w8rK/8TKyv/Eysr/xMrK/8XLy//Fy8v/xcvL/8bMzP/GzMz/vcjN/2+m3f9DlOf/Q5Tn/0OU + 5/9DlOf/Q5Tn/0OU5/9DlOf/Q5Tn/z6+7f9F4PL/RODy/0Lf8v9B3/L/QN/y/z7f8v893vL/Itrv/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yPd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/E5u2/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCBo0UAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO + 5v8Ak7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HOAICZCgCAnRoAg6JCAIKhagCD + oZIAg6G7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yjA1/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9M6Pv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/F9jv/yba8P8n2/D/Kdvw/yvb + 8P8s2/D/Ltzw/zDc8P8x3PD/Mtzx/zXd8f823fH/N93x/zne8f873vH/PN7x/yi91P8Ch6T/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbX/Rt3x/0/p/P9P6fz/T+n8/1Dn+v9U4vP/VuPz/1fj + 8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k9P9g5PT/YOT0/2Ll9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2fl + 9P9o5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9v5/X/cOf1/3Dn + 9f9w5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cOf1/3Dn9f9w5/X/b+f1/2/n9f9v5/X/b+f1/27n + 9f9t5vX/bOb1/2zm9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9s5vT/hOr2/4Xq9v+D6vb/g+r2/4Lq + 9v+B6vb/gOr2/2rM8f9ElOf/RJTn/0SU5/9DlOf/Q5Tn/0OU5/9DlOf/RZXm/4ux1P+/xsb/wMbG/8DH + x//Ax8f/wcfH/8HIyP/ByMj/wsjI/8LIyP/Cycn/w8nJ/8PJyf/Dysr/xMrK/8TKyv/Eysr/xcvL/8XL + y//Fy8v/xszM/8bMzP/GzMz/x83N/8fNzf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8nPz//Jz8//lbfW/0SU + 5f9Ck+b/QpPm/0KT5v9Ck+b/QZLm/0GS5v9Bkub/QcTu/0Lf8v9C3/L/QN/y/z/f8v893/L/PN/y/zre + 8f8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f875Pj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/wKFpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoeQAgIACAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDJ + 4/8Aj6z/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8juNH/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPX7v8n2/D/Kdvw/yvb + 8P8s2/D/Ldzw/y/c8P8x3PD/Mtzx/zTd8f823fH/N93x/zne8f863vH/PN7x/z7e8f8/3/H/NtDk/wqR + rf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP85zeP/T+n8/0/p/P9S5vf/VuPz/1jj + 8/9Z4/P/WuPz/1vj8/9d5PT/XuT0/1/k9P9g5PT/YuX0/2Ll9P9k5fT/ZeX0/2bl9P9n5fT/Z+X0/2nm + 9P9q5vT/aub0/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9w5/X/cef1/3Hn9f9x5/X/cuf1/3Ln + 9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9y5/X/cuf1/3Hn9f9x5/X/cef1/3Hn + 9f9w5/X/b+f1/2/n9f9u5/X/beb1/2zm9f9s5vX/a+b1/2rm9P9p5vT/f+r2/4Xq9v+E6vb/g+r2/4Lq + 9v+C6vb/ger2/3DO5f9CleX/QpPm/0KT5v9Ck+b/QpPm/0KT5v9Ck+b/SJXk/5250P/CyMj/wsjI/8LJ + yf/Dycn/w8nJ/8PKyv/Eysr/xMrK/8TKyv/Fy8v/xMrK/8LJyf/Ax8f/v8bG/77Fxf+9xMT/vcTE/73E + xP++xcX/v8bG/8HIyP/Fy8v/yM3N/8nPz//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8vR0f/M0dH/zNHR/8zS + 0v+qwtb/SJXj/0CR5P9AkeT/QJHk/0CR5P9AkeT/QJHk/0CU5f9A0e//P9/x/z3e8f883vH/PN7x/zne + 8f843fH/Kdvw/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8F1u7/Tej8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrP5f8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDG + 3/8Ai6n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HpAIahJgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Gq7I/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zXH3v8DmbX/AJOw/wCmwf8Azuf/ANTt/wDU7f8A1O3/ANTt/wDU7f8O1u7/KNvw/yrb + 8P8r2/D/Ldzw/y/c8P8w3PD/Mtzx/zTd8f813fH/N93x/zne8f863vH/PN7x/z3e8f8/3/H/Qd/y/0Lf + 8v8/2u7/F6K8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/ye2z/9O6Pv/VOX1/1jj + 8/9Z4/P/W+Pz/1zk8/9e5PT/XuT0/2Dk9P9h5PT/YuX0/2Pl9P9l5fT/ZeX0/2fl9P9n5fT/aeb0/2rm + 9P9r5vX/bOb1/2zm9f9t5vX/b+f1/2/n9f9w5/X/cef1/3Hn9f9y5/X/cuf1/3Pn9f9z5/X/c+f1/3To + 9f906PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dOj1/3To9f9z5/X/c+f1/3Pn + 9f9z5/X/cuf1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m9f9s5vX/dej2/4Xr9v+F6/b/hOv2/4Pq + 9v+C6vb/ger2/37l9f84dqz/OYHM/0CR5f9AkeX/QJHl/0CR5f9AkeX/SJTk/6m+z//Eysr/xMrK/8XL + y//Fy8v/xcvL/8bMzP/GzMz/xszM/8fNzf/Eysr/wMbG/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/x83N/83S0v/N0tL/zdPT/87T0//O09P/ztTU/8/U + 1P/P1NT/z9TU/7fI1v9GleL/PpDj/z6Q4/8+kOP/PpDj/z6Q4/8+j+P/P5rl/z/a8P873vH/Ot7x/zne + 8f833vH/Nt7x/zDc8P8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H9AICjJAAAAAAAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC5 + 0/8AhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfLQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISiNACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKguv9P6fz/T+n8/0/p + /P9P6fz/Rt3x/xegu/8Ag6H/AIOh/wCDof8Ag6H/AJaz/wDT7P8A1O3/ANTt/wDU7f8A1O3/CNXu/ynb + 8P8r2/D/Ldzw/y7c8P8w3PD/Mtzx/zPd8f813fH/N93x/zjd8f853vH/PN7x/z3e8f8+3vH/QN/y/0Lf + 8v9D3/L/ReDy/0bg8v8ot87/AYSj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fp+6/1Db + 7v9Z4/P/W+Pz/1zk8/9e5PT/X+T0/2Dk9P9i5fT/YuX0/2Tl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm + 9f9s5vX/beb1/27n9f9v5/X/b+f1/3Hn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/duj1/3bo + 9f926PX/duj1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3bo9f926PX/duj1/3bo + 9f916PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn9f9v5/X/b+f1/4Pq9v+F6vf/her3/4Tq + 9v+D6vb/gen2/3LV+f9Zr+H/JleJ/zBurf8/kOP/P5Dj/z+Q4/8/kOP/QZLj/6W90f/GzMz/x83N/8fN + zf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8bMzP+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/y9HR/9DV1f/Q1dX/0dbW/9HW + 1v/R1tb/0tfX/9LX1//S19f/s8fX/0GQ4v89juL/PY7i/z2O4v89juL/PY7i/z2O4v9Asej/Od7x/zjd + 8f833fH/Nd3x/zPd8f8t2/D/Jtrw/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zPi9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/C5Gu/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOitwAAAAAAAAAAAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTs/wCp + xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAISiNAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoRsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj63/T+n8/0/p + /P9O6Pv/Lb3V/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Awtz/ANTt/wDU7f8A1O3/ANTt/wPV + 7f8r2/D/LNvw/y3c8P8w3PD/Mdzw/zLc8f813fH/Nt3x/zfd8f853vH/O97x/zze8f8+3vH/QN/y/0Hf + 8v9D3/L/ReDy/0bg8v9I4PL/SeDy/zrM4P8Ijar/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Lj6v/TtXn/13k9P9e5PT/X+T0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2zm + 9f9s5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9z5/X/c+f1/3To9f916PX/duj1/3bo9f936PX/d+j1/3jo + 9f946PX/eOj1/3no9v956Pb/eej2/3no9v966fb/eun2/3rp9v956Pb/eej2/3no9v956Pb/eOj1/3jo + 9f946PX/d+j1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Pn9f9x5/X/cef1/3jo9v+G6/f/hev3/4Tq + 9/+E6vf/gur2/3/l9/9au/z/M3q7/yRVh/8mWY3/PY7g/z2P4v89juL/PY7i/5K11v/Jzs7/yc/P/8nP + z//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Hzc3/0tfX/9PY + 2P/U2Nj/1NnZ/9TZ2f/V2dn/1dra/9Xa2v+fvdr/O4zg/zuM4P87jOD/O4zg/zuM4P87jOD/PIzg/z3M + 7f823fH/Nd3x/zPd8f8x3fH/Kdvw/ynb8P8P1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f9K5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qtjt/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoUwAAAAAAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/AMvk/wCZ + tf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfNQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/07p + /P9C2O3/Epq1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/J9vw/y3c8P8v3PD/MNzw/zLc8f803fH/Nd3x/zfd8f853vH/Ot7x/zze8f8+3vH/P9/x/0Hf + 8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg8v9N4fL/SNns/xWct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/w+Srv9d4/P/YOT0/2Hk9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm + 9f9u5/X/b+f1/3Dn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3bo9f926PX/d+j1/3jo9f946PX/eej2/3no + 9v966fb/e+n2/3vp9v976fb/e+n2/3vp9v986fb/fOn2/3zp9v986fb/fOn2/3vp9v976fb/e+n2/3vp + 9v976fb/eun2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3bo9f906PX/c+f1/3Pn9f+E6vb/h+v3/4br + 9/+F6/f/hOv3/4Lq9/9u0fn/SbT8/zKCw/8kVIb/I1SG/zR7xf87jeH/O43h/3Cl2//L0ND/y9HR/8vR + 0f/M0dH/zNLS/83S0v/N0tL/zNHR/8HHx/+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/8LJ + yf/U2Nj/1tvb/9fb2//X29v/19zc/9jc3P/Y3Nz/2Nzc/3us3f85i9//OYvf/zmL3/85i9//OYvf/zmL + 3/89nuT/Ndzx/zPc8f8x3PH/MNzx/yfb8P8n2/D/INnv/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8X2/H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq5 + 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDod4AgIACAAAAAAAAAAAAAAAAAAAAAADU7f8A1O3/ALfR/wCI + pv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HiAIafKAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8jtc3/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wDO5v8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yDZ7/8u3PD/MNzw/zLc8f8z3fH/Nd3x/zfd8f843fH/Ot7x/zze8f893vH/P9/x/0Hf + 8v9C3/L/RN/y/0Xg8v9H4PL/SeDy/0rg8v9M4fL/TeHy/0/h8/9Q4fL/KLHJ/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/TtLl/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9q5vT/bOb1/23m + 9f9v5/X/b+f1/3Hn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f946PX/eOj1/3no9v966fb/e+n2/3vp + 9v986fb/fOn2/33p9v996fb/fen2/33p9v9+6fb/fun2/37p9v9+6fb/fun2/37p9v9+6fb/fen2/33p + 9v996fb/fen2/3zp9v976fb/e+n2/3vp9v966fb/eej2/3jo9f936PX/duj1/3bo9f956fX/h+r2/4fq + 9v+F6vb/her2/4Pq9v+C6fb/V7r7/z+v/P85oen/I1OF/yNThf8qZaP/Oovf/0eS3//CzdT/ztPT/87T + 0//O1NT/z9TU/8/U1P/P1NT/zdLS/7/Gxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/wMfH/9Xa2v/Z3d3/2t7e/9re3v/a3t7/297e/9vf3//S2+D/S5Tf/ziK3v84id7/OIne/ziJ + 3v84id7/OYne/znE7P8w3PD/L9zw/y3c8P8k2vD/JNrw/yTa8P8H1e3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAzOb/AJ26/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAISeHQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqHXAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Aoak/wC61f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8W2O//L9zw/zHc8P8y3PH/Nd3x/zbd8f843fH/Od7x/zve8f893vH/Pt7x/0Df + 8v9C3/L/Q9/y/0Xg8v9H4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/UuLz/1Ti8/88x9v/Boqn/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/1LV5/9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/bOb1/27n + 9f9v5/X/cOf1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo9f946PX/eun2/3vp9v976fb/fOn2/33p + 9v996fb/fun2/37p9v9/6fb/f+n2/3/p9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/3/p + 9v9/6fb/f+n2/3/p9v9+6fb/fen2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f946PX/gOr2/4jr + 9v+G6/b/her2/4Tq9v+D6vb/ddr4/0iz/P8+r/z/Pq/8/yZlnP8hUoT/IlSH/zeH2f+cvNr/0NXV/9DV + 1f/R1tb/0dbW/9HW1v/S19f/0NXV/8DGxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+6t7H/spqD/6yDYf+peFH/pm9D/6dxRv+qe1X/rYho/7Sh + kP+7vrv/vcTE/73ExP/Ax8f/2d3d/9zg4P/d4OD/3eDg/93h4f/e4eH/3uHh/7DJ4P82iNz/Nojc/zaI + 3P82iNz/Nojc/zaI3P85m+H/Ltzw/y3b8P8q2/D/Idrv/yHa7/8h2u//E9fu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9H3/P/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6HzAICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAIek/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKG8AICcEgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/E5y2/0PZ7v8U2fH/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/DNbu/zDc8P8y3PH/NN3x/zXd8f833fH/Od7x/zre8f883vH/Pt7x/0Df + 8v9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9L4fL/TeHy/0/h8/9Q4fP/UuLz/1Ti8/9V4vP/VuPz/03X + 6f8SlrL/AIOh/wCDof8Ag6H/AIOh/xWXs/9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9r5vX/bOb1/27n + 9f9v5/X/cef1/3Ln9f9z5/X/dOj1/3bo9f926PX/eOj1/3jo9f966fb/e+n2/3vp9v996fb/fen2/37p + 9v9/6fb/f+n2/4Dq9v+B6vb/ger2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq + 9v+C6vb/gur2/4Lq9v+B6vb/gOr2/3/p9v9/6fb/f+n2/33p9v996fb/fOn2/3vp9v976fb/eej2/4bq + 9v+H6/b/huv2/4Xr9v+D6vb/g+r2/1/C+f8/r/z/Pq/8/z6v/P8xh8f/IVKE/yBRhP9IgLn/0NbY/9LX + 1//T2Nj/09jY/9TY2P/U2Nj/1NnZ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/7izqv+sgmH/pGY2/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pmo7/7CRdv+7vbn/vcTE/8bMzP/f4uL/3+Pj/+Dj4//g4+P/4OPj/+Dk5P/g4+T/Y6De/zWH + 2/81h9v/NYfb/zSG2/80htv/NYbb/zHL7P8q2/D/Jtvw/x7Z7/8e2e//Htnv/xzZ7/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Lb3U/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAKqqAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoJcAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiaf/Lr/W/0/p/P9P6fz/Qub5/wLV + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f8x3PD/M93x/zXd8f833fH/ON3x/zne8f883vH/Pd7x/z/f + 8f9B3/L/Q9/y/0Tf8v9F4PL/SODy/0ng8v9K4PL/TeHy/07h8/9P4fP/UuLz/1Pi8/9U4vP/VuPz/1jj + 8/9Z4/P/WeHw/yquxv8GiKb/AYSi/x+iu/9Y2+v/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Ln9f9z5/X/dej1/3bo9f936PX/eOj1/3rp9v976fb/fOn2/33p9v996fb/f+n2/3/p + 9v+A6vb/ger2/4Lq9v+C6vb/g+r2/4Pq9v+E6vb/hOr2/4Tq9v+E6vb/her2/4Xq9v+F6vb/her2/4Tq + 9v+E6vb/hOr2/4Tq9v+E6vb/g+r2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/f+n2/37p9v996fb/fOn2/37p + 9v+I6/f/h+r3/4bq9v+F6vb/her2/4Dp9v9KtPv/Pq/8/z6v/P8+r/z/O6bx/yBShf8gUIP/b4KU/9XZ + 2f/V2tr/1tra/9ba2v/W2tr/19vb/8zS0v+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/77ExP++xcX/vcLB/66KbP+kZjb/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pWo8/7Oei/+9xMT/0tfX/+Ll5f/i5eX/4+bm/+Pm5v/j5ub/5Obm/77S + 5P8zhdr/M4Xa/zOF2v8zhdr/M4Xa/zOF2v8zreT/KNrw/yLZ8P8b2e//G9nv/xvZ7/8b2e//BdXt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f804vf/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xKatf8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCAoyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofUAgqJaAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqKBAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/R97y/0/p/P9P6fz/T+n8/0/p + /P8k3vT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ktvw/zTd8f813fH/N93x/zne8f873vH/PN7x/z7e + 8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TOHy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1fj + 8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Hk9P9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3bo9f946PX/eej2/3vp9v976fb/fen2/33p9v9/6fb/f+n2/4Hq + 9v+C6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr9/+H6/f/h+v3/4fr9/+H6/f/h+v3/4fr + 9/+H6/f/h+v3/4fr9/+G6/b/huv2/4Xq9v+E6vb/hOr2/4Pq9v+C6vb/gur2/4Hq9v+A6vb/f+n2/37p + 9v+D6vb/iev3/4jr9/+G6vf/her2/4Tq9v9z2vj/Q7H8/z6v/P8+r/z/Pq/8/z6v/P8obqj/LViD/36C + g//FyMj/2Nzc/9jc3P/Y3d3/2d3d/9fb2/+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/77F + xf+/xcX/v8bG/8DHx//Bx8f/wMG+/6p5Uv+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/r49z/7/Gxv/h5OT/5ejo/+Xo6P/m6Oj/5unp/+bp + 6f/l6er/V5nd/zGE2P8xhNj/MYTY/zGE2P8xhNj/M43b/yfb8P8f2e//Gdjv/xnY7/8Z2O//Gdjv/wzW + 7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0bd8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocYAg58lAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh8gCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/weMqf81x97/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/S+j7/wjX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/Z7/813fH/N93x/zjd8f863vH/PN7x/z7e + 8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UOHz/1Li8/9U4vP/VeLz/1fj + 8/9Z4/P/WuPz/1zk8/9e5PT/X+T0/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v986fb/fen2/3/p9v9/6fb/ger2/4Lq + 9v+C6vb/hOr2/4Tq9v+F6vb/huv2/4fr9/+H6/f/iOv3/4jr9/+J6/f/iev3/4nr9/+J6/f/iev3/4nr + 9/+J6/f/iev3/4nr9/+J6/f/iOv3/4jr9/+H6/f/h+v3/4br9v+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq + 9v9/6fb/h+r3/4jr9/+H6/f/huv3/4Xr9/+E6vb/YMb3/z6v/P8+r/z/Pq/8/z6v/P8+r/z/NZXa/1Vs + g/+ChIT/oqWl/9ve3v/b39//29/f/9zf3//L0dH/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/v8XF/8DG + xv/Bx8f/wcjI/8LJyf/Dycn/w8nI/61/W/+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+ymIH/0NXV/+fq6v/o6ur/6Ovr/+jr + 6//p6+v/6ezs/6bF4v8wgtf/MILX/zCC1/8vgtf/L4LX/y+C1/8ny+z/G9nv/xbY7/8W2O//Ftjv/xbY + 7/8Q1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8ru9P/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ84AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoHkAqqoDAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnyAAhKG4AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/x6qxP9K4vb/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P814vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8S1+7/Nt3x/zfd8f853vH/O97x/zze + 8f8+3vH/QN/y/0Lf8v9D3/L/ReDy/0fg8v9J4PL/SuDy/03h8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1bj + 8/9Y4/P/WuPz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p9v+A6vb/gur2/4Lq + 9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iev3/4rr9/+K6/f/i+v3/4vr9/+L6/f/i+v3/4vr + 9/+L6/f/i+v3/4vr9/+L6/f/i+v3/4vr9/+K6/f/iev3/4nr9/+I6/f/h+v3/4fr9/+G6/b/hOr2/4Tq + 9v+C6vb/gur2/4nr9/+J6/f/iOv3/4fr9/+F6/f/hOv3/062+P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z6v + +/9uhZf/hIaG/4WHh//T19f/3uHh/97h4f/c4OD/v8XF/73ExP+9xMT/vcTE/73ExP+/xsb/wMfH/8HI + yP/Cycn/w8rK/8TKyv/Fy8v/xszM/7KRdP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGY1/7eqm//n6ur/6+3t/+vt + 7f/r7e3/7O7u/+zu7v/c5Ov/LoHW/y6A1v8ugNb/LoDW/y6A1v8ugNb/KbTl/xjY7v8T1+7/E9fu/xPX + 7v8T1+7/Etfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCDof8Ag6H/AIOitwCGpCoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCEonAAg6H0AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Gu/zrO5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ8f8A1O3/ANTt/wDU7f8A1O3/BNXt/zbd8f843fH/Ot7x/zze + 8f8+3vH/P9/x/0Hf8v9D3/L/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Hi8/9S4vP/VOLz/1bj + 8/9Y4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f916PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fun2/3/p9v+B6vb/gur2/4Pq + 9v+E6vb/huv2/4fr9/+H6/f/iev3/4nr9/+K6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+O7Pf/juz3/47s + 9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iuv3/4nr9/+J6/f/h+v3/4fr + 9/+F6vb/hOr2/4Tq9v+K6/f/iev3/4fq9/+G6vf/her3/4Dp9/9Esvv/Pq/8/z6v/P8+r/z/Pq/8/z6v + /P8+r/z/XZ3H/4WHh/+Fh4f/r7Gx/+Dj4//g5OT/0tfX/73ExP+9xMT/vcTE/77Fxf/Axsb/wcjI/8PJ + yf/Eysr/xcvL/8bMzP/Hzc3/yM7O/8C1qf+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/oWMz/25E + I/8/JxT/JxgM/y0cDv9LLhj/gU8p/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+nbkL/19jV/+3v + 7//t7+//7vDw/+7w8P/u8PD/7/Dx/1CU2f8sf9T/LH/U/yx/1P8sf9T/LH/U/yub3P8T2O7/ENfu/xDX + 7v8Q1+7/ENfu/xDX7v8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW7/9O6Pz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/RNrv/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISgnwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOh/wCDof8AgqDVAIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDockAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhaL/JLHL/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/AtTt/wDU7f8A1O3/ANTt/wDU7f8q2/D/Od7x/zve + 8f883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9H4PL/SeDy/0rg8v9N4fL/TuHz/1Dh8/9S4vP/VOLz/1Xi + 8/9X4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9h5PT/YuX0/2Xl9P9m5fT/Z+X0/2rm9P9r5vX/bOb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq + 9v+F6vb/h+v3/4fr9/+J6/f/iev3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/4/s9/+P7Pf/kOz3/5Ds + 9/+Q7Pf/kOz3/5Ds9/+Q7Pf/kOz3/5Ds9/+P7Pf/j+z3/47s9/+O7Pf/jez3/4zs9/+L6/f/iuv3/4nr + 9/+I6/f/h+v3/4br9v+H6vb/iuv2/4jr9v+H6/b/hur2/4Xq9v933/f/QbD8/z6v/P8+r/z/Pq/8/z6v + /P8+r/z/Pq/8/0Gt9v+Di5D/h4mJ/42Pj//e4eH/4+bm/8bMzP+9xMT/vcTE/7/Gxv/Bx8f/wsnJ/8TK + yv/Fy8v/x8zM/8jOzv/Jz8//ys/P/8vQ0P+uf1r/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/jVct/x0S + Cf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP88JRP/nmEy/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7id + hv/w8fH/8PLy//Hy8v/x8vL/8fPz//Lz8/+Ot9//K37T/yt+0/8rftP/Kn3T/yp90/8rh9b/D9bu/w3W + 7v8N1u7/Ddbu/w3W7v8N1u7/BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8i3vT/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yi3z/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoXIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDodMAgqFcAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkpIHAIKigwCDofkAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8PlrL/P9Tq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yXe9P8A1O3/ANTt/wDU7f8A1O3/Gtjv/zne + 8f883vH/Pt7x/z/f8f9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/U+Lz/1Ti + 8/9W4/P/WOPz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n + 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq + 9v+G6/b/h+v3/4jr9/+J6/f/i+v3/4vr9/+M7Pf/juz3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Lt + 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Hs9/+Q7Pf/kOz3/4/s9/+O7Pf/jez3/4zs + 9/+L6/f/iuv3/4nr9/+I6/f/iev3/4rr9v+J6/b/h+v2/4br9v+F6vb/bNT3/0Cw/P8+r/z/Pq/8/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/ZZvA/4mKiv+Jior/ury8/+Xo6P++xcX/vsXF/8DGxv/ByMj/w8nJ/8XL + y//GzMz/yM7O/8nPz//K0ND/zNHR/83S0v/JyMP/pGY1/6RlNP+kZTT/pGU0/6RlNP+kZTT/ilUs/wsH + A/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/yUXDP+hYzP/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+pd0//8fPz//P09P/z9PT/8/X1//T19f/09fX/u9Lo/yl80v8pfNL/KXzS/yl80v8pfNL/K33S/w7R + 7P8L1u7/C9bu/wvW7v8L1u7/C9bu/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/P+T5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq7/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaNFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIKh1wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOGpP8quND/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdbv/wDU7f8A1O3/ANTt/wrW + 7v873vH/PN7x/z7e8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0ng8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti + 8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9l5fT/Z+X0/2jm9P9q5vT/bOb1/23m + 9f9v5/X/cef1/3Ln9f906PX/duj1/3fo9f946PX/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq + 9v+G6/b/h+v3/4nr9/+K6/f/i+v3/4zs9/+O7Pf/juz3/5Ds9/+Q7Pf/kez3/5Lt9/+T7ff/k+33/5Tt + 9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt+P+V7fj/lO33/5Pt9/+T7ff/k+33/5Lt9/+R7Pf/kOz3/4/s + 9/+O7Pf/jez3/4vr9/+L6/f/iev3/4rr9/+L7Pf/iev2/4jr9v+G6/b/hev2/13E9f8+r/z/Pq/8/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/0es8f+JjI7/iouL/5KTk//d4OD/vsXF/8DGxv/CyMj/xMrK/8bM + zP/Hzc3/yc7O/8rQ0P/M0dH/zdLS/87T0//Q1dX/v6mW/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/y4c + Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Xjoe/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/+Pf2v/19vb/9vf3//b39//29/f/9/j4/9rm8P8ne9D/J3vQ/yd70P8ne9D/J3vQ/yd7 + 0P8Qw+j/CNXu/wjV7v8I1e7/CNXu/wjV7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWmFwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApA4AhKGVAIOh/QCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xScuP9D2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbi9/8A1O3/ANTt/wDU + 7f8A1O3/Nd3x/z3e8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UeLz/1Pi + 8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/23m + 9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq + 9v+H6/f/iOv3/4nr9/+K6/f/i+v3/43s9/+O7Pf/j+z3/5Ds9/+R7Pf/k+33/5Pt9/+U7ff/le34/5Xt + +P+W7fj/lu34/5ft+P+X7fj/l+34/5ft+P+X7fj/l+34/5bt+P+V7fj/le34/5Xt+P+U7ff/k+33/5Lt + 9/+R7Pf/kOz3/47s9/+O7Pf/jOz3/4vr9/+K6/f/iuv3/4nr9/+H6vb/hur2/4Tq9v9TvfX/Pq/8/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/bpy7/4yNjf+MjY3/sbW1/8DGxv/CyMj/xMrK/8bM + zP/Izc3/yc/P/8vR0f/N0tL/ztPT/9DV1f/R1tb/0tfX/7aSdP+kZTT/pGU0/6RlNP+kZTT/pGU0/4ZS + Kv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xILBv+jZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP/Qvq7/+Pn5//n5+f/5+fn/+fr6//r6+v/u8/f/JnnP/yZ5z/8mec//JnnP/yV5 + z/8lec//ELvl/wXV7f8F1e3/BdXt/wXV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf + 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ia7H/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh3wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEok0Ag6HjAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/BYqn/zDB2P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ftrx/wDU + 7f8A1O3/ANTt/yLa7/8+3vH/QN/y/0Hf8v9D3/L/ReDy/0fg8v9I4PL/SuDy/0zh8v9O4fP/UOHz/1Li + 8/9U4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl9P9p5vT/aub0/2zm + 9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3vp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq + 9v+H6/f/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Hs9/+T7ff/k+33/5Xt+P+V7fj/lu34/5ft + +P+Y7vj/mO74/5nu+P+Z7vj/me74/5ru+P+Z7vj/me74/5nu+P+Y7vj/mO74/5ju+P+W7fj/le34/5Xt + +P+U7ff/k+33/5Hs9/+Q7Pf/j+z3/47s9/+M7Pf/i+v3/4rr9/+J6/f/iOv3/4bq9v+F6vb/Tbj1/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/06v8P+LkJL/jo6O/2JkZP+Dh4f/xMrK/8bM + zP/Izs7/ys/P/8zR0f/O09P/z9TU/9HV1f/S19f/1NjY/9XZ2f+xg1//pGU0/6RlNP+kZTT/pGU0/6Rl + NP9hPB//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8CAgL/BQUF/wYGBv8FBQX/kFku/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/yK2W//v7+//7/Pz/+/z8//z8/P/8/Pz/+/z9/yR4zv8keM7/JHjO/yR4 + zv8keM7/JHjO/w+14v8C1O3/AtTt/wLU7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU + 7f9F5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef7/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oKcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWmFwCDoKcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/GaO+/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm + +f8C1O3/ANTt/wDU7f8M1u7/Pt7x/0Df8v9C3/L/RN/y/0Xg8v9I4PL/SeDy/0vh8v9N4fL/T+Hz/1Hi + 8/9S4vP/VOLz/1bj8/9Y4/P/WePz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2bl9P9n5fT/aub0/2vm + 9f9t5vX/b+f1/3Hn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+C6vb/g+r2/4Tq + 9v+G6/b/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/lO33/5Xt+P+W7fj/mO74/5ju + +P+Z7vj/mu74/5ru+P+b7vj/m+74/5zu+P+c7vj/nO74/5vu+P+b7vj/mu74/5ru+P+Z7vj/mO74/5ju + +P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs9/+L6/f/iev3/4jr9/+H6/f/hOr2/0u1 + 9f8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbL8/0iz/P9KtPz/cqC//4+QkP8lJib/HB0d/8TK + yv/Izs7/ys/P/8zR0f/O09P/0NXV/9HW1v/T2Nj/1dnZ/9ba2v/Y3Nz/r3xV/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/VzYc/wAAAP8AAAD/AAAA/wwMDP8WFhb/GRkZ/xwcHP8gICD/IyMj/yYmJv8pKSn/LCws/5ds + S/+maTn/pGU0/6RlNP+kZTT/pGU0/8Ojiv/9/v7//v7+//7+/v/+//////////////8re8//InbN/yJ2 + zf8ids3/InbN/yJ2zf8NsOH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfP5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6FvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIShXwCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ijar/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/J971/wDU7f8A1O3/ANTt/zTd8f9B3/L/Q9/y/0Tf8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h + 8/9S4vP/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2rm + 9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Xo9f936PX/eOj1/3rp9v986fb/fen2/3/p9v+B6vb/gur2/4Tq + 9v+G6/b/h+v3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt+P+Y7vj/mO74/5ru + +P+a7vj/m+74/5zu+P+c7vj/ne74/57v+P+e7/j/nu/4/57v+P+d7vj/ne74/5zu+P+c7vj/m+74/5ru + +P+a7vj/mO74/5ju+P+W7fj/le34/5Pt9/+S7ff/kOz3/4/s9/+O7Pf/jOz3/4vr9/+J6/f/h+v3/4br + 9v9Mt/X/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/0iz/P9KtPz/TbX8/1az8v94fX//AQEB/wAA + AP98gID/ys/P/8zR0f/O09P/0NXV/9LW1v/U2Nj/1tra/9fb2//Z3d3/2t7e/7SIZf+kZTT/pGU0/6Rl + NP+kZTT/pGU0/2Y/IP8AAAD/AAAA/yAgIP80NDT/Nzc3/zo6Ov89PT3/QUFB/0RERP9HR0f/SkpK/01N + Tf+3kHH/vpFu/6ZoOP+kZTT/pGU0/6RlNP/Mspz////////////////////////////+/v//IXXL/yF1 + y/8hdcv/IXXL/yF1y/8gdMv/C7Ph/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/MeH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8bpsD/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8fqsT/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0zo+/8K1+//ANTt/wDU7f8d2e//Qd/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TeHy/07h + 8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm + 9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p9v+A6vb/gur2/4Tq + 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+U7ff/le34/5ft+P+Y7vj/me74/5ru + +P+b7vj/nO74/53u+P+e7/j/n+/4/5/v+P+g7/j/oO/4/6Dv+P+g7/j/oO/4/5/v+P+f7/j/nu/4/53u + +P+c7vj/m+74/5ru+P+Z7vj/mO74/5bt+P+V7fj/k+33/5Lt9/+Q7Pf/j+z3/43s9/+L6/f/iuv3/4nr + 9/+H6/f/Tbj2/z6v/P8+r/z/Pq/8/z6v/P9BsPz/RLH8/0ez/P9KtPz/TbX8/1C2/P9St/z/L0JQ/wAA + AP8AAAD/FRYW/8jOzv/O09P/0NXV/9LW1v/U2Nj/1tra/9jc3P/Z3d3/29/f/9zg4P+9mn7/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+RWS7/AwIB/xQUFP9SUlL/VVVV/1hYWP9bW1v/Xl5e/2FhYf9lZWX/aGho/2tr + a/9/eXT/zKmO/86skf+7jGf/pGU0/6RlNP+kZTT/2cm7////////////////////////////9vn7/x9z + yv8fc8r/H3PK/x9zyv8fc8r/H3PK/wm34/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/A9Xu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5vn/Aoak/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh8gCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAhKFyAIOh9QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuSrv870OX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/N+P3/wDU7f8A1O3/BtXt/0Hf8v9D3/L/ReDy/0jg8v9J4PL/S+Hy/03h + 8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9l5fT/Z+X0/2jm + 9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo9f976fb/fOn2/37p9v9/6fb/ger2/4Pq + 9v+E6vb/h+v3/4jr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu + +P+c7vj/ne74/5/v+P+f7/j/oO/4/6Hv+P+h7/j/ou/4/6Lv+P+i7/j/ou/4/6Lv+P+h7/j/oe/4/6Dv + +P+f7/j/nu/4/53u+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jez3/4vr + 9/+J6/f/h+v3/1S/9v8+r/z/Pq/8/z6v/P9AsPz/Q7H8/0ay/P9KtPz/TbX8/1C2/P9Tt/z/RpnR/wED + BP8AAAD/BwcH/wAAAP9xdHT/z9TU/9HW1v/T2Nj/1tra/9jc3P/a3t7/3N/f/97h4f/f4uL/zLmp/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/0IoFf9AQED/cnJy/3Z2dv95eXn/fHx8/39/f/+CgoL/hoaG/4mJ + if+MjIz/wa6f/9i9qP/Zv6v/07We/6RlNP+kZTT/pGY1//Hu6////////////////////////////+Ts + 8/8dcsn/HXLJ/x1yyf8dcsn/HXLJ/x1yyf8Gweb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/x3c8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/L83k/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoS4Ag6HLAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AYWi/yWzzP9M5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8X2/H/ANTt/wDU7f8v3PD/RN/y/0bg8v9I4PL/SuDy/0zh + 8v9N4fL/T+Hz/1Hi8/9T4vP/VeLz/1bj8/9Z4/P/WuPz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl + 9P9p5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/duj1/3jo9f956Pb/e+n2/33p9v9/6fb/gOr2/4Lq + 9v+E6vb/huv2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu + +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+k8Pn/pfD5/6Tw+f+k8Pn/pPD5/6Pv + +f+i7/j/oe/4/6Dv+P+f7/j/ne74/5zu+P+a7vj/me74/5ju+P+V7fj/lO33/5Pt9/+R7Pf/j+z3/47s + 9/+L6/f/iuv3/4jr9/9gy/n/Pq/8/z6v/P8/r/z/QrD8/0ay/P9Js/z/TLX8/0+2/P9St/z/Vbn8/xcy + Q/8AAAD/FyMr/xscHP8AAAD/EBER/8nNzf/T2Nj/1dra/9jc3P/a3t7/3N/f/97h4f/g4+P/4eTk/+De + 2/+laDn/pGU0/6RlNP+kZTT/pGU0/6RlNP+XXTD/dW1n/5OTk/+Wlpb/mpqa/52dnf+goKD/o6Oj/6am + pv+qqqr/wrq0/+PPwP/k0cL/5dPF/+TRwv+kZTT/pGU0/6+BXf/+/v7///////////////////////// + ///F1+r/HHDH/xxwx/8ccMf/HHDH/xxwx/8ccMf/As7r/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f854/j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKjvf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqFsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICfCACCoYUAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/D5ey/0DV6/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Reb6/wLU7v8A1O3/Fdju/0Xg8v9G4PL/SODy/0rg + 8v9M4fL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl + 9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq + 9v+D6vb/hOr2/4fr9/+I6/f/iuv3/4vr9/+O7Pf/j+z3/5Hs9/+T7ff/le34/5bt+P+Y7vj/mu74/5vu + +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+k8Pn/pfD5/6bw+f+m8Pn/p/D5/6fw+f+m8Pn/pvD5/6bw + +f+l8Pn/pPD5/6Pv+f+h7/j/oO/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt+P+T7ff/ku33/5Ds + 9/+O7Pf/jOz3/4vr9/+J6/f/atT5/z6v/P8+r/z/QbD8/0Wx/P9Is/z/S7T8/0+2/P9St/z/Vbj8/zt9 + qv8AAAD/AQID/0uRwv8TFxn/AAAA/wAAAP9gYmL/1NnZ/9fb2//Z3d3/29/f/97h4f/g4+P/4uXl/+Tn + 5//l6Oj/u5Jx/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/8Kcfv/Gv7r/t7e3/7u7u/++vr7/wcHB/8TE + xP/JyMj/39fR/+7h2P/v49r/8OXd//Hn3//k0sP/pGU0/6RlNP/EsJ7///////////////////////// + ////////kLbd/xpvxv8ab8b/Gm/G/xpvxv8ab8b/F3rK/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8J1+//Tun8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISgPgCDofsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Boqo/ym40P8ntc7/JbPM/yWzzP8ls8z/JbPM/yWzzP8ir8n/IKzG/yCsxv8grMb/IKzG/yCs + xv8dqcP/GqW//xqlv/8apb//GqW//xqlv/8Yor3/FZ65/xWeuf8Vnrn/FZ65/xWeuf8TnLf/EJiz/xCY + s/8QmLP/EJiz/xCYs/8PlrH/C5Gu/wuRrv8Lka7/C5Gu/wuRrv8Fjqv/AIim/wCNq/8dr8j/RNzv/0ng + 8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9h5PT/YuX0/2Xl + 9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo9f936PX/eOj1/3vp9v996fb/fun2/4Dq + 9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/lO33/5Xt+P+X7fj/me74/5ru + +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+l8Pn/pvD5/6fw+f+o8Pn/qPD5/6nw+f+p8Pn/qfD5/6jw + +f+o8Pn/pvD5/6bw+f+k8Pn/o+/5/6Hv+P+g7/j/n+/4/5zu+P+b7vj/mu74/5ju+P+W7fj/lO33/5Pt + 9/+Q7Pf/j+z3/43s9/+L6/f/iev3/3je+P8+r/z/P6/8/0Ox/P9Gsvz/SrT8/061/P9Rt/z/VLj8/02k + 3/8HDhP/AAAA/zNkhf9jvvz/BgsO/wAAAP8BAgL/CwwM/77Cwv/Y3Nz/2t7e/93h4f/f4+P/4eTk/+Tn + 5//m6Oj/6Orq/+Da0/+laTr/pGU0/6RlNP+kZTT/pGU0/6RlNP+xe1H/7+Tb/+7m3//p5N//6eXi/+vo + 5f/w7On/9vHt//jz7//59fL/+vf1//v59//9+/n/zKmO/6RlNP+selL/6+3s//////////////////// + /////////////0KHzf8ZbsX/GW3F/xltxf8ZbcX/GW3F/xGO0f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8myuL/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKiaACDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmj + vf9J4PL/S+Hy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9g5PT/YeT0/2Pl + 9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n9f9w5/X/cuf1/3Pn9f926PX/eOj1/3no9v976fb/fen2/3/p + 9v+B6vb/gur2/4Tq9v+G6/b/iOv3/4nr9/+L6/f/juz3/4/s9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5ru + +P+c7vj/ne74/5/v+P+h7/j/ou/4/6Tw+f+m8Pn/p/D5/6jw+f+p8Pn/q/H5/6vx+f+r8fn/q/H5/6vx + +f+r8fn/qfD5/6jw+f+n8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt + +P+T7ff/kez3/4/s9/+O7Pf/i+v3/4rr9/+F6ff/Pq/8/0Gw/P9Fsfz/SLP8/0y1/P9Ptvz/U7j8/1a3 + +f8VLDv/AQEC/zJigv9jvvz/Wqnf/wAAAP8CAwP/IiMj/wAAAP9MTk7/19vb/9zf3//e4uL/4eTk/+Pm + 5v/l6Oj/6Orq/+rs7P/s7u7/0Lek/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/9Cvlv/9+/r//v39//// + ////////////////////////////////////////6tvQ/6ZoOP+mbD7/ysS8//39/f////////////// + //////////////L1+P8XbMP/F2zD/xdsw/8XbMP/F2zD/xdsw/8KqNz/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0Lm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Cp66/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIWfMACDofwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Qtjr/0vh8v9N4fL/T+Hz/1Hi8/9T4vP/VOLz/1bj8/9Y4/P/WuPz/1zk8/9e5PT/YOT0/2Ll + 9P9j5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9v5/X/cef1/3Ln9f906PX/duj1/3jo9f966fb/e+n2/33p + 9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/l+34/5nu + +P+a7vj/nO74/5/v+P+g7/j/oe/4/6Tw+f+l8Pn/p/D5/6jw+f+q8fn/q/H5/6zx+f+t8fn/rfH5/63x + +f+t8fn/rPH5/6vx+f+q8fn/qPD5/6fw+f+l8Pn/pPD5/6Hv+P+g7/j/nu/4/5zu+P+a7vj/me74/5ft + +P+V7fj/k+33/5Lt9/+Q7Pf/juz3/4zs9/+K6/f/iev3/0m2+v9Dsfz/RrL8/0q0/P9Otfz/Ubf8/1W4 + /P8lT2v/ESIt/0mRwf9jvvz/Zb/8/0mHsP8AAAD/FCIs/0FKUP8AAAD/BgYG/4GDg//d4OD/3+Pj/+Ll + 5f/k5+f/5+np/+nr6//r7e3/7e/v//Dx8f/Go4f/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/zKmO//z6 + +f//////////////////////////////////////4Mq5/6ltPv+lZzb/xbep/+fq6v////////////// + //////////////////+0zOX/FWrC/xVqwv8VasL/FWrC/xVqwv8VasL/A8Tm/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xDY8P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O+H2/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/z3P4/9L4fL/TeHy/0/h8/9R4vP/U+Lz/1Xi8/9W4/P/WePz/1vj8/9c5PP/XuT0/2Dk + 9P9i5fT/ZOX0/2Xl9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp + 9v9+6fb/f+n2/4Lq9v+D6vb/her2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+U7ff/lu34/5ju + +P+a7vj/m+74/53u+P+f7/j/oe/4/6Pv+f+k8Pn/pvD5/6jw+f+q8fn/q/H5/63x+f+u8fn/sPL5/7Dy + +f+w8vn/r/L5/67x+f+t8fn/q/H5/6nw+f+o8Pn/pvD5/6Tw+f+i7/j/oe/4/5/v+P+d7vj/m+74/5nu + +P+Y7vj/le34/5Tt9/+S7ff/kOz3/47s9/+M7Pf/i+v3/4nr9/9eyfr/RLH8/0iz/P9Mtfz/T7b8/1O4 + /P8ya5H/NW2T/125+P9hvvz/Zb/8/2jA/P8sUGj/AAAA/zJXcP9Qfp3/AAAA/wAAAP8KCgr/srW1/+Dk + 5P/j5ub/5ejo/+jq6v/q7e3/7e/v/+/x8f/y8/P/8/Pz/8SdgP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+udkr/0rOb/+vd0v/28Ov/+PPv/+/k3P/awa3/t4Zf/6RlNP+maTn/xK+e/9fb2//8/Pz///////// + ////////////////////////RojM/xRpwf8UacH/FGnB/xRpwf8UacH/EnTG/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8u4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x7E + 3f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HrAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wGNqv9H3/H/TOHy/03h8v9P4fP/UuLz/1Pi8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k + 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3Xo9f926PX/eOj1/3vp + 9v986fb/fun2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Ds9/+T7ff/le34/5bt + +P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx+f+u8fn/sPL5/7Hy + +f+y8vr/svL6/7Hy+f+w8vn/rfH5/6zx+f+q8fn/qPD5/6bw+f+k8Pn/o+/5/6Hv+P+f7/j/ne74/5vu + +P+a7vj/mO74/5Xt+P+U7ff/ku33/5Ds9/+O7Pf/jOz3/4vr9/+J6/f/dtz4/0ay/P9KtPz/TrX8/1G3 + /P9Rr+//U67s/1y7/P9gvfz/ZL/8/2fA/P9ovvj/DBUb/wAAAP9ZmcT/Xp7J/wAAAP8AAAD/AAAA/x8f + H//S1dX/4+bm/+bp6f/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+Pj/2MCt/6xyRv+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+vflf/zsS6/9bb2//v8fH///////// + ////////////////////////2+bw/xJowP8SZ7//Eme//xJnv/8SZ7//Eme//wqY1P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo + +/8ElLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICkDgCCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCLqf8Awt3/LNvw/0zh8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/13k + 9P9e5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/23m9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo + 9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s9/+R7Pf/k+33/5Xt + +P+X7fj/mO74/5ru+P+c7vj/nu/4/6Dv+P+h7/j/pPD5/6bw+f+n8Pn/qfD5/6vx+f+t8fn/r/L5/7Dy + +f+y8vr/tfP6/7Ty+v+y8vr/sPL5/67x+f+s8fn/qvH5/6jw+f+m8Pn/pPD5/6Pv+f+h7/j/n+/4/53u + +P+b7vj/mu74/5ju+P+V7fj/lO33/5Lt9/+Q7Pf/juz3/4zs9/+L6/f/iev3/4bq9/9KtPv/S7T8/0+2 + /P9Tt/z/V7n8/1q7/P9evPz/Yr78/2a//P9pwfz/SIGn/wAAAP8ZKzj/dsb9/2Sk0P8AAAD/AAAA/wAA + AP8AAAD/Ozw8/+Ll5f/n6en/6evr/+zu7v/v8PD/8fPz//T19f/39/f/+fr6//v8/P/59vP/07af/6x0 + SP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7F/WP/Muar/3N/f/9nd3f/k5+f///////// + /////////////////////////////1mSz/8RZr7/EWa+/xFmvv8RZr7/EWa+/xFmvv8DweX/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdry/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8z2/H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFoUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKEbAISimQCDof8Ag6H/AIOh/wCE + ot0Ag6HTAIOhzACDocwAg6HMAIOhzACDocwAg6HEAIOhuwCDobsAg6G7AIOhuwCDobsAgqK0AIOhqgCD + oaoAg6GqAIOhqgCDoaoAg6KkAISimQCEopkAhKKZAISimQCDob0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/D67I/yq50f8qudH/KrnR/yq50f8rutL/L8DX/y/A1/8ivNX/ALPO/wCzzv8AtM//ALnU/wC5 + 1P8AudT/ALnU/wC92P8A0uv/ANTt/wfV7f9J4PL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj + 8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3fo + 9f946PX/e+n2/33p9v9+6fb/gOr2/4Lq9v+E6vb/huv2/4jr9/+J6/f/i+v3/43s9/+P7Pf/kez3/5Pt + 9/+V7fj/l+34/5ju+P+a7vj/nO74/57v+P+g7/j/oe/4/6Tw+f+m8Pn/p/D5/6nw+f+r8fn/rfH5/6/y + +f+w8vn/svL6/7Py+v+y8vr/sfL5/7Dy+f+t8fn/rPH5/6rx+f+o8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v + +P+d7vj/m+74/5nu+P+Y7vj/le34/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iuv3/4nr9/+H6/f/Ysr5/0y1 + /P9Qtvz/VLj8/1i6/P9cu/z/YL38/2S//P9nwPz/ab/4/xIgKv8AAQH/V5S9/3jH/f9mpc//AAAA/wAA + AP8AAAD/AAAA/wAAAP9eYGD/5+np/+nr6//s7u7/7/Dw//Hz8//09fX/9/f3//n6+v/8/Pz//f39//z8 + /P/4+fj/5NbL/86ymv/Foob/vpRz/76Xd//DoYf/y7Ke/9zVzv/h5OT/3+Li/9zg4P/g4+P//f39//// + /////////////////////////////9Df7P8PZb3/D2W9/w9lvf8PZb3/D2W9/w9lvf8Ofcf/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zbj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/FrfQ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoewAgKoGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJkKAIOh8gCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wKnwv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w3X8P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/J9vw/07h8/9P4fP/UuLz/1Ti8/9V4vP/V+Pz/1nj + 8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo + 9f936PX/eOj1/3vp9v996fb/fun2/4Dq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Hs + 9/+T7ff/le34/5bt+P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx + +f+t8fn/r/L5/7Dy+f+x8vn/sPL5/7Dy+f+u8fn/rfH5/6vx+f+p8Pn/p/D5/6bw+f+k8Pn/ou/4/6Dv + +P+f7/j/nO74/5ru+P+Z7vj/l+34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr9/+I6/f/h+v3/3rg + 9/9Ntfz/Ubf8/1W5/P9auvz/Xrz8/2G+/P9lv/z/acH8/zxri/8AAAD/M1dw/3jH/f97yP3/XZW7/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AgIC/4CCgv/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+fn/+vr6//r7 + +//6+vr/+Pn5//X29v/z9PT/8fLy/+7w8P/r7e3/6evr/+bp6f/k5+f/4eTk/97i4v/i5eX/+/z8//// + //////////////////////////////r7/P8sdsP/DmO8/w5jvP8OY7z/DWO7/w1ju/8NY7v/BbPe/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v9N6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/R+b6/wGIp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GSAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaAAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AiKX/Kd3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8y4fb/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7f9I4PL/T+Hz/1Li8/9U4vP/VeLz/1fj + 8/9Z4/P/W+Pz/13k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn + 9f916PX/d+j1/3jo9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s + 9/+Q7Pf/k+33/5Xt+P+W7fj/mO74/5ru+P+c7vj/ne74/5/v+P+h7/j/o+/5/6Tw+f+m8Pn/qPD5/6nw + +f+r8fn/rPH5/63x+f+u8fn/rvH5/67x+f+t8fn/rfH5/6vx+f+q8fn/qPD5/6bw+f+l8Pn/o+/5/6Hv + +P+f7/j/nu/4/5zu+P+a7vj/mO74/5ft+P+V7fj/k+33/5Hs9/+P7Pf/juz3/4vr9/+J6/f/iOv3/4br + 9v+E6vb/XMT6/1O3/P9Xufz/Wrv8/1+8/P9jvvz/Z8D8/1mi0v8EBwn/IzxN/3XE+v96yP3/fcn9/1B+ + nf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/nZ+f/+rt7f/t7+//7/Hx//Lz8//09fX/9fb2//f4 + +P/3+Pj/9/j4//b39//09fX/8vPz//Dx8f/t7+//6+3t/+jq6v/l6Oj/4+bm/+Dk5P/n6en//f39//// + //////////////////////////////////92pNL/DGK7/wxiuv8MYrr/DGK6/wxiuv8MYrr/DXvF/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8h3fT/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/yrQ5v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhMQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wm41P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tuj8/wrW + 7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/JNrw/0/h8/9S4vP/VOLz/1Xi + 8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Hn + 9f9z5/X/dej1/3fo9f946PX/e+n2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+J6/f/i+v3/43s + 9/+O7Pf/kOz3/5Lt9/+U7ff/le34/5ju+P+a7vj/m+74/5zu+P+f7/j/oO/4/6Lv+P+k8Pn/pfD5/6bw + +f+o8Pn/qfD5/6vx+f+r8fn/rPH5/6zx+f+s8fn/q/H5/6vx+f+p8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv + +P+g7/j/n+/4/53u+P+b7vj/mu74/5ju+P+W7fj/lO33/5Lt9/+Q7Pf/juz3/43s9/+L6/f/iev3/4fr + 9/+F6vb/hOr2/3be9/9TuPz/V7n8/1y7/P9gvfz/ZL/8/2S48f8SISr/KUhd/3HA9/94x/3/fMn9/3/K + /f82VWn/AAAA/wsRFf8AAAD/AAAA/wAAAP8AAAD/AAAA/wsLC/+lqKj/6+3t/+7w8P/w8fH/8vPz//P0 + 9P/09fX/9Pb2//T19f/z9PT/8vPz//Dx8f/u8PD/7O7u/+ns7P/n6ur/5Ofn/+Pm5v/x8vL///////// + //////////////////////////////////+1zOL/CmC5/wpguf8KYLn/CmC5/wpguf8KYLn/CmG6/w69 + 4/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Tuj8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8Nnrv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCC + odcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/NuL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f9G4PL/UuLz/1Ti + 8/9V4vP/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n + 9f9x5/X/c+f1/3Xo9f926PX/eOj1/3rp9v986fb/fun2/3/p9v+C6vb/g+r2/4Xq9v+H6/f/iev3/4vr + 9/+M7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/me74/5ru+P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw + +f+l8Pn/pvD5/6jw+f+o8Pn/qfD5/6rx+f+q8fn/qvH5/6nw+f+o8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv + +P+h7/j/n+/4/53u+P+c7vj/mu74/5ju+P+X7fj/le34/5Pt9/+R7Pf/kOz3/47s9/+M7Pf/iuv3/4nr + 9/+H6/f/her2/4Pq9v+B6vb/Ysj6/1i6/P9cu/z/Yb38/2W++/8pTGP/RHmc/3HE/f91xv3/ecj9/33J + /f+By/3/FSAo/wAAAP8oOkf/AAAA/wAAAP8OFBj/Exgb/wAAAP8AAAD/DQ0N/6utrf/s7u7/7e/v/+/x + 8f/w8vL/8fPz//Hz8//x8/P/8PLy/+/x8f/u8PD/7O7u/+rs7P/o6ur/5ejo/+vt7f/6+/v///////// + ///////////////////////////////////R3ej/C2C5/wlfuP8JX7j/CV+4/wlfuP8JX7j/CV+4/xCS + z/8J1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8/4PX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoHEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAe5eHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xDG3v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/wjW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdjv/1Li + 8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl9P9q5vT/a+b1/23m + 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f966fb/e+n2/33p9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4jr + 9/+K6/f/i+v3/47s9/+P7Pf/kez3/5Pt9/+V7fj/lu34/5ju+P+a7vj/m+74/5zu+P+e7/j/oO/4/6Hv + +P+i7/j/pPD5/6Xw+f+m8Pn/pvD5/6fw+f+o8Pn/qPD5/6jw+f+n8Pn/pvD5/6bw+f+k8Pn/pPD5/6Lv + +P+h7/j/n+/4/57v+P+c7vj/mu74/5nu+P+Y7vj/le34/5Tt9/+T7ff/kOz3/4/s9/+N7Pf/i+v3/4nr + 9/+I6/f/huv2/4Tq9v+C6vb/ger2/3nj9/9au/z/Xbz8/2G+/P9Un9L/Xqvf/27D/P9yxf3/dsb9/3rI + /f9+yv3/barS/wAAAP8JDhH/P11x/wAAAP8AAAD/GSQr/0NecP8AAAD/AAAA/wAAAP8PDw//paam/+vt + 7f/s7u7/7e/v/+7w8P/v8PD/7vDw/+3v7//s7u7/6+3t/+rs7P/p6+v/7vDw//j5+f////////////// + ///////////////////////////////////a5Oz/EGK4/wddt/8HXbf/B123/wddt/8HXbb/B122/wpw + vv8Y0uz/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zvk+P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Ir3V/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAgJ8QAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAXHEkAF1y6QCBnv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Al7P/OuT4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f853vH/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2vm + 9f9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4br + 9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/mO74/5ru+P+c7vj/ne74/5/v + +P+f7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+l8Pn/pfD5/6bw+f+l8Pn/pfD5/6Tw+f+k8Pn/o+/5/6Hv + +P+h7/j/n+/4/57v+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr + 9/+J6/f/h+v3/4Xq9v+D6vb/gur2/3/p9v9+6fb/bNP5/168/P9ivvz/ZsD8/2rB/P9vw/z/c8X9/3fH + /f97yP3/gMr9/zRRZP8AAAD/Vn+b/zxXaf8AAAD/AAAA/yo6Rf91o8D/AAAA/wAAAP8AAAD/AAAA/wwM + DP+RkZH/7/Hx/+7w8P/t7+//7O7u/+3v7//u8PD/8PLy//T19f/4+fn//v7+//////////////////// + ///////////////////////////////////M2uj/DmG3/wZctf8GXLX/Bly1/wVctf8FXLX/BVy1/wZi + t/8Xw+b/FNfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPZ8P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Tej7/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6CUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW20cAFpv4QBab/8Acoz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xTL4/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Tej8/wvX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Ctbu/0/h8/9U4vP/VuPz/1jj8/9a4/P/XOTz/17k9P9g5PT/YeT0/2Pl9P9l5fT/Z+X0/2nm + 9P9q5vT/bOb1/27n9f9w5/X/cef1/3Pn9f926PX/d+j1/3no9v976fb/fen2/37p9v+A6vb/gur2/4Tq + 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kez3/5Pt9/+U7ff/le34/5ju+P+Z7vj/mu74/5zu + +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+i7/j/o+/5/6Pv+f+j7/n/o+/5/6Pv+f+i7/j/oe/4/6Hv + +P+f7/j/n+/4/53u+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+O7Pf/jez3/4vr + 9/+J6/f/iOv3/4br9v+E6vb/gur2/4Hq9v9/6fb/fen2/3vp9v9myPv/Yr78/2fA/P9rwvz/b8P8/3PF + /f93x/3/fMn9/3e97P8FBwn/KT5M/43Q/f8wRVP/AAAA/wAAAP9HYXP/ndX7/wwRFP8AAAD/AAAA/wAA + AP8AAAD/BAQE/11dXf/29vb///////////////////////////////////////////////////////// + //////////////////////////////3+/v+nwtz/BVy0/wRbtP8EWrT/BFq0/wRatP8EWrT/BFq0/wRc + tf8Us93/Gtjv/wvW7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f885Pn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y7K4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIakKgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVW0VAFpv2gBab/8AWm//AGF3/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Amrf/PuX5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P804vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8m2vD/VOLz/1bj8/9Y4/P/WuPz/1vj8/9e5PT/X+T0/2Hk9P9i5fT/ZeX0/2bl + 9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq + 9v+C6vb/hOr2/4br9v+I6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5nu + +P+a7vj/m+74/5zu+P+d7vj/nu/4/5/v+P+g7/j/oO/4/6Hv+P+h7/j/oe/4/6Hv+P+h7/j/oO/4/5/v + +P+f7/j/nu/4/5zu+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/jez3/4vr + 9/+K6/f/iOv3/4fr9/+F6vb/g+r2/4Lq9v9/6fb/fun2/33p9v976fb/duP3/2TB+/9nwPz/a8L8/2/D + /P9zxf3/eMf9/3zJ/f86XHP/DxYc/3275v+N0P3/Hyw2/wAAAP8AAAD/a5Ks/6HY/f8yRFD/AAAA/wAA + AP8KDQ//AAAA/wAAAP8AAAD/Dg4O/4KCgv/n5+f///////////////////////////////////////// + //////////////////////////////Dz9v9Wjcf/A1mz/wJZs/8CWbP/Almz/wJZs/8CWbP/Almz/wJa + s/8Tpdb/HNnv/xrY7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vH/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/8KkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShuAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVXcPAFpv0gBab/8AWm//AFpv/wBab/8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xXO + 5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w/Y8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/0Xg8v9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl + 9P9m5fT/Z+X0/2rm9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p + 9v+A6vb/gur2/4Tq9v+F6vb/h+v3/4nr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt + +P+X7fj/mO74/5ru+P+a7vj/m+74/5zu+P+d7vj/nu/4/57v+P+f7/j/n+/4/5/v+P+f7/j/nu/4/57v + +P+d7vj/nO74/5zu+P+a7vj/mu74/5ju+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs + 9/+K6/f/iev3/4fr9/+F6vb/hOr2/4Lq9v+A6vb/f+n2/33p9v976fb/eun2/3jo9f9y4Pb/aMP7/2vC + /P9vw/z/c8X9/3fH/f9np9L/DRUa/3Gu2P+Jzv3/jdD9/wsQE/8AAAD/BwkL/5fP8/+e1/3/VnWL/wAA + AP8AAAD/P1tu/wAAAP8AAAD/AAAA/wAAAP8AAAD/AgIC/zY2Nv97e3v/vLy8//b29v////////////// + ////////////////////////+fr6/6jD3P8NX7T/AVix/wFYsf8BWLH/AVix/wFYsf8BWLH/AVix/wFY + sf8RndL/Hdnv/xvZ7/8W2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/Qeb6/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8xzeT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + okIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAXXQLAFtvyABab/8AWm//AFpv/wBab/8AWm//AGJ5/wCCoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ambb/OuP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P885Pn/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTs/wCuyf8DkK3/DJGt/yqxyf9X4fD/W+Pz/1zk8/9e5PT/YOT0/2Ll + 9P9k5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9u5/X/cOf1/3Hn9f9z5/X/dej1/3fo9f946PX/e+n2/3zp + 9v996fb/f+n2/4Hq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/ku33/5Pt + 9/+U7ff/le34/5ft+P+Y7vj/mO74/5ru+P+a7vj/m+74/5zu+P+c7vj/nO74/5zu+P+c7vj/nO74/5zu + +P+c7vj/m+74/5ru+P+a7vj/me74/5ju+P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs + 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Lq9v+B6vb/f+n2/33p9v986fb/e+n2/3jo9f936PX/dej1/2/d + 9/9rwfz/b8P8/3PF/f90wvf/JDpJ/2ahyf+EzP3/iM79/4C95/8AAAD/AAAA/z5WZ/+a1f3/m9b9/3ak + wv8AAAD/AAAA/2eWtv8OFRr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/Pj4+/2xs + bP+YmJj/tLS0/8rKyv/Fxsb/mKq7/xpRi/8APHv/AFSr/wBXsf8AV7H/AFex/wBXsf8AV7H/AFex/wBY + sf8RndL/H9nv/xzZ7/8b2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6Pv/CpGu/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ocQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAASW0HAFpvvQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8GgZz/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/xDH4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x3c8/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wClwP8Ag6H/AIOh/wCDof8Ag6H/I6nB/1rj8/9c5PP/XuT0/2Dk + 9P9h5PT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3To9f926PX/eOj1/3no + 9v976fb/fen2/37p9v+A6vb/gur2/4Pq9v+E6vb/huv2/4jr9/+J6/f/i+v3/4zs9/+O7Pf/j+z3/5Ds + 9/+R7Pf/k+33/5Tt9/+V7fj/le34/5ft+P+Y7vj/mO74/5nu+P+Z7vj/mu74/5ru+P+a7vj/mu74/5ru + +P+a7vj/me74/5nu+P+Y7vj/mO74/5ft+P+V7fj/le34/5Pt9/+T7ff/kez3/5Ds9/+P7Pf/juz3/4zs + 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/f+n2/37p9v996fb/e+n2/3no9v946PX/duj1/3To + 9f9z5/X/btv3/27E/P9yxf3/Qm6N/2iq1/9/yv3/g8z9/4fN/f9klbf/AAAA/wYICv+Dut//l9T9/5fU + /f+Hv+T/AAAA/wAAAP9Ygp7/RGd+/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAIEf8AL1//AFKn/wBXsf8AV7H/AFex/wFb + s/8Wrdr/INnv/x7Z7/8c2e//Gtjv/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0no + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LMje/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAZmYFAFlvsQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Qd4v/Ocrh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ak6//NeL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/BNXu/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC81v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9W3+//W+Pz/13k + 9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo + 9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iOv3/4nr9/+L6/f/jOz3/43s + 9/+O7Pf/kOz3/5Hs9/+S7ff/k+33/5Tt9/+V7fj/le34/5bt+P+X7fj/l+34/5ju+P+Y7vj/mO74/5ju + +P+Y7vj/mO74/5ft+P+X7fj/lu34/5Xt+P+V7fj/lO33/5Pt9/+S7ff/kOz3/5Ds9/+O7Pf/jez3/4vr + 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/e+n2/3rp9v946PX/duj1/3Xo + 9f9z5/X/cef1/3Dn9f9u3/b/XqbT/2y36v95yP3/fcn9/4HL/f+Fzf3/RmqC/wAAAP9Ue5X/kdL9/5PS + /f+U0/3/kdD6/wIDBP8AAAD/UnqV/3m34P8BAgL/AAAA/wAAAP8BAgL/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAGDP8AMmf/AFew/wVq + uv8cwOL/Idrv/x/Z7/8d2e//HNnv/xbY7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y/h + 9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sub6/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVVUDAFpupABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Ncob/SN3w/0/p/P8Xobv/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wq92P9N6fv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yzg + 9f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO5v8Aiqf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Utvt/1vj + 8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3To + 9f926PX/eOj1/3no9v976fb/fOn2/33p9v9/6fb/ger2/4Lq9v+E6vb/her2/4fr9/+I6/f/iev3/4rr + 9/+L6/f/jez3/47s9/+P7Pf/kOz3/5Hs9/+S7ff/k+33/5Pt9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt + +P+V7fj/le34/5Xt+P+V7fj/lO33/5Tt9/+T7ff/k+33/5Lt9/+Q7Pf/kOz3/4/s9/+O7Pf/jOz3/4vr + 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3rp9v946PX/d+j1/3Xo + 9f9z5/X/cuf1/3Hn9f9v5/X/beb1/2vg9/9yyvv/eMf9/3zJ/f+Ayv3/g8z9/yAwPP8qP03/jND9/47Q + /f+Q0f3/kNH9/5DR/f8IDA//AAAA/1B4lP+Fzf3/Gyk0/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wQi + Mv8duM3/Itrv/yHa7/8f2e//Hdnv/xvZ7/8L1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xLZ + 8P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yW81f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABAFtvmABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8LboP/Rtns/0/p/P9P6fz/Qdfs/wGE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ajar/KN3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9O6Pz/Edjw/wDU7f8A1O3/ANTt/wDU7f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/BpOv/1jj + 8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Hn + 9f9z5/X/dej1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+B6vb/gur2/4Tq9v+F6vb/h+v3/4fr + 9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Pt9/+T7ff/k+33/5Pt + 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Ds9/+Q7Pf/juz3/47s9/+N7Pf/i+v3/4vr + 9/+J6/f/iOv3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v946PX/d+j1/3bo + 9f906PX/cuf1/3Hn9f9v5/X/buf1/2zm9f9q5vT/aeX0/3DS+v96yP3/fsn9/3e86v8THST/fb7q/4nO + /f+Lz/3/jND9/4zQ/f+Mz/3/AwQF/wAAAP9KcYz/g8z9/z5hev8AAAD/AAAA/wAAAP8RHCX/AAAA/wEC + Av8dNkf/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AgkK/xaQnf8g2e//Htnv/xzZ7/8Z2O//AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU + 7f9C5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Ph9f8ChaT/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpwiwBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Jan//RNXo/0/p/P9P6fz/T+n8/0/p + /P8eqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOrx/9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0Lm+v8C1O3/ANTt/wDU7f8AtM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/zbQ + 5v833/L/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n + 9f9x5/X/cuf1/3Pn9f916PX/d+j1/3jo9f966fb/e+n2/33p9v9+6fb/f+n2/4Hq9v+C6vb/hOr2/4Tq + 9v+G6/b/h+v3/4jr9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+O7Pf/j+z3/5Ds9/+Q7Pf/kOz3/5Ds + 9/+R7Pf/kez3/5Hs9/+R7Pf/kOz3/5Ds9/+Q7Pf/j+z3/4/s9/+O7Pf/juz3/43s9/+L6/f/i+v3/4rr + 9/+J6/f/h+v3/4fr9/+F6vb/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo + 9f906PX/c+f1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aeb0/2fl9P9m5fT/atv3/3nL/P9OfJz/aqfQ/4TM + /f+Gzf3/iM79/4nO/f+Jzv3/hMbz/wAAAP8AAAD/W46w/4HL/f9Wiq7/AAAA/wAAAP8AAAD/L1Ns/wAA + AP8AAAD/JEZc/zhvlP8BAwT/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAQH/EXWA/x3Z7/8c2e//ENfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8p3/T/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8WpsD/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/gCEojQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvfABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8HZnv/QtHl/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Q9js/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/GdLp/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/KN/0/wDU7f8AyOP/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ya0 + zv9P6fz/Ten7/0bj9v9a4/P/XOTz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9p5vT/aub0/2zm + 9f9u5/X/b+f1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3rp9v976fb/fen2/37p9v9/6fb/ger2/4Lq + 9v+D6vb/hOr2/4Xq9v+H6/f/h+v3/4nr9/+J6/f/iuv3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/47s + 9/+O7Pf/juz3/4/s9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iev3/4nr + 9/+I6/f/h+v3/4br9v+E6vb/hOr2/4Lq9v+B6vb/f+n2/37p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/2zm9f9r5vX/aub0/2jm9P9m5fT/ZeX0/2Pl9P9i4/T/XbXT/37L + /f+By/3/g8z9/4TM/f+Fzf3/hc39/2Wbv/8AAAD/AAAA/2uq1P9+yv3/aqzZ/wAAAP8AAAD/AAAA/1KS + vP8EBQb/AQEB/wEBAv9PnNH/P4Kv/wMGCP8AAAD/AAAA/wAAAP8BAQL/Dig6/wABAv8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8MW2T/G9ju/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8Q2PD/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P800un/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvbgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8FY3f/P8zg/0/p/P9P6fz/SuDz/ze/ + 0v8knLD/EnqO/wJdcv8Ab4n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCYtP824vf/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o/P8P1+7/AJWy/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xCY + tP9N5/r/T+n8/0/p/P9P6fz/Tub4/1rj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/aOb0/2rm + 9P9r5vX/bOb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p + 9v+A6vb/gur2/4Lq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iuv3/4rr9/+L6/f/i+v3/4vr + 9/+M7Pf/jOz3/4zs9/+M7Pf/jOz3/4zs9/+M7Pf/i+v3/4vr9/+L6/f/iuv3/4rr9/+J6/f/iev3/4fr + 9/+H6/f/huv2/4Xq9v+E6vb/g+r2/4Lq9v+A6vb/f+n2/37p9v996fb/e+n2/3rp9v946PX/d+j1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Pl9P9i5fT/YOT0/17k + 9P9h4fX/btX5/37L/P+By/3/gcv9/4LL/f9DaoT/AAAA/w4WHP98x/v/e8j9/3O+8v8AAAD/AAAA/wAA + AP9jsuf/KEBQ/w8PD/8NDQ3/Kk1l/1q6/P9Ci73/CQ0Q/wQEBP8DAwP/AQEB/wouM/8mtcb/E15n/wIJ + Cv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wheaP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8E1e7/ROb5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J5/r/B46r/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDofUAgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpwYABab/4AWm//AFpv/wBab/8AWm//AFpv/wBab/8DX3X/NbrO/zW6zv8imKz/D3WJ/wFb + cP8AWm//AFpv/wBab/8AWm//AFtx/wB+mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CLzW/0rn + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/IrXO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOH + pf9D2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6Pv/WuT1/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl + 9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/c+f1/3To9f926PX/d+j1/3jo9f966fb/e+n2/3zp + 9v996fb/f+n2/3/p9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+I6/f/iev3/4nr + 9/+J6/f/iuv3/4rr9/+K6/f/iuv3/4rr9/+K6/f/iuv3/4nr9/+J6/f/iev3/4jr9/+I6/f/h+v3/4fr + 9/+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v9/6fb/f+n2/33p9v986fb/e+n2/3rp9v946PX/d+j1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/1/k + 9P9e5PT/W+Pz/1rj8/9b4vT/aNj3/3XP+v9+yv3/FyQu/wAAAP84W3L/esj9/3jH/f91xv3/AQIC/wAA + AP8HDRH/asH8/0l9oP8eHh7/HBwc/x4kKP9MsO//PbLs/yyNqv8TFxj/ERER/w8PD/8NDQ3/FktR/yrY + 7f8ozeD/FWNs/wUHCP8CAgL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AHB9/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HbPM/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtwVABab/wAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AZ37/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ + p/8e1+7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tp/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8uvtb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Do+/9Y5fb/XuT0/2Dk9P9h5PT/YuX0/2Tl + 9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/cOf1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3no + 9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr + 9/+H6/f/h+v3/4fr9/+I6/f/iOv3/4jr9/+I6/f/iOv3/4fr9/+H6/f/h+v3/4fr9/+G6/b/her2/4Xq + 9v+E6vb/hOr2/4Lq9v+C6vb/ger2/3/p9v9/6fb/fun2/33p9v976fb/e+n2/3no9v946PX/duj1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/1/k + 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/RLLC/wAAAP8FCgz/Ybjg/3HL+/9yyPz/cMf8/wAA + AP8AAAD/HTpK/13I+f9Rtdv/LCws/yoqKv8oKCj/M6e0/zfd8f813fH/LJai/x8fH/8eHh7/HBwc/xoa + Gv8heIP/Kdvw/yfb8P8iucv/FUlP/w8PD/8NDQ3/CwsL/wkJCf8ICAj/BgYG/wQFBf8BhZX/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N9jt/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HaAJKSBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtwSQBab/kAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wB2kv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AJu3/zjj+P9P6fz/T+n8/0/p/P9P6fz/TOX4/wyTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8XoLv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1bm+P9e5PT/YOT0/2Ll + 9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3bo + 9f946PX/eej2/3rp9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Lq9v+D6vb/hOr2/4Tq + 9v+E6vb/hOr2/4Xq9v+F6vb/huv2/4br9v+G6/b/huv2/4Xq9v+F6vb/her2/4Tq9v+E6vb/hOr2/4Pq + 9v+C6vb/gur2/4Lq9v+A6vb/f+n2/3/p9v9+6fb/fen2/3zp9v976fb/eun2/3jo9f936PX/duj1/3Xo + 9f9z5/X/cuf1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2nm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/xxOVP8AAAD/K3uE/0zh8v9K4PL/SeDy/0XZ + 6v8AAAD/AAAA/yV+if9B3/L/Pt7x/ztPUf84ODj/Nzc3/zaDjP823fH/NN3x/zLc8f8vdX3/LCws/yoq + Kv8oKCj/Jikp/yequf8m2vD/JNrw/yPa7/8fnKr/Gykr/xkZGf8XFxf/FhYW/xQUFP8SEhL/DxcY/wKz + yP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Dtjv/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOb5/wePq/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AX3X/AIGe/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8IutX/Sej7/0/p/P9P6fz/T+n8/yGux/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Giqj/R9/z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/U+j6/17k + 9f9g5PT/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/cuf1/3Pn + 9f916PX/duj1/3fo9f946PX/eej2/3vp9v976fb/fOn2/33p9v9+6fb/f+n2/3/p9v+A6vb/ger2/4Lq + 9v+C6vb/gur2/4Lq9v+D6vb/g+r2/4Pq9v+D6vb/hOr2/4Pq9v+D6vb/g+r2/4Pq9v+C6vb/gur2/4Lq + 9v+B6vb/gOr2/3/p9v9/6fb/fun2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f926PX/duj1/3To + 9f9z5/X/cuf1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9U4vP/U+Lz/0S8yv8BAgL/G09V/03g8f9L4fL/SeDy/0jg + 8v86uMf/AAAA/wIFBv88zd7/P9/x/z7e8f9Eg4v/R0dH/0VFRf8/cnn/Nd3x/zPd8f8y3PH/Mdjr/zhU + WP84ODj/Nzc3/zU1Nf8yRUf/JtHm/yTa8P8h2u//INnv/x/H2/8kV13/JiYm/yQkJP8jIyP/ISEh/x8f + H/8YOz//Ac3l/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/B9bu/0Tm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xatxv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOhowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFttKgBab/AAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/oAFluqwBzjqkAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIel/xfT6v9P6fz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9R6Pv/XOX1/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/b+f1/3Hn + 9f9y5/X/c+f1/3To9f926PX/duj1/3jo9f946PX/eej2/3vp9v976fb/fOn2/33p9v996fb/fun2/3/p + 9v9/6fb/f+n2/4Dq9v+A6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+A6vb/gOr2/3/p + 9v9/6fb/f+n2/37p9v996fb/fen2/3zp9v976fb/e+n2/3rp9v956Pb/eOj1/3fo9f926PX/dej1/3Pn + 9f9z5/X/cef1/3Dn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/8aSE7/GERJ/0va6v9L4fL/SuDy/0jg + 8v9G4PL/LpWh/wAAAP8WSlH/QN/y/z7e8f883vH/Rqm0/1VVVf9TU1P/TWhr/zTd8f8y3PH/MNzw/y/c + 8P80u8r/RkhI/0VFRf9DQ0P/QUFB/zSDjP8j2u//Idrv/x/Z7/8d2e//Gtfu/xqHlP8yMzP/MTEx/y8v + L/8tLS3/Kysr/xhvev8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/A9Xu/z7l+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yvN4/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh5wCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABbb7cAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnDgAFpwogBbb2UAWWwoAAAAAAAAAAAA//8BAISgugCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AlLH/LN/0/0ni9f8Hjar/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/HqrD/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/o/P9Y5/j/YOT0/2Ll9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/bOb1/27n + 9f9v5/X/cOf1/3Hn9f9z5/X/c+f1/3Xo9f926PX/duj1/3jo9f946PX/eej2/3rp9v976fb/e+n2/3zp + 9v996fb/fen2/33p9v9+6fb/fun2/37p9v9/6fb/f+n2/3/p9v9/6fb/f+n2/3/p9v9/6fb/fun2/37p + 9v996fb/fen2/33p9v986fb/e+n2/3vp9v966fb/eej2/3jo9f946PX/d+j1/3bo9f916PX/dOj1/3Pn + 9f9y5/X/cef1/2/n9f9v5/X/beb1/2zm9f9q5vT/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/88qbf/HVJZ/0vd7f9M4fL/SuDy/0jg + 8v9H4PL/ReDy/yJye/8AAAD/L6Ox/z7e8f893vH/PN7x/0HG1v9kZGT/YmJi/1tvcf8z3fH/Mdzw/zDc + 8P8t3PD/LNvw/0eEi/9TU1P/UVFR/1BQUP9NUFD/J8bY/yDZ7/8e2e//HNnv/wzW7v8A1O3/FaO0/z1E + RP89PT3/PDw8/zo6Ov84OTn/DLDD/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AdXt/zfj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zzf9P8ChaT/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm/vAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/XAFpumwBabl0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + nh0Ag6LwAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKoxP8Uor3/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/CpCt/0vk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Xo+f9f5PX/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm + 9f9s5vX/beb1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3Xo9f926PX/duj1/3fo9f946PX/eOj1/3no + 9v966fb/e+n2/3vp9v976fb/fOn2/3zp9v986fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3zp + 9v986fb/e+n2/3vp9v976fb/eun2/3no9v946PX/eOj1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Ln + 9f9x5/X/cOf1/2/n9f9u5/X/bOb1/2vm9f9q5vT/aeb0/2fl9P9m5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4fL/OaOw/03h8v9M4fL/SuDy/0ng + 8v9H4PL/ReDy/0Tf8v8RNzz/EDY7/z/f8f8+3vH/PN7x/zre8f863e//cHZ3/3BwcP9ne33/Mtzx/zDc + 8P8u3PD/Ldzw/yvb8P8yydv/YWNj/2BgYP9eXl7/XFxc/0SMlP8f2e//Hdnv/xXY7v8A1O3/ANTt/wDU + 7f8Rtsn/RldZ/0pKSv9ISEj/RkZG/zhdYf8B0en/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fm+v8IlbH/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFpvywBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/0AWm/RAFtwkgBbcFQAWW8XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKgXgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AYSi/zzR5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Uun6/13l9v9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm + 9P9q5vT/aub0/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/duj1/3bo + 9f936PX/eOj1/3jo9f946PX/eej2/3no9v966fb/eun2/3rp9v976fb/e+n2/3vp9v976fb/eun2/3rp + 9v966fb/eej2/3no9v946PX/eOj1/3jo9f936PX/duj1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn + 9f9w5/X/b+f1/27n9f9t5vX/bOb1/2rm9P9q5vT/aOb0/2fl9P9m5fT/ZeX0/2Pl9P9i5fT/YOT0/1/k + 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9M4fL/SuDy/0ng + 8v9I4PL/RuDy/0Tf8v89zd7/AQME/zOxwP8+3vH/PN7x/zve8f853vH/N93x/3GTl/9+fn7/cI2R/zHc + 8P8v3PD/Ldzw/yvb8P8q2/D/Kdvw/1mQlv9ubm7/bGxs/2pqav9obG3/ItLn/xvZ7/8D1e3/ANTt/wDU + 7f8A1O3/ANTt/xG90f9SYWP/VlZW/1VVVf9TU1P/JZmm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8Srcf/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeAAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm/8AFpv/wBab/8AWm//AFpv/wBab/0AWm/HAFtvigBb + b0wAYHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6fv/Wub3/2Ll9P9j5fT/ZOX0/2Xl + 9P9n5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9y5/X/c+f1/3Pn + 9f906PX/dej1/3bo9f926PX/duj1/3fo9f936PX/eOj1/3jo9f946PX/eOj1/3jo9f946PX/eOj1/3jo + 9f946PX/eOj1/3fo9f936PX/duj1/3bo9f926PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn + 9f9v5/X/buf1/23m9f9s5vX/a+b1/2rm9P9p5vT/Z+X0/2fl9P9l5fT/ZOX0/2Ll9P9i5fT/YOT0/1/k + 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/KIiU/xhVXP8+3vH/PN7x/zve8f853vH/ON3x/zbd8f9uq7L/jY2N/3Ki + qP8w3PD/Ltzw/yzb8P8r2/D/Kdvw/yfb8P9Bv87/fX19/3t7e/95eXn/d3d3/z21w/8K1u7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Fb/T/19rbf9jY2P/YWFh/1hnaf8GzeT/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/x3c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8hxt3/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAhKM6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFttOABab58AWm+qAFtvgQBZb0UAXXQLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICqDACCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/w+Xsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9X5/j/YeX0/2Ll + 9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9v5/X/cef1/3Hn + 9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/dej1/3bo9f926PX/duj1/3bo9f926PX/duj1/3bo + 9f926PX/duj1/3Xo9f916PX/dej1/3To9f9z5/X/c+f1/3Pn9f9y5/X/cef1/3Hn9f9w5/X/b+f1/2/n + 9f9u5/X/beb1/2zm9f9r5vX/aub0/2nm9P9o5vT/Z+X0/2bl9P9l5fT/Y+X0/2Ll9P9h5PT/YOT0/17k + 9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt7x/xNBRv870uT/Pd7x/zze8f853vH/ON3x/zfd8f813fH/Zb7J/5ub + m/9vtLz/Ltzw/y3c8P8r2/D/Kdvw/yjb8P8m2vD/J9jt/4WPkP+JiYn/h4eH/4WFhf9Zoar/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8avtH/bnN0/29vb/9ubm7/OZ2p/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/x/c8/9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u1uz/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6J7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaEuAIOh9gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wOGpP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Po + +v9f5fX/YuX0/2Pl9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n + 9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Ln9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn + 9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Ln9f9y5/X/cef1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m + 9f9s5vX/bOb1/2vm9f9q5vT/aeb0/2jm9P9n5fT/ZuX0/2Xl9P9k5fT/YuX0/2Ll9P9g5PT/X+T0/17k + 9P9d5PT/W+Pz/1rj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0rg + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/zCksv8wq7n/Pd7x/zze8f863vH/Od7x/zfd8f813fH/NN3x/1HE + 0/+UlJT/XL3J/y3c8P8r2/D/Ktvw/ynb8P8m2vD/Jdrw/yTa8P9zrbT/l5eX/5aWlv+UlJT/eZ2h/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y62xf9+fn7/fHx8/3V9fv8KzOP/ANTt/wDU + 7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P833fP/AYel/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKC6AKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWQAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8tvdT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Uej7/1zl9v9h5PT/XeDx/0rM3/9Dxdn/WNjp/2fl9P9n5fT/aOb0/2nm9P9q5vT/a+b1/2zm + 9f9s5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn + 9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/cOf1/2/n9f9v5/X/b+f1/27n9f9t5vX/bOb1/2zm + 9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/XuT0/17k + 9P9c5PP/W+Pz/1rj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Dc7v8yssD/Pt7x/zze8f863vH/Od7x/zfd8f823fH/NN3x/zLc + 8f9By9v/g4OD/0XE0/8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//WrnE/5mZmf+bm5v/nZ2d/5ei + o/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S625/4qKiv+IiIj/Ray4/wDU + 7f8A1O3/ANTt/yTe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P894vf/A4+s/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HgAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISinwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Wn7r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9A1ev/JazF/waIpv8Ag6H/AIOh/wKEo/89vtP/ZeX0/2bl9P9n5fT/aOb0/2nm + 9P9q5vT/aub0/2vm9f9s5vX/bOb1/2zm9f9t5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/b+f1/2/n + 9f9v5/X/b+f1/2/n9f9v5/X/b+f1/2/n9f9u5/X/buf1/27n9f9t5vX/bOb1/2zm9f9s5vX/a+b1/2rm + 9P9q5vT/aeb0/2jm9P9n5fT/Z+X0/2bl9P9l5fT/ZOX0/2Pl9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k + 9P9b4/P/WuPz/1nj8/9Y4/P/VuPz/1bj8/9U4vP/U+Lz/1Li8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8+2+z/Pt7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zLc + 8f8x3PD/NNXo/3Jycv8z0eT/K9vw/ynb8P8o2/D/Jtrw/yTa8P8j2u//Idrv/zzB0f+IiIj/ioqK/4yM + jP+Ojo7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7P92p63/l5eX/4qa + nP8H0en/ANTt/ybe9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5fn/BZa0/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIKhMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBopQAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Giqj/R97y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/S+T4/zDB2P8QmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/1rb7P9l5fT/ZeX0/2bl + 9P9n5fT/Z+X0/2jm9P9p5vT/aub0/2rm9P9q5vT/a+b1/2vm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm + 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9r5vX/a+b1/2rm9P9q5vT/aub0/2nm + 9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9k5fT/YuX0/2Ll9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk + 8/9b4/P/WePz/1nj8/9X4/P/VuPz/1Xi8/9U4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y7c8P9adHf/K9vw/ynb8P8o2/D/Jtrw/yXa8P8j2u//Idrv/yDZ7/8j0OT/d3d3/3l5 + ef97e3v/e35//wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Eszi/4+c + nv+bm5v/QrzL/yrf9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/CaC8/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoEYAg6H+AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8/1Or/IKzG/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof9Ex9r/YuX0/2Pl + 9P9k5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9p5vT/aub0/2rm9P9q5vT/aub0/2rm + 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aeb0/2nm9P9o5vT/Z+X0/2fl + 9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Pl9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9d5PT/XOTz/1vj + 8/9a4/P/WePz/1jj8/9W4/P/VuPz/1Ti8/9T4vP/UuLz/1Hi8/9P4fP/TuHz/03h8v9L4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y7c8P8t3PD/QI2W/yrb8P8p2/D/Jtrw/yXa8P8k2vD/Itrv/yDZ7/8b2e//BNXt/15u + cP9oaGj/ampq/2F3ef8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8yucn/ioqK/3ybnv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5/v/DqjC/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnBIAg6HkAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/HajC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/y/A + 1/8QmLP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T9Pl/2Dk + 9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl9P9l5fT/ZeX0/2bl9P9m5fT/Z+X0/2fl9P9n5fT/Z+X0/2jm + 9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2fl9P9n5fT/Z+X0/2fl9P9m5fT/ZuX0/2Xl + 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Ll9P9h5PT/YOT0/2Dk9P9f5PT/XuT0/13k9P9c5PP/W+Pz/1rj + 8/9Z4/P/WOPz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UuLz/1Dh8/9P4fP/TeHy/03h8v9L4fL/SuDy/0jg + 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/K9vw/zGruv8p2/D/J9vw/yXa8P8k2vD/Itrv/yHa7/8a2O//A9Xt/wDU + 7f9HaW3/V1dX/1paWv9CfYT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/B9bv/2mWm/97e3v/WtLh/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/D6zF/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhvQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GqAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Co+s/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/H6vF/wSI + pf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HqK8/17k + 9P9e5PT/X+T0/2Dk9P9g5PT/YeT0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9Q0OL/Pb3S/03N + 4f9l5fT/ZuX0/2bl9P9m5fT/ZuX0/2bl9P9m5fT/ZuX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZOX0/2Tl + 9P9j5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/1nj + 8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/0/h8/9O4fP/TeHy/0zh8v9K4PL/SeDy/0jg + 8v9H4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p0OT/J9vw/yba8P8k2vD/Itrv/yHa7/8W2O//AtTt/wDU + 7f8A1O3/Mmpw/0ZGRv9JSUn/J4yX/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Dtjv/0Xn+v9T1+f/aW5v/2Gdpf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/DavG/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AYSi/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+P3/y+/1/8Pl7L/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5Ov/1jg + 8P9b4/P/XOTz/13k9P9e5PT/XuT0/1/k9P9g5PT/YOT0/2Dk9P9h5PT/YuX0/2Ll9P8prMP/AIOh/wCD + of8Ag6H/IaO8/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/Y+X0/2Pl9P9j5fT/Y+X0/2Ll9P9i5fT/YuX0/2Ll + 9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1jj + 8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9N4fL/TeHy/0vh8v9K4PL/SeDy/0jg + 8v9G4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p2/D/J9vw/yba8P8k2vD/I9rv/yHa7/8P1+7/ANTt/wDU + 7f8A1O3/ANTt/yFsdf81NTX/ODg4/xOgsf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/GNry/0vo+/9P6fz/T+n8/1WbpP9bZWb/UOb4/0/p/P9P6fz/T+n8/0/p/P9G5/r/DKnE/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh4wCApBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKUfAIOi8ACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/ySyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+1On/H6rE/wSIpf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCat/8AutX/BdTr/zfd + 8f9Z4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/17k9P9e5PT/X+T0/2Dk9P9Gyd3/AIOh/wCD + of8Ag6H/AIOh/wCDof89wNX/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9g5PT/YOT0/2Dk + 9P9g5PT/X+T0/1/k9P9e5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Z4/P/WOPz/1bj + 8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9K4PL/SODy/0fg + 8v9F4PL/ReDy/0Pf8v9C3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/I9rv/xzZ7/8I1e7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Ub3n/JCQk/ycpKv8DyN//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Jd70/07p/P9P6fz/T+n8/0/p/P9O3u//S1ZX/0+5xv9P6fz/T+n8/0/p/P9F5vn/CqXB/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOhwACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6Vsf9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/y6+1v8PlrL/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKrF/wDJ4/8A1O3/ANTt/wDU + 7f8A1O3/Ftjv/0jg8v9Z4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XOTz/13k9P9e5PT/LbHJ/wCD + of8Ag6H/AIOh/wCDof8Ag6H/JanC/1/k9P9f5PT/X+T0/1/k9P9f5PT/X+T0/1/k9P9e5PT/XuT0/17k + 9P9e5PT/XuT0/13k9P9d5PT/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1bj + 8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/1Dh8/9P4fP/TuHz/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0bg + 8v9F4PL/RN/y/0Pf8v9B3/L/Qd/y/z/f8f8+3vH/PN7x/zve8f853vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/Itrv/xPX7v8B1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Cm56/xQUFP8RQ0n/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8H1u//OeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0WUnf9DeoH/T+n8/0/p/P9D5fn/CaK+/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCCoz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdQCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKGpP9B2O3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+j/HqrE/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCbt/8Autb/ANPs/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/JNrw/1Dh8/9Y4/P/WOPz/1nj8/9Z4/P/WuPz/1rj8/9b4/P/W+Pz/yWq + w/8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGlvv9d5PT/XeT0/13k9P9d5PT/XeT0/13k9P9c5PP/XOTz/1zk + 8/9c5PP/W+Pz/1vj8/9b4/P/WuPz/1rj8/9Z4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9W4/P/VeLz/1Ti + 8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9O4fP/TeHy/0zh8v9L4fL/SuDy/0ng8v9I4PL/R+Dy/0Xg + 8v9F4PL/Q9/y/0Lf8v9B3/L/QN/y/z7e8f893vH/PN7x/zve8f853vH/ON3x/zfd8f813fH/NN3x/zLc + 8f8x3PD/MNzw/y/c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/yba8P8f2e//ENfu/wPV7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wBue/8DAwP/AnSB/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8X2/H/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N3e//MENG/0/n+v864vf/Bpq3/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCBo0UAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD + ofkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/SuP2/y691f8OlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKvG/wDK5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1e3/L9zw/1Ti8/9W4/P/V+Pz/1jj8/9Y4/P/WePz/1nj + 8/8lqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8dp8H/WOT2/1vj8/9b4/P/W+Pz/1vj8/9a4/P/WuPz/1rj + 8/9a4/P/WePz/1nj8/9Z4/P/WePz/1jj8/9Y4/P/V+Pz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Pi + 8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0vh8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg + 8v9E3/L/Q9/y/0Hf8v9B3/L/P9/x/z7e8f893vH/PN7x/zre8f853vH/N93x/zfd8f813fH/NN3x/zLc + 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/x/Z7/8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AdYP/AAAA/wCvw/8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU + 7v8u4PX/T+n8/0Xc8P81x97/PNDm/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zJ4gf8qscL/AZCt/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfCACD + odMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P890uj/HqrD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AYSi/xehvP8nxt3/B9Xu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8M1u7/NN3x/1Pi8/9W4/P/VuPz/1bj + 8/9W4/P/JKrD/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9R6Pr/VuX1/1nj8/9Y4/P/WOPz/1jj + 8/9Y4/P/V+Pz/1fj8/9X4/P/VuPz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li + 8/9R4vP/UOHz/0/h8/9P4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0fg8v9G4PL/ReDy/0Tf + 8v9D3/L/Qt/y/0Hf8v9A3/L/Pt7x/z3e8f883vH/O97x/zne8f853vH/N93x/zbd8f813fH/M93x/zLc + 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/Itrv/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AH2L/wAZHP8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y + 7/9B5vr/T+n8/yy80/8BhaL/AIOh/wCDof8Vn7n/Tef6/0/p/P9P6fz/T+n8/07o/P8dtMj/BkpZ/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDolIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oY0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiqf/Rt7x/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/SuL2/y291f8OlLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Jj6v/J7bP/0bd8f9P6fz/T+n8/0no+/8Z2/L/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u7/MNzw/1Hi + 8/9U4vP/VOLz/yOqw/8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p/P9R5/r/VOX1/1bj + 8/9W4/P/VuPz/1Xi8/9V4vP/VeLz/1Ti8/9U4vP/VOLz/1Pi8/9T4vP/UuLz/1Li8/9R4vP/UeLz/1Dh + 8/9P4fP/T+Hz/07h8/9N4fL/TeHy/0zh8v9K4PL/SuDy/0ng8v9I4PL/R+Dy/0bg8v9F4PL/RN/y/0Pf + 8v9C3/L/Qd/y/0Df8v8/3/H/Pt7x/z3e8f883vH/Ot7x/zne8f843fH/N93x/zXd8f813fH/M93x/zLc + 8f8w3PD/L9zw/y3c8P8t3PD/K9vw/yrb8P8n2/D/E9fu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCImP8AV2L/ANTt/wDU7f8A1O3/AtTt/yfe + 9f9N6fv/T+n8/0Ta7v8BhKL/AIOh/wCDof8Ag6H/AIOh/yq50f9P6fz/T+n8/0rn+/8Vw9z/AIaj/wA8 + Sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCFoEsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + okIAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8bc/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P890ef/HanD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AYSi/xihvP83y+H/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8H1e3/K9vw/03h8v8iqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p + /P9Q5/r/U+P1/1Pi8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Li8/9R4vP/UeLz/1Dh8/9P4fP/T+Hz/0/h + 8/9O4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Lf + 8v9B3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f873vH/Od7x/zne8f833fH/Nt3x/zXd8f803fH/Mtzx/zHc + 8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8a2O//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Aj6D/AIyc/wDU7f8A1O3/Fdnx/0Pm + +f9P6fz/T+n8/0/p/P82yd//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T+n8/0Lm+v8MsMv/AIOh/wCD + of8AVWn/AHqW/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCDoUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH + pREAg6HiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKbB/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/y29 + 1P8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKH/FqrE/0bd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/xja + 8v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8E1e3/DabB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P5/n/UOT0/1Hi8/9Q4fP/UOHz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TeHy/03h + 8v9M4fL/TOHy/0vh8v9K4PL/SuDy/0ng8v9I4PL/SODy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf + 8v9B3/L/P9/x/z7e8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc + 8P8w3PD/Ltzw/y3c8P8r2/D/K9vw/yHa7/8K1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJao/wCzyP8I1u//M+L3/0/p + /P9P6fz/T+n8/0/p/P9P6fz/MsTb/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqn/y7e8/8Dm7f/AIOh/wCD + of8Ag6H/AH6b/wBkev8Ag6H/AIOh/wCDof8Ag6H/AIOh7QCCoDsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6GmAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY6s/0ri9v9P6fz/T+n8/0/p/P880Of/HajC/wKG + pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ats7/S+f7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/N+P3/wnW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5vn/TuL0/03h8v9N4fL/TeHy/03h8v9M4fL/TOHy/0vh + 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Df + 8v8/3/H/Pt7x/z3e8f883vH/PN7x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8y3PH/Mdzw/zDc + 8P8v3PD/Ldzw/y3c8P8r2/D/JNrw/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wGqv/8h2vH/Suf7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3S6P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ai6j/AIOh/wCD + of8Ag6H/AIOh/wCDof8AaYH/AIOh/wCDof8Ag6H/AISh3wCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKBZAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zvP5f9P6fz/SeL1/yy80/8Mk7D/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wybt/9B4vf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9L6Pv/JN30/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMH/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vj/S+L0/0rg8v9K4PL/SuDy/0ng + 8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Hf8v9B3/L/QN/y/z/f + 8f8+3vH/Pd7x/zze8f883vH/Ot7x/zne8f843fH/N93x/zbd8f813fH/NN3x/zPd8f8y3PH/MNzw/zDc + 8P8u3PD/Ldzw/yzb8P8l2vD/ENfu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8v8/3vL/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AHSP/wCDof8Ag6H/AIOgzQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yOwyv87z+b/HKfC/wKFpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoMEAg6DtAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4in/y7Q5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/Gtvy/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKTB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5fj/SeH0/0jg + 8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v8/3/H/Pt7x/z7e + 8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/N93x/zXd8f813fH/M93x/zLc8f8x3PD/MNzw/y/c + 8P8t3PD/LNvw/x7Z7/8N1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/z7l+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wB/nP8Ag6H/AIKgsgCAqgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA//8BAIOhvQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8KkK3/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKHfAIOiewCAnRoAAAAAAICkHACD + ocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fq3G/0fm+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+5fn/Etnw/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn + wf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9K5fj/RuHz/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pd7x/zze + 8f883vH/O97x/zre8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/yzb + 8P8g2e//Etfu/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8R2PD/OuT4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKigwCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIShcgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoa0AgaJHAICAAgAAAAAAAAAAAAAAAAAA + AAAAjqoJAIOhqACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8GjKr/Ndbs/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P854/j/Etnw/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Ap8T/AIOh/wCDof8Ag6H/AIOh/wCD + of8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07p/P9J5ff/Q+Dz/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pt7x/z7e8f893vH/PN7x/zve + 8f863vH/Od7x/zne8f843fH/N93x/zbd8f813fH/Nd3x/zPd8f8y3PH/Mdzw/yjb8P8g2e//Fdju/wvW + 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xfa8f864/j/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HanD/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HzAIOhVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIWiLACDofcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKLdAIOgeQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqFsAIOh+ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Yr8j/RuX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P865Pj/F9rx/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKnF/wCDof8Ag6H/AIOh/wCD + of8Ag6H/GKG8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo+/844fX/I9rw/yHa7/8j2u//JNrw/yTV6v8bvtb/HcTb/yja + 7/8q2/D/Kdvw/yjb8P8l2vD/Itrv/yDZ7/8c2e//F9jv/xLX7v8N1u7/B9Xt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/IN3z/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq40P8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICqBgCDotIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoasAgaNFAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqgYQAfZr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wSJpv8szOL/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9E5vn/It30/wTV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wCpxf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/8n3vX/Bdbu/wC40v8Ah6T/AIOh/wCD + of8AkrD/AM/o/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrW7/8q3/X/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P81x97/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKCfAIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoYoAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HcAIOhdwCFphcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/KAFxy/wB1kP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w+duP873fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/L+H2/xLZ8P8A1O3/ANTt/wDU7f8AqcX/AIOh/wCD + of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3Y7f8BhqT/AIOh/wCD + of8Ag6H/AIOh/wCivv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8G1e7/INzz/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEofQAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACGpCoAg6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh+QCDoakAg6FEAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXG0vAFpv/wBab/8AWm//AGiB/wCAnv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmwyf9E4/f/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/ROb5/yfe9f8K1u//AKnF/wCD + of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8QmLP/AIOh/wCD + of8Ag6H/AIOh/wCDof8Aj6v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AtTt/xnb8v824vf/Ten8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl + +P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKJ+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HaAISidgCAohYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvlQBab/8AWm//AFpv/wBab/8AXnT/AHeT/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/yG81f9I5vn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x6w + yv8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ots7/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV + 7v8b2/L/MuH2/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/CY6s/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKiaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhmACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+ACD + oKcAg6JCAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBab+8AWm//AFpv/wBab/8AWm//AFpv/xaB + lv8xwdn/BYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4im/yPB + 2v9H5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AoWj/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC30f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8Q2PD/J971/z3k + +f9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HYAISgdACG + nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbbl8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/81us7/T+n8/0jh9P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Aoel/yG/2P9G5fn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vn/DZSw/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCduP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v8U2fH/Jd70/zXi9/9I5/v/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/SeH1/0/p + /P9P6fz/T+n8/0nn+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8grcb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6G1AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISieACDofUAg6H/AIOh+ACDoaYAg59AAP//AQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/EAFpv/wBab/8AWm//AFpv/wBa + b/8HZnv/Teb5/0/p/P9P6fz/T+n8/zvP5v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x+40f9C4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/JLHK/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJqP8Azef/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/B9bv/xLZ8P8h3fT/MuH2/0Lm+f9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0LY7f8QmLP/AIOh/wGF + ov8eq8T/Od/1/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/LLzT/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8QAICeIgCApA4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW20qAFpv/wBab/8AWm//AFpv/wBa + b/8AWm//JZ2x/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/yu70/8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xaowv842e7/Tun8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PNDn/wGE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AutX/ANTt/wDU7f8D1e7/B9bv/wzX8P8Q2fD/Fdnx/xrb + 8v8i3fT/LOD1/zfj9/9B5vr/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8Lkq7/AIOh/wCD + of8Ag6H/AIOh/xehvP9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfL4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAmZkFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlvjwBab/8AWm//AFpv/wBa + b/8AWm//AFtw/0PT5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuL2/ySxyv8ChqT/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuUsP8syeD/SOX5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/wuR + rv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8dqcP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D2e7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEltBwBab+wAWm//AFpv/wBa + b/8AWm//AFpv/xR+k/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rt3x/x2o + wv8BhKL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKF + o/8YqML/NdTq/03o+/9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x+r + xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Lm+f8m3fP/CLbR/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb1oAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8zt8r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/RNru/x2pw/8BhKH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Giqj/HbHK/zbV6/8kssv/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/znM + 4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Os3k/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/K97z/xDG3f8Aor7/AIim/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yy91P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq//AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKFyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm++AFpv/wBa + b/8AWm//AFpv/wBab/8GZHr/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0zj9v8glKn/AGB3/wBzjf8AgqD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0ni + 9f8Ijqv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/N+P3/yDX7/8KutT/AJu4/wCGpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8Xb/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AISglwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV3ImAFpv/gBa + b/8AWm//AFpv/wBab/8AWm//I5it/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/z3J3f8LboP/AFpv/wBab/8AWm//AF90/wBxjP8AgZ7/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xeh + vP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8cpsH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Ku/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9L6Pv/O+T4/yrb8f8UxNz/AqTB/wCPrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/87z+X/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yOwyv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpviABa + b/8AWm//AFpv/wBab/8AWm//AFpv/0LS5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Teb5/yWesv8BXHH/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1x/wBthf8Afpv+AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P81x97/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/z3S6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p + /P8+5fn/Lt7z/x3P5/8NuNP/AZ+8/wCOrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/z7T + 6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8vv9f/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFWABgBa + b+kAWm//AFpv/wBab/8AWm//AFpv/xJ7j/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/QdDk/w90if8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXDQAFpwMACE + oU8AhKC6AIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9I3/P/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/9A5fr/NeL3/yvc8f8g0ej/FsLa/wuw + y/8CnLj/AI6r/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Hi6n/RNrv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZ + b1MAWm//AFpv/wBab/8AWm//AFpv/wBab/8xs8f/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9O6Pv/Kqa6/wJec/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/wAWm+IAFWABgAA + AAAAAAAAAAAAAACAoh4Ag6F9AIOh3ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSPrP8cyN//Gsnh/x/L5P8gzub/IM/n/yTS + 6f8l0en/JM/m/x/M5P8ayOD/FcLa/xG61P8Nsc3/CKrF/wOfu/8AlbL/AIup/wCEov8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDocEAhKKkAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuSrv9G3fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be + 8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWm+4AFpv/wBab/8AWm//AFpv/wBab/8FY3f/TOT2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9E1ej/E3yQ/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab9wAWG46AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAhKA+AIShmwCDoesAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/McHZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HQAIOhdwCAnyAAAAAAAAAAAACCoYcAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/DpWx/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/A4el/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWHAgAFpv/QBab/8AWm//AFpv/wBab/8AWm//IZaq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o + +/8vsMP/BGF2/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/gBbb5gAXXQLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiQgCDoJQAg6HlAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Rd3x/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqD/AIOhywCDoX8Ag6IpAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOipACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/Tef6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvgwBab/8AWm//AFpv/wBab/8AWm//AFpv/0DO4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0fb + 7v8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv5gBZbkgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCD + oaAAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIGf/wB2kf8AaoL/AGB4lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq + qgMAg6GxAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xiivf9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GiAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAECABABZb+QAWm//AFpv/wBab/8AWm//AFpv/xB3jP9P6fz/T+n8/0/p/P9P6fz/T+n8/zW5 + zf8GZHr/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpvqABVcRIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/y291P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/wB3 + kv8AbYb/AGN6/wBbcP8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICZCgCDocgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/JbTN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhxgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABZcU0AWm//AFpv/wBab/8AWm//AFpv/wBab/8vsMP/T+n8/0/p/P9P6fz/St/y/xyM + oP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/tAFpuWAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0PZ7v8DhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8DhqT/EZm1/yGvyP8xwtr/Qdfs/y+x + xf8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhp4VAIOh1ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ot8//T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+zAFpv/wBab/8AWm//AFpv/wBab/8EYXb/S+L1/0/p/P9P6fz/OcLV/whp + fv8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AW2+3AF5xGwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07n+/8RmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ChaP/Co+s/xOctv8fq8X/K7rS/zjL4f9E2u//Tuj7/0/p/P9P6fz/T+n8/0/p + /P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AW2/IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/ + 1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICcEgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAW20cAFpv+wBab/8AWm//AFpv/wBab/8AWm//H5Km/0/p/P9M5Pb/IZWp/wBb + cP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/QAWnBpAAAAAQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8puM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh9gCCon4Ag6F/AIKikQCD + oaMAgqG2AIOhuwCDobsAg6HLAIOhzACDocwAg6HMAIOhzACDosoAg6G7AIOhuwB3lPIAdI7/AHGK/wBu + h/8Aa4T/AGd//wt3jP9H3/P/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFlwiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8BhKL/N8rg/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC + oTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFpufQBab/8AWm//AFpv/wBab/8AWm//AFpv/z/M3/89yd3/C2+E/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab8YAXHEkAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9A1ev/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoGEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW252AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//P8zg/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqI3AIOh9QCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wKGpP87z+X/T+n8/0/p/P9P6fz/T+n8/0/p/P8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBacOAAWm//AFpv/wBab/8AWm//AFpv/wxxhv8mnrP/AV1x/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+QBbcHkAVVUDAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9N5/n/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFxwGQBa + b/sAWm//AFpv/wBab/8AWm//AFpv/yGWqv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoE4Ag6H8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/BYmn/0HX7P9P6fz/T+n8/0/p/P9P6fz/Epq1/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFlv0wBcbS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/JbLL/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoesAgJ8YAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW2+0AFpv/wBab/8AWm//AFpv/wBab/8GZHn/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiYwCD + of4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/RNvv/0/p/P9P6fz/T+n8/x2owv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+tAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/9AFlwiQBJbQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/PdHn/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtwVABab/8AWm//AFpv/wBab/8AWm//AFpv/zS4zP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFpviAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuSrv9I4fT/T+n8/0/p/P8puND/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW8XAFpv+gBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/dAFlvPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xeh + vP9P6fz/T+n8/0/p/P9P6fz/TOX4/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GlAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABVgAYAWm/sAFpv/wBab/8AWm//AFpv/wBab/8Xg5f/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0zl+P9P6fz/NMfd/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlveABab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnCZAGJ2DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD + of8Xobz/T+n8/0/p/P9P6fz/T+n8/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HlAIelEQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFtwkgBab/8AWm//AFpv/wBab/8AWm//AVxx/0fa7f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgIACAIOipACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/Tef6/0DW + 6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBab9sAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab+YAWHBLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD + of8Ag6H/F6G8/0/p/P9P6fz/T+n8/znN4/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOfSAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYbTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8qpbn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjaH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn + wv9L5Pf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZcEIAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab6oAXmsTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCD + of8Ag6H/AIOh/xehvP9P6fz/T+n8/0ri9v8Jj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhmAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlw0ABab/8AWm//AFpv/wBab/8AWm//DHCF/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/JLHK/wiOq/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2+mAFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv7gBbb1oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCD + of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P8dqML/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi3QCA + qgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabm8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/89yNz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBb + cEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhp4VAISh2wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv+ABab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpvugBecRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCD + of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P82yN//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACC + oz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVW0VAFpv+QBab/8AWm//AFpv/wBa + b/8AWm//H5Kn/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoyQAg6HqAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhswAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdyJgBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/1AFlvagAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9I4PT/B4up/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb64AWm//AFpv/wBa + b/8AWm//AFpv/wRid/9M5Pb/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjKH/AFpv/wBab/8AWm//AFpv/wBa + b/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD + ovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWm/wAFpv/wBa + b/8AWm//AFpv/wBab/8AW2/IAFpuJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/GaO9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + odQAgJ8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXFNAFpv/wBa + b/8AWm//AFpv/wBab/8AWm//MrTI/0/p/P9P6fz/T+n8/0/p/P9P6fz/CGl+/wBab/8AWm//AFpv/wBa + b/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6JCAIOh+QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H6AIC/BAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpuXQBa + b/4AWm//AFpv/wBab/8AWm96AECABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofoAhaMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABa + b+gAWm//AFpv/wBab/8AWm//AFpv/xR9kv9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBZbkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACEoVcAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCA + oyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW3EtAFtuhABabn0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6F/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnCLAFpv/wBab/8AWm//AFpv/wBab/8AW3D/Rdfp/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6LKAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFdwKQBab/8AWm//AFpv/wBab/8AWm//AFpv/yehtf9P6fz/T+n8/0/p/P8cjKH/AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H3AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm/HAFpv/wBab/8AWm//AFpv/wBab/8KbIH/Tuj7/0/p/P9P6fz/CGl+/wBa + b/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCEoJcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCEopkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOgcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFlvZwBab/8AWm//AFpv/wBab/8AWm//AFpv/zvE2P9P6fz/RNXo/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOhsQCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhwACAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8AWm/2AFpv/wBab/8AWm//AFpv/wBab/8djqL/T+n8/zCx + xP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HDAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh8gCDoiEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvpQBab/8AWm//AFpv/wBab/8AWm//A191/0vh + 9P8cjKH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD + oe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDockAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCCoWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb0UAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8wscT/CGl+/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgKQcAIOh4wCDof8Ag6H/AIOh/wCDof8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDobUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAFpv4QBab/8AWm//AFpv/wBa + b/8AWm//B2Z7/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACFphcAhKGdAIOh/QCDoesAg6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoe0AhaMZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab4MAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAXXQLAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDonsAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqBWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV20jAFpv/gBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6GpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b8EAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oZ4Ag6H/AIOh/wCDof8Ag6HQAICfEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnBgAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOgRgCDoXcAhKBbAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFVqDABab/MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/oAXXQLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+fAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP/////////////A///////gB////////////////////////////////8B//////8AH + ////////////////////////////////wD//////wAP///////w////////////////////////AH/// + ///AA///////+D///////////////////////8AP/////4AD///////gH/////////////////////// + wA//////gAP//////8Af///////////////////////AB/////8AA///////gD////////////////// + /////+AD/////wAB//////8AP///////////////////////4AH/////AAH//////gA///////////// + ///////////gAP////4AAf/////8AD///////////////////////+AAf////gAB//////AAf/////// + ////////////////4AA////8AAH/////4AB////////////////////////gAB////wAAP/////AAH// + //////////////////////AAD////AAA/////4AAf///////////////////////8AAH///gAAAA//// + AAD////////////////////////wAAP/+AAAAAAD//4AAP////////////////////////AAAf8AAAAA + AAAf/AAA////////////////////////8AAA8AAAAAAAAAHwAAH////////////////////4f//wAAAA + AAAAAAAAACAAAf////////////////////Af//gAAAAAAAAAAAAAAAAB////////////////////8Af/ + +AAAAAAAAAAAAAAAAAH////////////////////wA//4AAAAAAAAAAAAAAAAA/////////////////// + //AA//gAAAAAAAAAAAAAAAAD////////////////////+AB/+AAAAAAAAAAAAAAAAAP///////////// + ///////4AB/4AAAAAAAAAAAAAAAAB/////////////////////wAB/wAAAAAAAAAAAAAAAAH//////// + /////////////AAD+AAAAAAAAAAAAAAAAAf////////////////////+AADgAAAAAAAAAAAAAAAAA/// + //////////////////4AAAAAAAAAAAAAAAAAAAAA/////////////////////wAAAAAAAAAAAAAAAAAA + AAA/////////////////////AAAAAAAAAAAAAAAAAAAAAB////////////////////+AAAAAAAAAAAAA + AAAAAAAAB////////////////////4AAAAAAAAAAAAAAAAAAAAAD////////////////////wAAAAAAA + AAAAAAAAAAAAAAD/////////////h//////AAAAAAAAAAAAAAAAAAAAAAH////////////8B/////8AA + AAAAAAAAAAAAAAAAAAAAH////////////wB/////4AAAAAAAAAAAAAAAAAAAAAAP////////////AB// + ///gAAAAAAAAAAAAAAAAAAAAAAf///////////8AD////8AAAAAAAAAAAAAAAAAAAAAAAf////////// + /4AD////gAAAAAAAAAAAAAAAAAAAAAAA////////////gAD///8AAAAAAAAAAAAAAAAAAAAAAAB///// + ///////AAD///gAAAAAAAAAAAAAAAAAAAAAAAD///////////+AAD//8AAAAAAAAAAAAAAAAAAAAAAAA + D///////////4AAD//gAAAAAAAAAAAAAAAAAAAAAAAAH///////////wAAD/8AAAAAAAAAAAAAAAAAAA + AAAAAAP///////////AAAD/gAAAAAAAAAAAAAAAAAAAAAAAAAf//////////+AAAD8AAAAAAAAAAAAAA + AAAAAAAAAAAA///////////4AAADgAAAAAAAAAAAAAAAAAAAAAAAAAB///////////wAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAD///////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////////+AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAP//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////// + //+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH// + ////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAf//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAB//////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////gAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAH//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////8AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAf/////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////gH///gAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAD////+AB///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////wAB//+A + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////AAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///8A + AD//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////wAAH/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD + ////gAAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///+AAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP///4AAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gAAD+AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAB///+AAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4AAAfAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAH///gAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//+AAADgAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAA///4AAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///gAAA4AAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAD//+AAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//4AAAMAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//gAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8A + AACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//wAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAD/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAD+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAHAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAB/gAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAB/4AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAB + /+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AD8AB//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAP/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA/AB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8Af//gAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAfwD//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/A///gAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP8H//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/x///AAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAH/f//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///+AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///8AAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + A///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/4AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/+AAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAA//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//wAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////4AA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///// + ///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + f///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAH///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAD///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAB///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////+AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAA///////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////// + /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/ + //////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA///////AAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////8AAH/gAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAf//////wAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////gH//8AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAf///////H///4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////wAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAP////////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////////////+AAAAA + AAAAAAAAAAAAAAAAAAAAAAAAP////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////////// + gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + /////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/// + //////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAA + AA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAAAAAAA + AAAAAAA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAA + AAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////gAAAAAAAAAAA + AAAAAAAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wAAAAAA + AAAAAAAAAAAAAAAAAAAAAA/////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wA + AAPAAAAAAAAAAAAAAAAAAAAAAAA//////////////AAAD+AAAAAAAAAAAAAAAAAAAAAAAP////////// + ///4AAB/+AAAAAAAAAAAAAAAAAAAAAAB//////////////AAAf/4AAAAAAAAAAAAAAAAAAAAAAP///// + ////////4AAP//gAAAAAAAAAAAAAAAAAAAAAD//////////////gAD//+AAAAAAAAAAAAAAAAAAAAAAf + /////////////+AB///wAAAAAAAAAAAAAAAAAAAAAB//////////////wAf///AAAAAAAAAAAAAAAAAA + AAAAD//////////////gP///8AAAAAAAAAAAAAAAAAAAAAAP//////////////D////gAAAAAAAAAAAA + AAAAAAAAAA///////////////////+AAAAAAAAAAAAAAAAAAAAAAD///////////////////wAAAAAAA + AAAAAAAAAAAAAAAP///////////////////AAAAAAAAAAAAAAAAAAAAAAA///////////////////8AA + AAAAAAAAAAAAAAAAAAAAD///////////////////gAAAAAAAAAAAAAAAAAAAAAAH//////////////// + //+AAAAAAAAAAAAAAAAAAAAAAAf//////////////////wAAAAAAAAAAAAAAAAAAAAAAB/////////// + ////////AAAABgAAAAAAAAAAAAAAAAAH//////////////////8AAAAPwAAAAAAAAAAAAAAAAAf///// + /////////////gAAAD/wAAAAAAAAAAAB4AAAB//////////////////+AAAAf/4AAAAAAAAAAA/wAAAH + //////////////////wAAAH//4AAAAAAAAAAH/gAAAP//////////////////AAAA///gAAAAAAAAAA/ + /AAAA//////////////////8AAAP//+AAAAAAAAAAD/+AAAD//////////////////gAAB///4AAAAAA + AAAAP/8AAAP/////////////////+AAAf///gAAAwAAAAAA//4AAA//////////////////4AAD///+A + AAH//wAAAH//wAAD//////////////////AAA////4AAAf//AAAAf//gAAH/////////////////8AAH + ////gAAD//8AAAB///AAAf/////////////////gAA////+AAAf//4AAAH//+AAB//////////////// + /+AAP////4AAB///gAAA///4AAH/////////////////4AB/////gAAP//+AAAD///wAAf////////// + ///////AAf////+AAB///8AAAP///gAB/////////////////8AD/////4AAH///wAAA////AAH///// + ////////////gA//////gAA////gAAH///+AAP////////////////+AH/////+AAH///+AAAf///8AA + /////////////////4B//////4AAf///4AAB////4AD/////////////////gP//////gAD////wAAH/ + ///wAP/////////////////D//////+AAf////AAA/////gA/////////////////+///////4AD//// + 8AAD/////AD/////////////////////////gAP////4AAP////8AP////////////////////////+A + B/////gAA/////4Af////////////////////////4AP/////AAH/////wB///////////////////// + ////gA/////8AAf/////gH////////////////////////+AH/////wAB//////Af/////////////// + /////////4A//////gAH/////+D/////////////////////////gD/////+AA//////8f////////// + //////////////+Af/////4AD////////////////////////////////8D//////wAP//////////// + ////////////////////wP//////AA/////////////////////////////////B//////+AH/////// + /////////////////////////////////4Af////////////////////////////////////////gB// + ///////////////////////////////////////AH///////////////KAAAAIAAAAAAAQAAAQAgAAAA + AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgJwSAIOh+gCDof8Ag6H/AIKjLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvrwBab/8AWm//AFpv/wBa + b/8AWm+eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuiCwCZmQUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6H/AIOh/wCDof8Ag6H4AIOfJQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABecRsAWm/7AFpv/wBab/8AWm//AFpv/wBab9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoK8Ag6H/AIOh2gCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpufwBab/8AWm//AFpv/wBa + b/8AWm//AFpv/QBidg0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAg6GYAIOh/wCDof8Ag6H/AIGgQwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCD + of8Ag6HaAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABAgAQAWm/iAFpv/wBab/8qprr/BmR6/wBab/8AWm//AFlwQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgKoMAIOhtQCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKGbAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HPAIiZDwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBab/8AWm//Al1y/0jd + 8P8Xg5f/AFpv/wBab/8AWW94AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCDoM0Ag6H/AIOh/wCDof8Ag6H/AIOh8wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HDAICZCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+1AFpv/wBab/8bi6D/T+n8/yehtv8AWm//AFpv/wBZb64AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF + oiwAg6HhAIOh/wCDof8Ag6H/AIOh/wCDof8AhKKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/CY+s/yWzzP8Ag6H/AIOh/wCDof8Ag6K3AICqBgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFprHwBab/0AW3H/AF1z/zO9 + 0v9D2e7/LLPJ/wBief8AYXf/AF926QCAoh4AkpIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOi8ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + olIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQcAIOh/wCD + of8Ag6H/SuP2/yGtx/8Ag6H/AIOh/wCDof8Ag6GpAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAICZCgCC + oDsAg6JrAIOhkACDoK8AfpvnAICd/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HqAIOi0gCCoLIAgqCJAIKiWgCFoiwAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShXwCD + ofkAg6H/AIOh/wGEov8grMb/AIOh/wCDof8Ag6H8AIiZDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYFAFxwGQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6LwAIOh/wCDof8+0+j/Tuj7/xynwv8Ag6H/AIOh/wCD + of8Ag6GaAP//AQCAnxgAgqJgAIOhngCDotIAg6H7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofUAhKC6AIOhdwCCnzUAgIACAP//AQCDoX8Ag6H+AIOh/wCDof8EiKX/OMvh/yq50f8Ag6H/AIOh/wCD + ocMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABZb78AWm//AFpv/QBabz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + ocYAg6H/AIOh/zHB2f9P6fz/Tef6/xihvP8Ag6H/AIOh/wCDof8Ag6LoAIOh/gCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoeUAg6HWAIOh/wCD + of8Ag6H/CI2q/z/U6v9P6fz/FJ24/wCDof8Ag6H/AIOhfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW0HAFpv/wBab/8AWm//AFpv/wBZ + cKAAWXMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgnACDof8Ag6H/JLHK/0/p/P9P6fz/TOX4/xSc + uP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wCOrP8AmLT/AKK9/wCq + xf8Cr8r/BbXQ/wW30f8Du9X/ALnU/wC10P8As87/ALHN/wCrxv8Aob3/AJm2/wCPq/8AhKL/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w2UsP9G3fH/T+n8/0vk+P8ChaT/AIOh/wCD + of8AhKE2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm/4AFpv/wBab/8AWm//AFpv/wBab+wAWm9jAAAAAQAAAAAAAAAAAAAAAAAA + AAAAhKFyAIOh/wCDof8WoLv/T+n8/0/p/P9P6fz/SuP2/xCYtP8Ag6H/AIOh/wCDof8Ag6H/AIWj/wGX + s/8Nr8z/Gsff/yfY7/804vf/PeT5/0Lm+f9F5fj/OMzh/yayyv9M5fj/T+n8/0/p/P9P6fz/TOj7/0rn + +/9F5/r/PuX5/zPi9/8o3/T/Htzz/xPW7v8GxN7/ALLO/wCeuv8Aiaf/AIOh/wCDof8Ag6H/AIOh/wCD + of8Unbj/SuL2/0/p/P9P6fz/OMvh/wCDof8Ag6H/AIOh7QCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabz4AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/DAF1uLAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wmPrP9P6fz/T+n8/0/p + /P9P6fz/SOH0/w2TsP8Ag6H/AIOh/wSLqf8+5Pn/Ten8/0/p/P9P6fz/T+n8/03m+v82x93/G6O8/wSF + oP8Af5v/AH+b/zC/1f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9B5vr/L+D2/xfK4v8Biqj/AIOh/wCDof8Ag6H/HanD/03n+v9P6fz/T+n8/0/p/P8hr8j/AIOh/wCD + of8Ag6CnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb8UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AFpviABm + ZgoAgqExAIOhxgCDof8Ag6H/AIOh/0vk9/9P6fz/T+n8/0/p/P9P6fz/Rt3y/wCDof8Ag6H/FZ+5/0/p + /P9P6fz/T+n8/0ri9v8otcz/CIql/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpKs/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P880Ob/BIil/wCDof8Ag6H/AIOh/ye1 + zv9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCEobgAgJ8QAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBa + b/8AWm//AFpv/w92iv8AWm//AFpv/wBab/8AXnT/AHSP8wCDof4Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p + /P9P6fz/T+n8/0/p/P9P6fz/S+T4/zrO5P9N5/n/T+n8/0zl+P8qt87/BYei/wB/m/8Af5v/AH+b/wB/ + m/8djqb/NZqw/wB/m/8Af5v/Hr7U/ynT6P8s1ej/L9bq/zPY7P883fD/R+P1/07m+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/xiivf8Ag6H/AIOh/wKFo/8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/AIOh/wCD + of8Ag6H/AIOh/wCDoesAg6BpAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAFlv0wBab/8AWm//H5Gm/zrC1v8Lb4P/AGuE/wCB + nv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xwtn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P890uf/Co+q/wB/m/8Af5v/AH+b/wGAm/85nrb/j87f/7ro9/+Lydn/EYii/wB/m/8Ak67/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/BcLY/w7F2f8Yy+D/J9Pn/zrc8P9L5fj/Lb3U/wCDof8Fiqf/Os7k/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HFAIOiKQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWW9cAFpv/wBab/8CXXL/Ps/j/xOct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weVsf8Ag6H/AIOh/ySx + yv9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+/8y3/T/EK/I/wCAnP8Af5v/AH+b/wB/m/9ApLv/pdzs/8fw + /v/I8P7/yPD+/7Xk8/86nLL/AH+b/wCAnP8At8//AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wLA1/8Rx9z/H8ng/zXY7f9O5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GaO9/wCD + of8Ag6H/AYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H4AIShegCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6B5AIOh5QCDor8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWnDgAFpv/wBqgv8ChKL/AIOh/wCD + of8Ag6H/AIOh/wqfvP823fL/LLzT/wCDof8Ag6H/F6C7/0/p/P9P6fz/S+j7/zHh9v8S0+n/Acbd/wCf + uP8Af5v/AH+b/wB/m/8RiaT/jtDj/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/3rA0f8EgZ3/AH+b/wCb + tf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wbE + 2f8e0Ob/OuDz/07o/P9P6fz/T+n8/0/p/P8Fiqf/AIOh/wCDof8zy+L/HMXd/wCJp/8Ag6H/AIOh/wCD + of8Ag6H/AIOhxgCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDofYAg6H/AIOh/wCD + of8Ag6GoAIahJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABecnIAdI//AIOh/wCDof8Ag6H/AIOh/wGMqP8nz+f/TOj7/0/p/P83yuD/AIOh/wCD + of8Lkq7/ROb5/yLc8/8Ezeb/AMTb/wDB2P8Apb//AH+b/wB/m/8Af5v/NJ63/7bn9//H8P7/yPD+/8jw + /v/G8P7/uuz+/8jw/v/I8P7/ptzs/yeTqv8Af5v/AIKe/wC81P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/BsXc/yHT6f9E5fj/T+n8/xKa + tf8Ag6H/CI6r/03n+f9P6fz/PuP4/xGvyf8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACColgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOh8gCDof8Ag6H/AIOh/wCDof8Ag6H7AIShmwCEnh0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh0QCDof8Ag6H/AIOh/wCD + of8NqMT/P+P4/0/p/P9P6fz/T+n8/0fe8v8Ag6H/AIOh/wicuf8Bzub/AMTb/wDC2f8Awtn/ALTM/wCA + nP8Af5v/AH+b/0Glvv+96/z/xO/+/8bw/v/H8P7/vO3+/6bn/v+i5v7/uOz+/8Lw/v+77vz/W6/D/wB/ + m/8Af5v/AKO9/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHC2P8a0Ob/Ot3x/zXH3v9I3/P/T+n8/0/p/P9P6fz/T+n8/zPZ + 7/8HkrD/AIOh/wCDof8Ag6H/AIOh/wCCoI8AqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhqQqAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCDoo4AgKYUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOhRACDoewAg6H/AIOh/wCDof8Ah6T/Hsrh/03p+/9P6fz/T+n8/0/p/P9P6fz/Suf7/xzP + 5v8Bs83/AMPa/wDD2f8Aw9n/AMPZ/wDB1/8AiqX/AH+b/wB/m/8znrj/u+v8/8Lv/v/D7/7/w+/+/6/q + /v+i5v7/oub+/6Hl/f+g5/3/vO/9/7bu/f+Q0+P/E4mi/wB/m/8AiKP/AMHY/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awtn/Es3j/zjg9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+v8YsMr/AIOh/wCDof8Ag6H/AIOh/wCD + orcAiJkPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KWAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofMAg6GAAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoFYAg6H5AIOh/wCDof8Ag6H/ApGt/y/b + 8P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLh9/8G1e7/AM3l/wDE2/8AxNv/AMTb/wDE2/8AxNv/AKS+/wB/ + m/8Af5v/EYql/6/m+f+/7v7/we7+/8Du/v+p6P7/oub+/6Lm/v+g5f3/l+T9/43k/P+s7P3/sO79/6bq + +f9Bobf/AH+b/wB/m/8Arsb/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/xXP5f9B5Pf/T+n8/0/p + /P9P6fz/T+n8/0/p/P8uz+b/A4ak/wCDof8Ag6H/AIOh/wCCoNUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG + nhUAg6HxAIOh/wCDof8Fiqf/CY+s/wCDof8Ag6H/AIOh/wCDof8Ag6HrAIOicwCOqgkAAAAAAAAAAAAA + AAAAgqBmAIOh/ACDof8Ag6H/AIOh/wSfu/864vf/T+n8/0/p/P9P6fz/T+n8/0jn+/8Z2/L/ANLq/wDI + 4P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDC2v8AhKD/AH+b/wB/m/93x97/ve3+/77u/v+/7v7/qej+/6Lm + /v+i5v7/n+X9/5bk/f+M4/z/g+P8/4/n/P+q7f3/pO38/3rJ2v8Fgp3/AH+b/wCQq/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPZ/wHF2/8g1er/Sej7/0/p/P9P6fz/T+n8/0/p/P9C4vX/D5m0/wCD + of8Ag6H/AIOh/wCDoewAg59AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoXcAg6H/AIOh/wKFpP9C2e3/LL3U/weL + qf8Ag6H/AIOh/wCDof8Ag6H/AIOh5ACCoGYAgL8EAIShcgCDof4Ag6H/AIOh/wCDof8IqMP/QOX5/0/p + /P9P6fz/T+n8/0/p/P895Pn/CNfv/wDQ6P8AyN//AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AK3F/wB/ + m/8Af5v/FIyo/7Xq/f+87f7/ve3+/7Lq/v+i5v7/oub+/57l/f+V5P3/i+P8/4Li/P954vv/ceH7/6Ds + /P+e7Pz/keT0/ymUrP8Af5v/AH+b/wC30P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8Fyd//M97y/0/p/P9P6fz/T+n8/0/p/P9N6Pv/ILHK/wCDof8Ag6H/AIOh/wCDofoAgqJYAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICfCACDoeAAg6H/AIOh/xumwP9P6fz/S+T4/yi3z/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCD + oOoAhKH+AIOh/wCDof8Ag6H/Ca3J/0Tm+v9P6fz/T+n8/0/p/P9P6fz/L+H2/wLV7f8Azeb/AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Al7H/AH+b/wB/m/9UtM3/uez+/7rt/v+87f7/pef+/6Lm + /v+d5f3/lOT9/4rj/P+B4vz/eOH7/27g+/9k4Pr/guf7/5jr+/+T6/v/YLzP/wB/m/8Af5v/AJmz/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/HNPp/0vn+v9P6fz/T+n8/0/p + /P9P6fz/KsLZ/wCDof8Ag6H/AIOh/wCDofwAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiWACDof8Ag6H/AIOh/z3S + 6P9P6fz/T+n8/0nh9f8kssv/A4ak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmyzf9E5vr/T+n8/0/p + /P9P6fz/T+n8/yTe9P8A1O3/AM3l/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCF + oP8Af5v/AH+b/4nS6f+47P7/uez+/7br/v+i5v7/nOX9/5Pk/f+J4/z/gOL8/3fh+/9t4Pv/ZN/6/1rf + +f9d4Pn/kev7/4zq+/9+2+3/FYqj/wB/m/8AgZz/AMDY/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/Ccrh/z3h9f9P6fz/T+n8/0/p/P9P6fz/M8/l/wKFpP8Ag6H/AIOh/wCD + ofwAg6BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAA//8BAIOhyACDof8Ag6H/EZm0/0/p/P9P6fz/T+n8/0/p/P9H3vL/IKzG/wGF + ov8Ag6H/AIOh/wCDof8Ag6H/EJq2/zzQ5v9P6fz/T+n8/03p/P8c2/L/ANTt/wDN5v8AyuL/AMri/wDK + 4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AxNz/AH+b/wB/m/8CgZz/qef8/7fs/v+47P7/r+n+/5vl + /f+S5P3/ieP8/3/i/P924fv/bOD6/2Pf+v9Z3vn/UN75/0bd+P955/r/her6/4Do+v9Dqb//AH+b/wB/ + m/8Aor3/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMfe/wDH3v8Ax97/A8jf/zPa + 7v9P6fz/T+n8/0/p/P9P6fz/PNft/wWKp/8Ag6H/AIOh/wCDof4AgqBmAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKM6AIOh/gCD + of8Ag6H/NMfd/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/xynwv8BhKL/AIOh/wCDof8Ag6H/AIOh/xih + vP9K5fj/GNrx/wDU7f8Azeb/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wC+ + 1/8Af5v/AH+b/xOLp/+u6f7/tuv+/7fs/v+l6P3/keT9/4jj/P9+4vv/deH7/2vg+v9i3/r/WN75/0/d + +f9F3fj/PNz4/1Hg+P9/6fr/fun6/3HS4/8Ggp3/AH+b/wCGof8Ax+D/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/Acjg/yrV6f9O5/r/T+n8/0/p/P9P6fz/QNvw/wWJ + p/8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GrAIOh/wCDof8KkK3/Teb5/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0HX7P8Yobz/AIOh/wCDof8Ag6H/AIOh/w+91v8A1O3/AM7m/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AL7X/wB/m/8Af5v/HZKt/63p/v+16/7/r+v9/5rm + /P+H4/z/feL7/3Ph+/9q4Pr/Yd/6/1je+f9O3fn/RNz4/zvc+P8y2/f/K9v3/3jo+v9+6fr/fuf4/y2a + sf8Af5v/AH+b/wCvyP8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/yDT6f9N5Pj/T+n8/0/p/P9P6fz/Ptrv/wWJp/8Ag6H/AIOh/wCDof4AhKBbAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA + niIAg6H4AIOh/wCDof8qutL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/FJ23/wCD + of8Bh6T/AMfh/wDP5/8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AxN3/AH+b/wB/m/8akKv/q+j+/6vq/f+m6v3/kub8/3zi+/9y4fv/aeD6/2Df+v9X3vn/Td35/0Pc + +P862/j/Mdr3/yfa9/8n2vf/WOL5/37p+v9+6fr/Ycfa/wB/m/8Af5v/AI6p/wDM5P8AzOT/AMzk/wDM + 5P8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xvR5/9M4/b/T+n8/1vr + /P9h6/z/R9ru/wKFo/8Ag6H/AIOh/wCDofkAg6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYsAg6H/AIOh/wSJpv9I4fT/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HM7m/wDM5v8A0en/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDK5P8Af5v/AH+b/weEn/+d5fz/oun9/53o + /f+N5vz/ceH7/2jg+v9f3/r/Vd75/0zd+f9C3Pj/Odv4/zDa9/8n2vf/J9r3/yfa9/813Pj/fun6/37p + +v985PX/GIym/wB/m/8Af5v/ALrT/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMvj/wDL4/8EzOP/Qdjq/47p9P/J9/z/0fn+/9H5/v/R+f7/t+72/2m2yP8Vjan/AIOh/wCD + ovAAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICfEACDoesAg6H/AIOh/yGux/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/y/h + 9v8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wCHo/8Af5v/AH+b/3vU7f+Z6P3/k+f8/43n/P9r4fr/Xt/6/1Te+f9L3fn/Qdz4/zjb + 9/8v2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9p5fn/fun6/37p+v9KuM7/AH+b/wB/m/8AmLL/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8EzuX/VN3u/6vv9v+98vj/vfL4/8b0 + +v/R9/z/0fn+/9H5/v/R+f7/xu70/7nd5f9escT/AoSi/wCDoeoAgKMkAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWjGQCDopYAg6GYAISgdACB + o0UAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCDof8Ag6H/AYWi/0LZ + 7f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5vr/AdXt/wDT7P8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AJiy/wB/m/8Af5v/QrHL/43m + /P+J5vz/g+b7/33l+/9l4fr/S935/0Dc+P832/f/Ltr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Xf + +P9+6fr/fun6/3bd7/8Ggp3/AH+b/wCAnP8AxNz/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Bzeb/Rtvt/6fu9v+98vn/oOz1/3fd6/9Sytz/RcDT/0u80P9jx9f/fNXk/6vs9f/Q+f7/wunv/73f + 5/+Qydb/D4qn/wCEodsAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKG+AIOh/wCDof8Ag6H/AIOh/wCDofsAg6G1AIGjRQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAmZkFAIOh4gCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj8/wfW + 7v8A1O3/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR + 6f8A0en/ANHp/wDR6f8Ascv/AH+b/wB/m/8Af5v/euD4/37l+/955fv/c+T6/3Dk+f9l4/n/Rt74/y3a + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/Kdr3/3fo+v9+6fr/fun6/zOmvf8Af5v/AH+b/wCi + vP8A0Oj/ANDo/wDQ6P8A0Oj/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/L9jr/5nr9f+i7PX/W9Dg/xunwP8Ah6P/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/xeQrP9zz97/td/o/73f5/+e0Nv/E4yo/wCCoLIA//8BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoeYAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOhowCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6HeAIOh/wCD + of8Ag6H/Os7k/0/p/P9P6fz/T+n8/0/p/P8c3PL/ANTt/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDN5v8Agp3/AH+b/wB/ + m/8MiqX/Rb/Y/2vi+f9q4/n/ZeP5/2Hj+f9d4/n/TeH4/zDc9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/VuL5/37p+v9+6fr/Z9Ll/wB/m/8Af5v/AIOf/wDN5f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/G9Xq/4Ho + 9P+J5O//NbbM/wGHo/8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8Xkaz/fsDQ/73f5/+f0Nz/BYWj/wCDoHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhzACJ + nQ0AAAAAAAAAAAAAAAAAAAAAAIShXwCDof8Ag6H/AIOh/wCDof8OlbH/Tuj7/0/p/P9P6fz/O+T4/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wCeuf8Af5v/AH+b/wB/m/8Af5v/EZKu/0TL5P9a4vn/VuL5/1bi + +f9b4/n/XeP5/0ng+P8r2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8z3Pf/fun6/37p+v9+6Pn/HZWu/wB/ + m/8Af5v/AK7I/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDR6f8A0en/FNXr/2rk8v972uj/Jqa9/wCBnv8AgZ7/AIyp/wCatf8Apb//AKrF/wCr + xv8AqsT/AKO+/wCYtP8AhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8BgZ7/XK7C/7ze5v9XrcH/AIOh/ACB + oUEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCD + of8Ag6H/ALbR/wCbuP8Ag6L/AIOh/wCDof8Ag6H/AIOhmAAAAAAAAAAAAAAAAACAnwgAg6HiAIOh/wCD + of8Ag6H/AIOh/wCDof8xwdn/T+n8/0zo/P8D1u3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wC0 + zv8AiKP/AH+b/wB/m/8Af5v/AH+b/xafu/9B1O3/VOL5/1ji+f9c4/n/X+T5/17j+f9B3vj/KNr3/yfa + 9/8n2vf/J9r3/yfa9/9m5fn/fun6/37p+v9Sxdv/AH+b/wB/m/8Ai6b/ANLr/wDT7P8A0+z/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Fdbt/2ff7v9uz9//Hpix/wCJ + pv8Aor3/ALnS/wDM5f8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8Aw9v/EqrE/waI + pf8AgZ7/AIGe/wCBnv8AgZ7/QKG3/7DZ4v8Qi6f/AIOg6ACAnRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoZAAg6H/AIOh/wCEov8A1O3/ANTt/wDF3v8Aiqf/AIOh/wCD + of8Ag6H7AIOiIQAAAAAAAAAAAIOgcQCDof8Ag6H/AIOh/wOTsP8Ag6H/AIOh/wiNqv9L5Pj/Htzy/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDQ6f8AqML/AIKe/wB/m/8Af5v/AH+b/wGB + nf8jrsj/T9z0/1nj+f9c4/n/YOT5/2Tk+f9c4/n/ON34/yfa9/8n2vf/J9r3/0Lf+P996fr/fun6/3rl + 9/8Kh6P/AH+b/wB/m/8AudT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/JNnv/1nU5v9SvM//D5ex/wCow/8Awdv/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8C0+v/O+D0/y691P8Giab/AIGe/wCBnv8AgZ7/Tqi8/3G6 + y/8Ag6H/AIOhwwCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKigwCD + of8Ag6H/AIOh/wDS6/8A1O3/ANTt/wC30v8Ag6H/AIOh/wCDof8AgqGNAAAAAACAvwQAg6HhAIOh/wCD + of8Aiaf/KdTq/wOHpf8Ag6H/AIOh/yCzzP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8C1O3/AtTt/wLU7f8Dy+T/AqC6/wCAnP8Af5v/AH+b/wB/m/8Fh6P/MLzV/1ji9/9e4/n/YeT5/2Xl + +f9o5fn/VuL5/y/b9/8n2vf/Ot34/3ro+v9+6fr/fun6/zy2zv8Af5v/AH+b/wCUr/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//PtTo/0zE1v84tcv/Aq/J/wDJ4v8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT + 7P8I1u7/SOb5/0rh9f8hq8T/AYKf/wCBnv8AgZ7/Wq7B/yGTrf8Ag6H/AIOihgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KGAIOh/wCDof8Ag6H/ANLq/wDU7f8A1O3/ANPs/wCP + rP8Ag6H/AIOh/wCDodEAAAAAAIKgVgCDof8Ag6H/AIOh/wqyzf9N6fz/J7bP/wCDof8Ag6H/AIek/wDS + 6v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8I1e7/B8Xe/wKV + sP8Af5v/AH+b/wB/m/8Af5v/DZGs/0DK4v9f5Pn/YuT5/2bl+f9q5fn/beb6/3Hn+v905/r/d+j6/3zp + +v9+6fr/Z9zw/wB/m/8Af5v/AH+b/wDD3f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BMri/wa9 + 1v8Av9r/ANDp/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8T2fD/Ten8/0/p/P85y+L/Boil/wCB + nv8AgZ7/TKe8/wCDof8Ag6H9AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oZAAg6H/AIOh/wCDov8A1O3/ANTt/wDU7f8A1O3/AKfD/wCDof8Ag6H/AIOh/QCOqgkAg6HHAIOh/wCD + of8AhaP/Md70/0/p/P9M5fj/CY+r/wCDof8Aiqf/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW + 7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8N1u7/Dtbu/wq60/8Ci6f/AH+b/wB/m/8Af5v/AH+b/xqf + uv9Q1u3/ZOT5/2fl+f9r5vr/bub6/3Ln+v915/r/eOj6/1je9P8XqMT/AH+b/wB/m/8Af5v/AZ65/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8h3fP/T+n8/0/p/P9F3fD/EJex/wCBnv8OiaP/JpWv/wCDof8Ag6HmAIaeFQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhqACDof8Ag6H/AIyq/wDU7f8A1O3/ANTt/wDU + 7f8AqcX/AIOh/wCDof8Ag6H/AIOiQgCDof8Ag6H/AIOh/weow/9N6Pz/T+n8/0/p/P9C3PD/A6/L/wDH + 4f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wPV + 7f8E1e3/BtXt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8P1+7/ENfu/xDX7v8Q1+7/Edfu/xLX + 7v8S1+7/Etfu/xPV7P8Kr8n/AYSg/wB/m/8Af5v/AH+b/wKAnf8pr8j/Xt/0/2jl+f9s5vr/cOb6/2Lk + +P8oudP/AoWg/wB/m/8Af5v/AH+b/wCAnf8DpsD/BNXt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8x4fb/T+n8/0/p + /P9M5fj/FJ23/wCBnv8Yjqn/A4Si/wCDof8AgqK0AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6HQAIOh/wCDof8AmbX/ANTt/wDU7f8A1O3/ANTt/wCfu/8Ag6H/AIOh/wCDoe0Ag6GQAIOh/wCD + of8Ag6H/JtXt/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wTV7f8G1e3/B9Xt/wnW7v8K1u7/DNbu/w3W7v8O1u7/ENfu/xDX + 7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xXY7v8W2O//Ftjv/xfY7/8X2O//F9jv/xfY7/8V0ej/CqS+/wCA + nf8Af5v/AH+b/wB/m/8Gh6H/O77W/2Lj+P85yOH/B46q/wB/m/8Af5v/AH+b/wB/m/8Dl7L/Ccjh/wnW + 7v8I1e7/B9Xt/wXV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8/5fn/T+n8/0/p/P9M5vn/EZ23/wCCoP8EhaL/AIOh/wCD + of8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCCoPwAgqD/AIOh/wCpxf8A1O3/ANTt/wDU + 7f8A1O3/AJCt/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGRrv9F5/r/T+n8/0/p/P9M6Pv/Bdbu/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/A9Xt/wTV7f8H1e3/CNXu/wrW + 7v8M1u7/Ddbu/w7W7v8Q1+7/Edfu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xjY7/8Z2O//Gtjv/xrY + 7/8b2e//G9nv/xvZ7/8c2e//HNnv/xzZ7/8b2e//F8ri/wiYsv8Af5v/AH+b/wB/m/8Af5v/AYCb/wB/ + m/8Af5v/AH+b/wB/m/8DjKj/Db7X/xDX7v8P1+7/Dtbu/wzW7v8L1u7/Cdbu/wfV7f8F1e3/BNXt/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX + 7/9L6Pv/T+n8/0/p/P9N5/r/Cpm1/wCCof8Ag6H/AIOh/wCDoewAhp4VAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKBZAIKg/wCBnv8AgJ7/ALvU/wDU7f8A1O3/ANTt/wDU7f8AhqT/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/F77X/0/p/P9P6fz/T+n8/zHh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8B1O3/A9Xt/wXV7f8H1e3/Cdbu/wvW7v8M1u7/Dtbu/xDX7v8R1+7/E9fu/xTX7v8V2O7/F9jv/xjY + 7/8a2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z7/8f2e//H9nv/x/Z7/8f2e//INnv/yDZ7/8g2e//INnv/yDZ + 7/8g2e//H9nv/xbA2P8Fjqj/AH+b/wB/m/8Af5v/AH+b/wB/m/8ChaD/DbDJ/xfW7f8W2O//Fdju/xPX + 7v8S1+7/ENfu/w7W7v8N1u7/C9bu/wnW7v8H1e3/BdXt/wTV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x7d8/9P6fz/T+n8/0/p/P9I5Pf/Ao+q/wCD + of8Ag6H/AIOh/wCEoZsAAAAAAAAAAAAAAAAAAAAAAAAAAACCorQAg6H/AIKg/wCDof8AyuP/AMzk/wDO + 5v8A0uv/ANTt/wCcuP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof813vT/T+n8/0/p/P9P6fz/Edjw/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xDX + 7v8T1+7/FNfu/xXY7v8X2O//Gdjv/xrY7/8b2e//HNnv/x7Z7/8f2e//INnv/yHa7/8h2u//Itrv/yPa + 7/8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yLZ7f8UtMz/A4ai/wB/ + m/8BgJ3/DaS+/xzS6P8c2e//HNnv/xrY7/8Z2O//GNjv/xbY7/8U1+7/E9fu/xDX7v8P1+7/Ddbu/wvW + 7v8J1u7/B9Xt/wXV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p/P8r1uv/AIil/wCDof8Ag6H/AIOh/gCEnzgAAAAAAAAAAAAA + AAAAhKUfAIOh+wCDof8Ag6H/AJy5/wDU7f8A0uv/AM/n/wDL4/8Ay+P/AMfg/wCcuf8AhKL/AIOh/wCD + of8Ag6H/BZy4/03p/P9P6fz/T+n8/0Hl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV + 7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xHX7v8T1+7/Fdju/xbY7/8Y2O//Gtjv/xvZ7/8d2e//H9nv/yDZ + 7/8h2u//Itrv/yTa8P8k2vD/Jdrw/yba8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb + 8P8p2/D/Kdvw/ynb8P8o2/D/J9vw/yba8P8k1Ov/E6/H/x7L4v8k2vD/Itrv/yHa7/8f2e//H9nv/x3Z + 7/8b2e//Gtjv/xjY7/8W2O//Fdju/xPX7v8R1+7/D9fu/w3W7v8L1u7/Cdbu/wfV7f8E1e3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/C9fv/03p+/9P6fz/T+n8/03p + /P8IuNP/AIOh/wCDof8Ag6H/AIShuAAAAAAAAAAAAAAAAACDoZIAg6H/AIOh/wCDof8Au9X/ANTt/wDU + 7f8A1O3/ANTt/wDT7P8Azeb/AMvj/wDJ4P8Av9n/AK3J/wCsx/8a1+7/T+n8/0/p/P9P6fz/Jd70/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wPV7f8F1e3/CNXu/wrW7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY + 7/8Z2O//Gtjv/xzZ7/8e2e//H9nv/yHa7/8j2u//JNrw/yXa8P8m2vD/KNvw/ynb8P8q2/D/K9vw/yvb + 8P8s2/D/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb + 8P8p2/D/Kdvw/yjb8P8m2vD/Jdrw/yTa8P8i2u//Idrv/x/Z7/8e2e//HNnv/xrY7/8Y2O//F9jv/xXY + 7v8T1+7/ENfu/w7W7v8M1u7/Ctbu/wfV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Kd/0/0/p/P9P6fz/T+n8/yvg9f8Amrj/AIOh/wCDof8Ag6H+AIakKgAA + AAAAgKYUAIOh8wCDof8Ag6H/AI6q/wDS6v8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDP + 5/8A0Oj/ANLr/zLh9v9L4/f/Os7k/z3S6P8LyOD/AMni/wDO5v8A0er/ANTt/wLU7f8E1e3/B9Xt/wnW + 7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY7/8Z2O//G9nv/xzZ7/8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb + 8P8p2/D/Ktvw/yvb8P8s2/D/Ldzw/y7c8P8v3PD/MNzw/zDc8P8x3PD/Mtzx/zLc8f8y3PH/Mtzx/zLc + 8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/L9zw/y3c8P8t3PD/LNvw/yvb8P8p2/D/KNvw/yba + 8P8l2vD/JNrw/yHa7/8g2e//Htnv/xzZ7/8a2O//GNjv/xbY7/8U1+7/Etfu/xDX7v8N1u7/C9bu/wnW + 7v8G1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8G1e7/S+j7/0/p + /P9P6fz/R+f6/wHG3/8Ag6H/AIOh/wCDof8Ag6CEAAAAAACEoHwAg6H/AIOh/wCDof8Ascv/ANLq/wDQ + 6P8Azub/ANDo/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/RuX3/wqPrP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIim/wGMqf8Bj63/A5Sx/wSZtf8Fm7j/B5+6/wmlwf8LqcT/DazG/w+x + yv8Sts//FbrS/xe91f8aw9v/Hsbe/yLO5P8q2/D/K9vw/y3c8P8u3PD/MNzw/zDc8P8y3PH/Mtzx/zPd + 8f813fH/Nd3x/zXd8f823fH/Nt3x/zfd8f833fH/N93x/zfd8f833fH/Nt3x/zbd8f813fH/Nd3x/zTd + 8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//INnv/x7Z + 7/8c2e//Gtjv/xjY7/8V2O7/E9fu/xDX7v8O1u7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8s4PX/T+n8/0/p/P9P6fz/FNrw/wCZtf8Ag6H/AIOh/wCD + odYAgJ8QAIOh7ACDof8Ag6H/AIqn/wDS6/8A1O3/ANPs/wDS6v8Az+f/AM3l/wDM5P8Az+f/ANPs/wDU + 7f8A1O3/ANTt/xDZ8P9F3PD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6f + uf8w3PD/Mdzw/zLc8f803fH/Nd3x/zbd8f833fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/O97x/zve + 8f883vH/O97x/zve8f873vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zDc + 8P8v3PD/Ldzw/yvb8P8p2/D/KNvw/yba8P8k2vD/Idrv/x/Z7/8d2e//G9nv/xnY7/8W2O//FNfu/xHX + 7v8P1+7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y + 7/9P6fz/T+n8/0/p/P8t4PX/ALLO/wCDof8Ag6H/AIOh/wCDoIwAg6H/AIOh/wCDof8Arcj/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDQ6P8Azeb/AM7m/wDR6f8A1O3/It30/0/p/P8hr8j/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zHY7f813fH/N93x/zjd8f853vH/Ot7x/zze + 8f883vH/Pd7x/z7e8f8+3vH/P9/x/z/f8f9A3/L/QN/y/0Df8v9A3/L/P9/x/z/f8f8+3vH/Pt7x/z7e + 8f883vH/PN7x/zve8f853vH/ON3x/zfd8f823fH/NN3x/zLc8f8x3PD/L9zw/y3c8P8r2/D/Kdvw/yfb + 8P8l2vD/I9rv/yHa7/8f2e//HNnv/xrY7/8X2O//Fdju/xLX7v8P1+7/Ddbu/wrW7v8H1e3/BNXt/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0rn+/9P6fz/T+n8/z/k+f8Awdv/AIOh/wCD + of8Ag6H/AIOh+QCDof8Ag6H/AIuo/wDP6f8A0+z/ANLr/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDT7P8y4fb/T+n8/0/p/P8yw9r/AIak/wCDof8Ag6H/AIOh/xGZtP8/1On/QNbr/z3S + 6P86zuT/Ncfe/zHC2v8uvtb/KrnR/yazzf8jr8n/H6vF/xqlv/8Xobv/FJy4/xCYs/8Lkq//CI6r/wCD + of8arsf/ON3x/zne8f873vH/PN7x/z7e8f8+3vH/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9D3/L/RN/y/0Tf + 8v9E3/L/RN/y/0Tf8v9E3/L/Q9/y/0Pf8v9D3/L/Qt/y/0Hf8v9A3/L/P9/x/z7e8f883vH/O97x/zne + 8f843fH/N93x/zXd8f8z3fH/Mdzw/y/c8P8t3PD/K9vw/ynb8P8m2vD/JNrw/yHa7/8f2e//HNnv/xrY + 7/8Y2O//Fdju/xPX7v8Q1+7/Ddbu/wrW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/O+T4/0/p/P9P6fz/Ten8/wLL5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AtdD/ANTt/wDT + 7P8A0ur/ANHp/wDP5/8Az+f/AM/n/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/0Hm+v9P6fz/T+n8/0vo + +/8BxuD/AI+s/wCDof8Ag6H/AIOh/wqQrf8/1Or/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9O6fz/Ot/y/zre8f883vH/Pt7x/z/f8f9B3/L/Qt/y/0Pf + 8v9E3/L/ReDy/0bg8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9J4PL/SeDy/0jg8v9I4PL/SODy/0fg + 8v9G4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f863vH/Od7x/zfd8f813fH/Mtzx/zDc + 8P8v3PD/ON3x/0Df8v9F4PL/SODy/0bg8v9C3/H/Od7x/y3b8P8f2fD/Fdju/xPX7v8Q1+7/Ddbu/wrW + 7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p/P9P6fz/B8/o/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AJWy/wDT7P8A1O3/ANTt/wDT7P8A0uv/ANLq/wDR6f8Az+f/AM3m/wDM + 5P8AzOT/AM3m/wDR6f8C1O3/Tuj8/0/p/P9P6fz/POT5/wDU7f8Az+n/AJ26/wCDof8Ag6H/AIOh/wKF + pP8vwNf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vn + +v883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9G4PL/SODy/0ng8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h + 8v9N4fL/TeHy/03h8v9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9G4PL/ReDy/0Pf + 8v9C3/L/QN/y/z7e8f883vH/Ot7x/zrd8f9M4fP/X+T0/23m9f9u5vT/bOb0/2rm9P9o5vT/Z+X0/2Xl + 9P9i5PT/YeT0/1/k9P9T4vP/O97x/yDZ7/8Q1+7/Ddbu/wnW7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P8Mzeb/AIOh/wCDof8Ag6H/AIOh/wCHpf8AyeL/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLq/wvU6/9P6Pv/T+n8/0/p + /P8x4fb/ANTt/wDU7f8A1O3/AbDL/wCEo/8Ag6H/AIOh/wCDof8dqML/TOX4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+T3/0Hf8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg + 8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi8/9R4vP/UuLz/1Li8/9S4vP/UuLz/1Li8/9S4vP/UeLz/1Dh + 8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9J4PL/SODy/0Xg8v9E3/L/Qt/y/0Lf8v9b4/P/cuf1/3To + 9f9z5/X/cef1/3Dn9f9u5vT/bOb0/2rm9P9p5vT/Z+X0/2Xl9P9j5fT/YeT0/1/k9P9d5PT/W+Tz/0Xg + 8v8f2u//DNbu/wnW7v8G1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MOH2/0/p/P9P6fz/T+n8/wvG + 3/8Ag6H/AIOh/wCDof8Ag6H/ALfS/wDT7P8A0ur/AM/n/wDN5v8AzOT/AMvj/wDK4v8AyuL/AMnh/wDJ + 4f8AyeH/AMnh/wDK4v8Ay+P/E9Tp/0/o+/9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8C1O3/EMPc/wOM + qf8Ag6H/AIOh/wCDof8OlLD/Q9nu/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9G4vX/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Dh8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj + 8/9W4/P/VuPz/1bj8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h + 8v9L4fL/SuDy/0jg8v9X4/P/def1/3rp9v946fX/d+j1/3Xo9f9z6PX/aeL0/1/X9P9Y0PT/Vsv0/1TI + 9P9SzfT/VNLz/1jb8/9f5PP/YOT0/17k9P9d5PT/W+Tz/1nj8/873vH/Etfu/wjV7v8F1e3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/BrXQ/wCDof8Ag6H/AIOh/wCpxf8A1O3/ANPs/wDR + 6f8Azub/AM3l/wDL4/8AyuL/AMri/wDK4v8AyuL/AMzk/wDN5f8Azub/ANHp/wDT7P8c2/L/T+n8/0/p + /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wLU7f8Z2O//GtHo/wiZtf8Ag6H/AIOh/wCDof8Eiab/Ncfe/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8/9I4PL/SuDy/03h8v9O4fP/UOHz/1Li + 8/9T4vP/VOLz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj + 8/9Z4/P/WePz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UeLz/0/h8/9Q4fL/bOf0/4Dp9v9/6fb/fOn2/3vp + 9v9x5PX/Xsv0/1Gz8v9SovP/UqLz/1Kh8/9SofP/UqHz/1Kh8/9SofP/UqHz/06n8v9MvfP/UdTz/1zj + 9P9c5PT/WuPz/1jj8/9O4fP/Htnv/wfV7f8E1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k+f9P6fz/T+n8/0zo + +/8Anbj/AIOh/wCDof8Ag6HpANTt/wDU7f8A0+z/ANPs/wDT7P8A1O3/ANTt/wDU7f8AutT/AIOh/wCq + xf8AtM//AL/a/wDK5P8A0+z/ANTt/xzb8v9P6fz/T+n8/0/p/P8d3PL/ANTt/wDU7f8A1O3/A9Xt/xvZ + 7/8f2e//Idnu/xGsx/8Ag6H/AIOh/wCDof8Ag6H/I7DK/03n+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9N5/v/SuDy/03h8v9P4fP/UOHz/1Li8/9U4vP/VuPz/1fj8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k + 9P9f5PT/X+T0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj + 8/9V4vP/WOPz/3np9v+B6vb/gOr2/3/q9v925fX/Wsby/06j8f9Pn/H/T5/w/0+f8P9Pn/D/T5/w/0+f + 8P9PnvD/T57w/0+e8P9PnvD/Tp7w/06e8P9OnvD/Rq/w/0rU8v9Y4/T/V+Lz/1Ti8/9R4vP/Jtvw/wbV + 7f8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Suf7/0/p/P9P6fz/P+T5/wCHpP8Ag6H/AIOh/wCCoa4A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ALPN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFo/8Am7f/G9Tr/0/p + /P9P6fz/T+n8/yHd8/8A1O3/ANTt/wDU7f8C1O3/Htnv/yHa7/8k2vD/J9vw/x7C2P8Diab/AIOh/wCD + of8Ag6H/Epu2/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/07l9/9O4fP/UOHz/1Li8/9U4vP/VuPz/1nj + 8/9a4/P/W+Pz/17k9P9e5PT/YOT0/2Hk9P9i5fT/YuX0/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/ZOX0/2Pl + 9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9c5PP/W+Pz/13k8/986fb/gur2/4Hq9v9/6vb/atj0/02l + 7v9MnO7/TJzu/0yc7v9MnO7/Tp3t/2il4/97rd3/hrDZ/4uy1v9/rdr/c6ne/1ig6f9Lm+3/S5vt/0ub + 7f9Lm+3/S5vt/0O57v9P4PL/UeLz/0/i8/9N4fP/Jdrw/wXV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wzY8P9P6fz/T+n8/0/p + /P8y2O7/AIOh/wCDof8Ag6H/AIKhZADS6v8A0ur/ANHp/wDR6f8A0en/ANHp/wC40v8Ag6L/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Xssz/T+n8/0/p/P9P6fz/It70/wDU7f8A1O3/ANTt/wDU + 7f8h2u//JNrw/yfb8P8q2/D/Ldzw/yrR5v8KlbH/AIOh/wCDof8Ag6H/B4up/zvP5f9P6fz/T+n8/0/p + /P9P6fz/T+T2/1Li8/9U4vP/VuPz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9k5fT/ZeX0/2bl + 9P9n5fT/Z+X0/2jm9P9o5vT/aeb0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuX0/2Dk + 9P9f5PT/fen2/4Pq9v+B6vb/f+r2/2PK8v9Kmuz/SZns/0mZ7P9Jmez/YaLk/5W10f+2wsf/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vMTE/6i8zP9+rNn/TJrq/0iY6/9HmOr/R5jq/0Op7P9J2vH/SuHy/0ng + 8v9G4PL/Gtju/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/H9zz/0/p/P9P6fz/T+n8/yK+1v8Ag6H/AIOh/wCDofwAhaMZAMzk/wDM + 5P8AzeX/AM3m/wDQ6P8Avtj/AIWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xWl + wP9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANTt/yDZ7/8m2vD/Ktvw/y3c8P8w3PD/M93x/zTa + 7v8XqMH/AIOh/wCDof8Ag6H/AYSi/ym4z/9P6fz/T+n8/0/p/P9T4vT/VeLz/1jj8/9a4/P/XOTz/17k + 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9s5vX/beb1/23m9f9t5vX/beb1/2zm + 9f9s5vX/bOb1/2vm9f9q5vT/aOb0/2fl9P9l5fT/ZOX0/3bo9f+E6vb/gur2/4Dq9v9iwvD/Rpbp/0aW + 6f9Glun/UJnm/5a00P+8xMT/vcTE/77Fxf+/xcX/v8bG/8DHx//Bx8f/wcjI/8LIyP/Dycn/w8rK/8TK + yv+4xs3/cafd/0SV6P9Elej/RJXo/0Sf6v9F2PH/ReDy/0Lf8v893/L/C9bt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p + /P9P6fz/Dpez/wCDof8Ag6H/AIShuAAAAAAA1O3/ANTt/wDU7f8A1O3/AL7Y/wCIpf8Ag6H/AIOh/wCD + of8Ag6KkAImdDQCFoS4AhKFRAIOh/wCDof8Ag6H/Ep65/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU + 7f8A1O3/Htnv/ynb8P8s2/D/MNzw/zLc8f823fH/Od7x/zze8f8ovdP/A4ek/wCDof8Ag6H/AIOh/xeh + vP9J4vX/UOj7/1bj8/9Z4/P/W+Pz/17k9P9g5PT/YuX0/2Xl9P9m5fT/aOb0/2rm9P9s5vX/beb1/27n + 9f9v5/X/cOf1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/b+f1/27n9f9s5vX/a+b1/2rm + 9P9v5/X/her2/4Pq9v+B6vb/acvt/0OU5/9Dk+b/Q5Pm/1uc4P+xwcr/wcjI/8LIyP/Cycn/w8rK/8PK + yv/CyMj/wsjI/8HIyP/Dycn/xcvL/8jOzv/Jzs7/yc/P/8rQ0P/K0ND/krbW/0KS5P9BkuX/QZLl/0Gh + 6P9A3PH/Pt7x/zve8f8n2/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AtXt/0rn+/9P6fz/T+n8/0nl+P8BhKL/AIOh/wCDof8AhKBbAAAAAADU + 7f8A1O3/ANTt/wC0z/8AhKL/AIOh/wCDof8Ag6H/AIOhvQCAqgYAAAAAAAAAAAAAAAAAgqGNAIOh/wCD + of8Kj6z/T+n8/0/p/P8+1On/Dp25/wCTsP8AxN//ANTt/wDU7f8a2O//K9vw/y7c8P8y3PH/Nd3x/zjd + 8f883vH/Pt7x/0Lf8v85z+P/CpGt/wCDof8Ag6H/AIOh/wqQrf9B0ub/WePz/1zk8/9f5PT/YeT0/2Tl + 9P9m5fT/aOb0/2rm9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f926PX/duj1/3bo + 9f926PX/dej1/3To9f9z5/X/cuf1/3Hn9f9v5/X/buf1/4Dp9v+F6/b/gur2/3XZ8/8xbar/QJHk/0CR + 5P9Vm+H/u8fO/8bMzP/Hzc3/x83N/8XLy//Ax8f/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/v8XF/8XL + y//O09P/z9TU/9DV1f/Q1dX/nb3Z/z6P4v8+j+L/Po/i/zyw6P863vH/ON3x/zLd8P8M1u7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p + /P9P6fz/M8je/wCDof8Ag6H/AIOh6wCOqgkAAAAAANTt/wDT7P8AqMT/AIOh/wCDof8Ag6H/AIOh/wCE + obwAgJkKAAAAAAAAAAAAAAAAAAAAAACDoX0Ag6H/AIOh/wGEov9K5Pj/I6/J/wGEov8Ag6H/AIOh/wCi + vv8A1O3/ANTt/xXY7v8t3PD/Mdzw/zTd8f833fH/O97x/z7e8f9B3/L/ReDy/0jg8v9G2+7/GaG8/wCD + of8Ag6H/AIOh/wKFpP9N1Ob/YOT0/2Ll9P9l5fT/Z+X0/2rm9P9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo + 9f936PX/eOj1/3no9v966fb/e+n2/3vp9v976fb/e+n2/3rp9v966fb/eOj1/3jo9f926PX/dej1/3Pn + 9f936PX/huv3/4Tq9/984vj/SKnr/yVViP86iNj/SZTf/7nI0f/K0ND/y9HR/8vR0f/Dysr/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/7/Gxv/P1NT/1dra/9ba2v/W29v/irTd/zqM + 4P86jOD/Oo3g/zXQ7v8z3PH/K9zw/x7Z7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y3g9f9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8AgqKDAAAAAAAA + AAAAx+H/AJe0/wCDof8Ag6H/AIOh/wCDof8AhKGdAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCD + of8Ag6H/AIOh/wmQrP8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/Dtbu/zDc8P8z3fH/N93x/zne + 8f893vH/Qd/y/0Pf8v9H4PL/SuDy/03h8v9R4vP/L7jO/wGFov8Ag6H/AIOh/yyvx/9i5fT/ZeX0/2jm + 9P9r5vX/beb1/3Dn9f9y5/X/dOj1/3bo9f946PX/eun2/3vp9v996fb/fen2/37p9v9/6fb/f+n2/3/p + 9v9/6fb/f+n2/37p9v996fb/fOn2/3vp9v946PX/d+j1/4Hq9v+G6vb/hOr2/2HF+P89rfn/JFiM/y9y + uP+du9b/z9TU/9DV1f/Q1dX/w8rK/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+5ta7/tKGO/7KZ + g/+0o5H/urm0/77Fxf/U2dn/29/f/9zg4P/a3+D/Vpnc/zeJ3f83id3/NaLi/y/c8P8n2+//I9rv/wXV + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/SOf6/0/p + /P9P6fz/Tef6/wWJp/8Ag6H/AIOh+QCFoxkAAAAAAAAAAACEov8Ag6H/AIOh/wCDof8Ag6H+AIOgeQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/DZOw/zvP + 5v8W2vH/ANTt/wDU7f8E1e3/Mtzx/zXd8f853vH/PN7x/z/f8f9D3/L/RuDy/0ng8v9N4fL/UOHz/1Pi + 8/9W4/P/QMjb/wCDof8MkKz/TtHj/2bl9P9p5vT/bOb1/2/n9f9x5/X/c+f1/3bo9f946PX/eun2/3zp + 9v9+6fb/f+n2/4Hq9v+C6vb/g+r2/4Tq9v+E6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v+A6vb/fun2/33p + 9v986fb/h+v2/4br9v+D6vb/R7L5/z6v/P8rdbH/PGiT/9PY2f/U2dn/1dra/8nPz/+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+6ubP/q4Bd/6RlNP+kZTT/pGU0/6RlNP+kZjb/ropr/77Bvv/d4OD/4eTk/+Ll + 5f+8z+H/NIbb/zSG2/8zhtr/KdPu/yHa7/8d2e//Dtbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXa8f9P6fz/T+n8/0/p/P83yuD/AIOh/wCDof8Ag6KhAAAAAAAA + AAAAAAAAAIOh/wCDof8Ag6H/AIOg6ACDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF + oHMAg6H/AIOh/wCDof8Ag6H/AoWj/ye1zv9N5/n/T+n8/0Tm+f8C1O3/ANTt/wDU7f8t3PD/N93x/zre + 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9S4vP/VuPz/1nj8/9c5PP/YOT0/2Ll9P9l5fT/aeb0/2zm + 9f9v5/X/cef1/3To9f936PX/eej2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+H6/f/iOv3/4jr + 9/+I6/f/iOv3/4fr9/+H6/f/her2/4Tq9v+C6vb/gOr2/4Lq9v+I6/f/huv3/3jd9/8/r/z/Pq/8/zea + 4v9jc4H/w8bG/9re3v/W2tr/vsTE/73ExP+9xMT/vsXF/8DGxv/ByMj/u7Sp/6dsP/+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/p3JH/8bBuv/m6en/5+np/+fq6v9Sldn/MIPY/zCD2P8ptuX/Gtnv/xfY + 7/8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p + /P9P6fz/T+n8/xulwP8Ag6H/AIOh/wCCnzUAAAAAAAAAAAAAAAAAg6H/AIOh/wCDoasAgKYUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEnh0AgqK0AIOh/wCDof8Ag6H/AIOh/xGZtf9B1+z/T+n8/0/p + /P9P6fz/T+n8/ybe9f8A1O3/ANTt/yDZ7/853vH/PN7x/0Df8v9D3/L/R+Dy/0rg8v9O4fP/UeLz/1Ti + 8/9Y4/P/W+Pz/17k9P9i5fT/ZeX0/2jm9P9s5vX/b+f1/3Hn9f916PX/eOj1/3vp9v996fb/f+n2/4Lq + 9v+E6vb/huv2/4jr9/+J6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+M7Pf/i+v3/4vr9/+J6/f/iOv3/4br + 9v+E6vb/h+v2/4jr9/+G6/f/XcT2/z6v/P8+r/z/Pq/8/3OLnf+go6P/3+Li/8rQ0P+9xMT/vsXF/8HH + x//Dycn/xcvL/8XIxf+obkL/pGU0/6RlNP+jZTT/fU0o/2Y/IP+FUir/pGU0/6RlNP+kZTT/qHhQ/+Tn + 5v/s7u7/7e/v/52+3v8tgNX/LYDV/yqX3P8T1+7/Etfu/xLX7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7v9L6Pv/T+n8/0/p/P9L5fj/A4el/wCDof8Ag6H/AAAAAAAA + AAAAAAAAAAAAAACDotIAg59IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6JrAIOh8gCD + of8Ag6H/AIOh/wSIpf8tvdT/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wrW7/8A1O3/Edfu/zre + 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Tl9P9n5fT/a+b1/27n + 9f9x5/X/dOj1/3jo9f976fb/fen2/4Dq9v+D6vb/her2/4jr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Hs + 9/+R7Pf/kez3/5Hs9/+Q7Pf/j+z3/47s9/+L6/f/iev3/4fr9/+J6/b/iOv2/4Xq9v9MtPT/Pq/8/z6v + /P8+r/z/WaHS/4mKiv/Y29v/wMbG/7/Gxv/Dycn/xszM/8nOzv/L0ND/vaaR/6RlNP+kZTT/nmEy/ykZ + Df8AAAD/AAAA/wAAAP8+JhT/o2U0/6RlNP+kZTT/zL+y//Lz8//y9PT/0t/q/yp90/8qfdP/KoDT/wzW + 7v8M1u7/DNbu/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gtvy/0/p + /P9P6fz/T+n8/zLF3P8Ag6H/AIOh/wCDoaMAAAAAAAAAAAAAAAAAAAAAAP//AQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6IpAIOhxQCDof8Ag6H/AIOh/wCDof8WoLv/Rdzw/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/NuP3/wDU7f8D1e3/Ot7x/z/f8f9D3/L/R+Dy/0rg8v9O4fP/UuLz/1Xi + 8/9Z4/P/XOTz/2Dk9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3Pn9f936PX/e+n2/33p9v+A6vb/g+r2/4fr + 9/+J6/f/i+v3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/le34/5bt+P+W7fj/le34/5Xt+P+T7ff/kez3/5Ds + 9/+N7Pf/i+v3/4vr9/+I6/b/hev2/0Wv9v8+r/z/Pq/8/z6v/P9Brvn/g5CZ/6qtrf/Axsb/xMrK/8fN + zf/L0ND/ztPT/9DV1f+xhGD/pGU0/6RlNP9SMxr/AAAA/wAAAP8AAAD/AAAA/wAAAP9qQSL/pGU0/6Rl + NP+5lnn/9/j4//j5+f/r8PT/J3rQ/yd60P8netD/Cs/s/wfV7f8H1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/FqC7/wCDof8Ag6H/AIOgcQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAISifgCDofkAg6H/AIOh/wCD + of8Gi6j/MsTb/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/F9vx/wDU + 7f8r2/D/Qd/y/0Tf8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n + 9f9z5/X/duj1/3no9v996fb/gOr2/4Pq9v+H6/f/iev3/4zs9/+P7Pf/ku33/5Tt9/+W7fj/mO74/5nu + +P+a7vj/mu74/5ru+P+a7vj/mO74/5ft+P+V7fj/k+33/5Ds9/+O7Pf/i+v3/4jr9/+F6vb/P6z3/z6v + /P8+r/z/P6/8/0Wy/P9lptL/aGlp/4KFhf/Izs7/zNHR/8/U1P/T19f/1tra/6pzSP+kZTT/pGU0/y0c + Dv8AAAD/AgIC/woKCv8RERH/FRUV/1E4Jf+kZjX/pGU0/7CCXv/8/Pz//f39//z9/f8jd83/I3fN/yN3 + zf8Hxej/AdTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Bdbu/03p + /P9P6fz/T+n8/0jl+P8BhaL/AIOh/wCDof8AhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAISfOACDodMAg6H/AIOh/wCDof8Ag6H/HKfB/0jh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vr/AtTu/xTX7v9C3/L/ReDy/0ng8v9N4fL/UeLz/1Ti + 8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr + 9/+M7Pf/j+z3/5Lt9/+V7fj/mO74/5ru+P+c7vj/ne74/5/v+P+f7/j/n+/4/57v+P+c7vj/m+74/5nu + +P+W7fj/k+33/5Ds9/+O7Pf/iuv3/4fr9/9Arff/Pq/8/z6v/P9Fsfz/SrT8/1C2+/8oLjL/HB0d/8rQ + 0P/Q1dX/1NjY/9fb2//b3t7/rnxU/6RlNP+kZTT/OyQT/xsbG/9GRkb/TExM/1NTU/9ZWVn/lIBx/7mJ + ZP+kZTT/toxr////////////+fv8/yB0y/8gdMv/IHTL/wTJ6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8g3PP/T+n8/0/p/P9P6fz/Lsvi/wCDof8Ag6H/AIOh/ACJ + nQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqDACCopEAg6H9AIOh/wCDof8Ag6H/CY+s/zjM + 4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8n3vX/AdTt/z7e8f9H4PL/SuDy/07h8/9S4vP/VeLz/1nj8/9d5PT/YOT0/2Tl9P9n5fT/a+b1/2/n + 9f9z5/X/duj1/3rp9v996fb/ger2/4Tq9v+I6/f/i+v3/47s9/+S7ff/le34/5ju+P+b7vj/nu/4/6Dv + +P+h7/j/o+/5/6Tw+f+k8Pn/ou/4/6Hv+P+f7/j/nO74/5nu+P+W7fj/k+33/5Ds9/+M7Pf/iev3/0Ov + 9v8+r/z/Q7H8/0mz/P9Ptvz/QpDE/wgOEf8HBwf/e39//9PY2P/Y3Nz/3N/f/9/j4//BoIX/pGU0/6Rl + NP+KVSz/aGZl/4iIiP+Ojo7/lZWV/6Kfnf/Yw7P/28Ov/6RlNP/Ks6D////////////s8fX/HXHI/x1x + yP8dcsj/AdLs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k + +f9P6fz/T+n8/0/p/P8Qn7r/AIOh/wCDof8Ag6HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + olUAg6H/AIOh/wCDof8Ag6H/AIOh/w2TsP82yeD/Os7k/zrO5P86zuT/Oczi/zXH3v81x97/Ncfe/zXH + 3v81x97/NMbd/y/A1/8vwNf/L8DX/y/A1/8vwNf/L7/X/ym50f8FsMr/Gb3W/0jg8v9L4fL/T+Hz/1Pi + 8/9W4/P/WuPz/17k9P9i5fT/ZeX0/2nm9P9s5vX/cOf1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr + 9/+N7Pf/kOz3/5Tt9/+Y7vj/m+74/57v+P+h7/j/pPD5/6bw+f+n8Pn/qPD5/6jw+f+m8Pn/pPD5/6Hv + +P+f7/j/m+74/5ju+P+V7fj/kez3/47s9/+K6/f/SrT0/0Cv/P9Hs/z/TrX8/1O09v8RIy//OXCV/wIC + A/8WFxf/0dbW/9vf3//g4+P/5Ofn/9vTyv+lZjb/pGU0/6RlNP/AppL/19LO/9jV0v/h3dr/8Onj//Xu + 6f/m1Mb/pms9/+ro5P///////////8jY5/8absb/Gm7G/xd7yv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1/D/T+n8/0/p/P9P6fz/QuP3/wCDof8Ag6H/AIOh/wCD + onMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JlAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0zh8v9P4fP/VOLz/1fj8/9b4/P/XuT0/2Ll9P9m5fT/aub0/23m + 9f9x5/X/dej1/3jo9f986fb/gOr2/4Tq9v+H6/f/i+v3/47s9/+S7ff/lu34/5ru+P+d7vj/oe/4/6Tw + +f+n8Pn/qvH5/6zx+f+t8fn/rPH5/6rx+f+n8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+T7ff/j+z3/4vr + 9/9gyfj/RLH8/0u0/P9St/z/KFVz/zlxlv9aqd7/ChAU/xYXF/9eYGD/3uHh/+Ll5f/n6ur/7O7u/8io + jv+kZTT/pGU0/610SP/w5d3/////////////////9vDr/7SBWf/Drpv//v7+////////////daTT/xZr + w/8Wa8P/DpfU/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf + 9P9P6fz/T+n8/0/p/P8lyOD/AIOh/wCDof8Ag6H/AIafKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oasAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8YsMn/TeHy/1Dh + 8/9U4vP/WOPz/1vj8/9f5PT/Y+X0/2fl9P9q5vT/buf1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr + 9/+L6/f/kOz3/5Pt9/+X7fj/mu74/5/v+P+i7/j/pvD5/6nw+f+t8fn/sPL5/7Hy+f+w8vn/rPH5/6nw + +f+l8Pn/oe/4/57v+P+a7vj/l+34/5Pt9/+P7Pf/i+v3/3ne+P9Hsvz/T7b8/0yj3f9UquP/ZL/8/z5x + kv8nQ1b/KjpF/xITE/+Tlpb/5Ofn/+rs7P/v8PD/8/X1/9O3of+maTr/pGU0/6VnN/+/knD/zKiM/8GX + df+rckf/x7Wl//Hy8v////////////f5+v8bbMH/E2jA/xNowP8Ft+H/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/Ref6/0/p/P9P6fz/Tun8/wmcuf8Ag6H/AIOh/wCD + odwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISjOgCDof8Ag6H/AIOh/wCDoe4Ag6HhAISi3QCE + ot0AhKLdAISi3QCEot0Ag6HRAIOhzACDocwAg6HMAIOh7wCDof8Ag6H/AISi/w+cuP8Vnrn/FZ65/xWe + uf8Gnbn/AJ66/wCeuv8Anrr/AKnF/wzS6/9M4fL/UeLz/1Ti8/9Y4/P/W+Pz/2Dk9P9j5fT/Z+X0/2vm + 9f9v5/X/cuf1/3bo9f966fb/fen2/4Hq9v+E6vb/iev3/4zs9/+Q7Pf/lO33/5ju+P+b7vj/n+/4/6Pv + +f+m8Pn/qvH5/67x+f+y8vr/tPL6/7Dy+f+t8fn/qfD5/6bw+f+h7/j/nu/4/5ru+P+X7fj/k+33/4/s + 9/+L6/f/h+v3/0u0+P9Rt/z/Wbr8/2G9/P9nv/r/Ficz/12cx/8vTF//DQ8R/wsMDP++v7//6+3t//Dx + 8f/19vb/+/v7//Hp4//KqI3/t4di/7F9VP+2iGT/xKaO/9nW0v/o6ur/////////////////l7rb/xBl + vv8QZb7/EG/C/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xTZ + 8f9P6fz/T+n8/0/p/P863/T/AIOh/wCDof8Ag6H/AIKhhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJNAIOh/wCDof8Ag6H/GMXe/0/p/P9P6fz/T+n8/zfj9/8A1O3/ANTt/wDU7f8A1O3/ANTt/zHc + 8P9R4vP/VOLz/1jj8/9c5PP/YOT0/2Pl9P9n5fT/a+b1/2/n9f9y5/X/duj1/3rp9v996fb/ger2/4Tq + 9v+J6/f/jOz3/5Ds9/+T7ff/mO74/5vu+P+f7/j/ou/4/6bw+f+p8Pn/rPH5/6/y+f+w8vn/rvH5/6vx + +f+o8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+S7ff/juz3/4vr9/+H6/f/Zs34/1S4/P9cu/z/ZL/8/0By + lP81W3X/esj9/ypCUv8THCL/AAAA/xscHP/W2Nj/7/Dw//T19f/4+Pj/+fn5//b39//x8/P/7O7u/+fq + 6v/i5eX/6evr/////////////////+zx9f8SZrz/DWO7/wxiu/8Motj/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p/P9P6fz/T+n8/x291v8Ag6H/AIOh/wCD + of0AgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAvwQAg6HnAIOh/wCDof8BlrP/Q+X5/0/p + /P9P6fz/Tun8/wzY8P8A1O3/ANTt/wDU7f8A1O3/Ctbu/0/h8/9U4vP/WOPz/1vj8/9g5PT/Y+X0/2fl + 9P9q5vT/b+f1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr9/+L6/f/j+z3/5Pt9/+W7fj/mu74/53u + +P+h7/j/pPD5/6bw+f+p8Pn/q/H5/6vx+f+q8fn/qPD5/6Xw+f+i7/j/n+/4/5zu+P+Y7vj/le34/5Hs + 9/+O7Pf/iev3/4br9v9/5vf/Vrj5/168/P9XotT/PWqJ/3fG/f9+yv3/EBge/xsoMP8FBgj/BAQE/yco + KP/b3d3/8PHx//Lz8//z9PT/8fPz/+7w8P/q7Oz/5+rq//L09P/////////////////2+Pr/MXnC/wlf + uP8JX7j/DHDA/wbS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV + 7v9P6fz/T+n8/0/p/P9L6Pv/A4uo/wCDof8Ag6H/AIKhtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAGZmCgByjeEAg6H/AIOh/wCDof8f0Oj/T+n8/0/p/P9P6fz/NOL3/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Kdvw/1Ti8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9u5/X/cef1/3Xo9f946PX/fOn2/4Dq + 9v+E6vb/h+v3/4vr9/+O7Pf/kez3/5Xt+P+Y7vj/m+74/5/v+P+h7/j/pPD5/6Xw+f+m8Pn/pvD5/6bw + +f+k8Pn/ou/4/5/v+P+c7vj/me74/5bt+P+T7ff/j+z3/4vr9/+I6/f/hOr2/4Hq9v9t1fj/X738/2Gy + 6v9wxP3/ecf9/2ijy/8YJCz/IC44/xgiKf8oNj//AAAA/yoqKv/P0dH/7vDw/+7w8P/v8fH/8PLy//b2 + 9v/+/v7/////////////////9ff4/0uJxv8GXbb/Bl22/wZet/8VueH/AdTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Kt/0/0/p/P9P6fz/T+n8/zHT6f8Ag6H/AIOh/wCD + of8AhKFRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVgAYAWm+1AF91/wCDof8Ag6H/AIOh/wOa + t/9J6Pv/T+n8/0/p/P9P6fz/Etnw/wDU7f8A1O3/ANTt/wDU7f8D1e3/SODy/1fj8/9b4/P/XuT0/2Ll + 9P9l5fT/aub0/23m9f9x5/X/dOj1/3jo9f976fb/f+n2/4Lq9v+G6/b/iev3/4zs9/+Q7Pf/k+33/5bt + +P+Z7vj/m+74/57v+P+f7/j/oe/4/6Lv+P+i7/j/oe/4/6Dv+P+e7/j/nO74/5nu+P+W7fj/k+33/5Ds + 9/+N7Pf/iev3/4fr9/+D6vb/f+n2/3zp9v9mx/n/aMH8/3HE/f96yP3/Mk9i/2+myv8SGiD/L0FM/1V0 + iP8AAAD/AgMD/w8PD/+BgYH/+fn5/////////////////////////////////+Ho7/8ncLz/A1q0/wNa + s/8DWrP/EqXX/xDX7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW + 7/9M6Pv/T+n8/0/p/P9O6Pz/DZm1/wCDof8Ag6H/AIOh5ACAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVVUDAFtwqQBab/8AWm//AHWQ/wCDof8Ag6H/AIOh/yXT6v9P6fz/T+n8/0/p/P8+5fn/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Qv9j/O8Xa/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n9f9z5/X/duj1/3rp + 9v996fb/gOr2/4Tq9v+H6/f/iuv3/47s9/+Q7Pf/k+33/5Xt+P+Y7vj/mu74/5zu+P+c7vj/ne74/53u + +P+c7vj/nO74/5ru+P+Y7vj/lu34/5Pt9/+Q7Pf/juz3/4vr9/+H6/f/hOr2/4Hq9v996fb/eun2/3Xl + 9v9nxfn/ccT9/2CeyP9ek7j/hMby/wEBAf9kiqT/f6/O/wAAAP8lNkL/AAAA/wQEBP8LCwv/SkpK/5CQ + kP+6urr/3t7e/9LV1v9rjrH/A1iw/wBXsf8AV7H/AFmy/xGd0v8c2e//CNXu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ld/1/0/p/P9P6fz/T+n8/zLT6f8Ag6H/AIOh/wCD + of8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBZbp0AWm//AFpv/wBab/8XiqH/AYSi/wCD + of8Ag6H/Ape0/0bn+v9P6fz/T+n8/0/p/P8b2/L/ANTt/wDU7f8A1O3/AK/K/wCDof8Ag6H/N7/U/1zk + 8/9g5PT/Y+X0/2fl9P9q5vT/buf1/3Hn9f916PX/eOj1/3vp9v9/6fb/gur2/4Xq9v+I6/f/i+v3/47s + 9/+Q7Pf/k+33/5Tt9/+W7fj/mO74/5ju+P+Z7vj/me74/5ju+P+Y7vj/lu34/5Tt9/+S7ff/kOz3/43s + 9/+L6/f/h+v3/4Tq9v+C6vb/fun2/3vp9v946PX/dOj1/3Dk9v9rw/H/ZafU/4DK/f9spMn/FiEo/5HO + 9/+Rzfb/AAAA/1N9mf8AAAD/BQcI/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ADhz/ADl0/wNg + tv8Ytd7/H9nv/xrY7/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y + 8P9O6Pz/T+n8/0/p/P9O6Pz/C5az/wCDof8Ag6H/AIOg6ACLogsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnCQAFpv/wBab/8AWm//GIWZ/03m+f8cp8H/AIOh/wCDof8Ag6H/G8rj/0/p/P9P6fz/T+n8/0fm + +v8G1e7/ANTt/wDE3v8AhaP/AIOh/wCDof8mtc3/W+Pz/1/k9P9i5fT/ZeX0/2nm9P9s5vX/b+f1/3Pn + 9f926PX/eej2/3zp9v9/6fb/gur2/4Xq9v+I6/f/iuv3/4zs9/+O7Pf/kOz3/5Lt9/+T7ff/lO33/5Tt + 9/+U7ff/lO33/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iev3/4fr9/+E6vb/gur2/3/p9v986fb/eOj1/3bo + 9f9y5/X/b+f1/2zk9f9w0vr/fcn9/0Zth/9wp83/jtD9/47Q/f8DBAX/aJ7D/xIdJP8AAAD/BQkL/wUK + Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Cj1F/x7I2/8d2e//D9fu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/QOX5/0/p/P9P6fz/T+n8/y7N5P8Ag6H/AIOh/wCD + of8AhKJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/xR/k/9M5Pb/T+n8/0LY + 7f8BhKL/AIOh/wCDof8Ai6n/PuT4/0/p/P9P6fz/T+n8/zTi9/8A0uv/AJCt/wCDof8Ag6H/CpCt/zze + 9P9U4/T/XeT0/2Dk9P9k5fT/Z+X0/2rm9P9u5/X/cef1/3Pn9f936PX/eun2/33p9v9/6fb/gur2/4Tq + 9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/kOz3/5Ds9/+P7Pf/juz3/47s9/+M7Pf/iuv3/4jr + 9/+G6/b/hOr2/4Hq9v9/6fb/fOn2/3no9v926PX/c+f1/3Dn9f9s5vX/aub0/2bl9P9p2/b/YqLI/4PM + /f+Hzf3/fb/r/wAAAP9ys9//LEhb/wAAAP8lQ1b/CREX/y1bev8AAQH/AAAA/wQLEP8AAQH/AAAA/wAA + AP8AAAD/BB4h/xi6zP8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yXe + 9P9P6fz/T+n8/0/p/P9H5fn/BYqn/wCDof8Ag6H/AIOh5ACOqgkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb + b3MAWm//AFpv/wBab/8Pdor/OcHV/yqluf8XhZj/BWN3/wBzjf8Ag6H/AIOh/wCDof8Qssz/Tuj8/0/p + /P9P6fz/T+n8/wypxP8Ag6H/AIOh/wGEov880Ob/T+n8/0rn+v9X5fX/XuT0/2Ll9P9l5fT/aOb0/2vm + 9f9v5/X/cef1/3To9f936PX/eun2/33p9v9/6fb/ger2/4Pq9v+F6vb/h+v3/4nr9/+J6/f/iuv3/4vr + 9/+L6/f/i+v3/4vr9/+K6/f/iev3/4jr9/+G6/b/hOr2/4Lq9v+A6vb/fen2/3vp9v946PX/duj1/3Pn + 9f9w5/X/beb1/2rm9P9n5fT/Y+X0/2Dk9P9d4/T/aNn3/3jO+/9WiKv/ERwj/3rI/f84XXf/BAgK/1CN + t/8VFRX/OXSb/ytojv8LCwv/ChMV/yCXpf8OTFP/AAEB/wAAAP8AAAD/Ahga/wC90/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8S2fD/Ten8/0/p/P9P6fz/T+n8/xqwyf8Ag6H/AIOh/wCD + of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29lAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AF1z/wB/nP8Ag6H/AIOh/wCDof8o1ev/T+n8/0/p/P86zeT/AIOh/wCDof8Ag6H/JbPM/0/p + /P9P6fz/T+n8/0/o/P9X5vj/YOT0/2Ll9P9m5fT/aeb0/2zm9f9v5/X/cef1/3To9f926PX/eej2/3vp + 9v996fb/f+n2/4Lq9v+D6vb/hOr2/4Xq9v+G6/b/h+v3/4fr9/+H6/f/h+v3/4br9v+E6vb/hOr2/4Lq + 9v+A6vb/fun2/3zp9v966fb/eOj1/3Xo9f9z5/X/cOf1/23m9f9q5vT/Z+X0/2Tl9P9h5PT/XuT0/1vj + 8/9X4/P/VOLz/xg+RP84hZf/XdT3/ypjdP8TMDj/S8/u/zI3OP8yYWf/Nd3x/y19hv8kJCT/Ij1B/yjQ + 5P8hr8D/Fzo+/xEREf8ODg7/CC0y/wDM5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0bn + +v9P6fz/T+n8/0/p/P832O3/AIOh/wCDof8Ag6H/AIShvgD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvVQBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AGqC2ACDof8Ag6H/AIOh/wKR + rv8/5Pn/SeL1/wiOq/8Ag6H/AIOh/w+Wsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9U5/n/X+T1/2Pl + 9P9m5fT/aeb0/2zm9f9v5/X/cef1/3Pn9f926PX/eOj1/3rp9v986fb/fen2/3/p9v+A6vb/ger2/4Lq + 9v+C6vb/gur2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/fen2/3zp9v976fb/eOj1/3bo9f906PX/cef1/2/n + 9f9s5vX/aub0/2fl9P9k5fT/YeT0/17k9P9b4/P/WOPz/1Xi8/8+rLn/IV5l/0vh8v9I4PL/GVNZ/yV/ + iv8+3vH/SnJ3/0ldX/803fH/Mdvv/z1hZf89PT3/MXuE/yLa7/8f1On/IXiD/ysrK/8nJyf/FWhy/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f844/j/T+n8/0/p/P9P6fz/RuX5/waNq/8Ag6H/AIOh/wCD + ofcAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/bAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv9QBa + b70AWm5/AFlwQgBmZgoAAAAAAIOgrwCDof8Ag6H/AIOh/wyqxf8apcD/AIOh/wCDof8ChqT/Qtjt/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9S6Pr/XeX1/2Pl9P9m5fT/aeb0/2vm9f9u5/X/cOf1/3Ln + 9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3vp + 9v956Pb/eOj1/3bo9f906PX/cuf1/3Dn9f9u5/X/bOb1/2nm9P9n5fT/ZOX0/2Hk9P9e5PT/W+Pz/1jj + 8/9V4vP/UuLz/zmir/9M4fL/SODy/0Xg8v8QNzz/O9Dh/zze8f9VnKX/ZW9w/zLc8f8u3PD/Nr/P/1pa + Wv9WWFn/KMTX/xvZ7/8D1e3/HJ6t/0NHSP9AQUH/EKy//wDU7f8A1O3/ANTt/wDU7f8A1O3/LuD2/0/p + /P9P6fz/T+n8/07o/P8TpL//AIOh/wCDof8Ag6H/AISgfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b7wAWm//AFpv/wBab/8AWm+1AFlveABYbjoAZmYFAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh7ACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y291P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9Q6fz/Wub3/2Pl9P9l5fT/aOb0/2rm9P9s5vX/b+f1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo + 9f946PX/eej2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3Xo9f9z5/X/cuf1/3Hn9f9v5/X/bOb1/2rm + 9P9o5vT/ZeX0/2Pl9P9g5PT/XuT0/1vj8/9Y4/P/VeLz/1Li8/9P4fP/TeHy/0ng8v9G4PL/O8TV/yN6 + hf883vH/Od7x/1a8yP96kJL/MNzw/yzb8P8p2/D/ZIyR/3Nzc/9Mnab/CNXu/wDU7f8A1O3/Iqq6/11e + Xv9LbXH/AdPs/wDU7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/JMDZ/wCDof8Ag6H/AIOh/wCD + ocgAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBbcFQAXHAyAFVVAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh/ACDof8Ag6H/AIOh/wCDof8Vnrn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Vuf4/2Hl9P9l5fT/Z+X0/2nm + 9P9r5vX/bOb1/27n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/dej1/3To9f906PX/c+f1/3Pn + 9f9y5/X/cef1/2/n9f9u5/X/bOb1/2rm9P9o5vT/Z+X0/2Tl9P9i5fT/YOT0/13k9P9b4/P/WOPz/1Xi + 8/9S4vP/T+Hz/03h8v9K4PL/RuDy/0Pf8v8unqz/PNvu/zre8f833fH/SM/f/4GvtP8t3PD/Ktvw/yba + 8P9TvMj/j4+P/3eYnP8A1O3/ANTt/wDU7f8A1O3/Oam2/3Z2dv8nssL/ANTt/wDU7f8h3fT/T+n8/0/p + /P9P6fz/T+n8/y7S6P8Ag6H/AIOh/wCDof8Ag6HsAICiHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg599AIOh/wCDof8Ag6H/BYqn/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/TuP3/zm+1P8mqMH/QcLW/2fl9P9p5vT/aub0/2zm9f9s5vX/buf1/2/n + 9f9v5/X/cOf1/3Dn9f9w5/X/cOf1/3Dn9f9v5/X/b+f1/27n9f9s5vX/a+b1/2rm9P9o5vT/ZuX0/2Xl + 9P9i5fT/YOT0/17k9P9c5PP/WePz/1fj8/9U4vP/UuLz/0/h8/9M4fL/SeDy/0bg8v9D3/L/QNzu/zvT + 5f863vH/N93x/zTd8f821ej/W6Or/yvb8P8n2/D/JNrw/zfL3P+RkZH/jZmb/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/WKy2/3ebn/8B0+z/JN70/0/p/P9P6fz/T+n8/0/p/P832/D/AYSj/wCDof8Ag6H/AIOh/gCF + oUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEonYAg6H/AIOh/wCDof80x93/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tq/yCsxv8EiKb/AIOh/wCD + of8BhKL/W93u/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2vm9f9r5vX/bOb1/2zm9f9s5vX/a+b1/2rm + 9P9q5vT/aeb0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/17k9P9d5PT/W+Pz/1jj8/9W4/P/VOLz/1Hi + 8/9O4fP/TOHy/0ng8v9G4PL/Q9/y/0Hf8v8+3vH/O97x/zfd8f813fH/Mtzx/y7c8P89qrf/KNvw/yXa + 8P8h2u//F9br/21xcv9tenz/ANTt/wDU7f8A1O3/ANTt/wDU7f8G0en/fpuf/0jJ2f9P6fz/T+n8/0/p + /P9P6fz/PN/0/wOKqP8Ag6H/AIOh/wCDof8Ag6GCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhaEuAIOh+ACDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9L5Pf/L8DX/xCYs/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weKp/9c4vH/YOT0/2Ll9P9j5fT/ZOX0/2Xl + 9P9i4fD/Wtnq/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Ll9P9h5PT/YOT0/17k + 9P9c5PP/W+Pz/1nj8/9W4/P/VOLz/1Li8/9Q4fP/TeHy/0vh8v9I4PL/ReDy/0Pf8v9B3/L/Pt7x/zve + 8f833fH/Nd3x/zLc8f8v3PD/K9vw/yvL3v8m2vD/Itrv/xPX7v8B1O3/Rlxe/0JrcP8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wLU7f9Axtb/aJuh/0/p/P9P6fz/T+n8/zrg9P8Di6n/AIOh/wCDof8Ag6H/AIOipAD/ + /wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDodMAg6H/AIOh/wmPrP9K4vb/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1On/H6vF/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ + p/8MqMP/RNPm/1vj8/9c5PP/XuT0/1/k9P9g5PT/W9/x/wyPq/8Ag6H/NLfN/2Ll9P9i5fT/YuX0/2Ll + 9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k9P9b4/P/WuPz/1nj8/9W4/P/VOLz/1Pi8/9R4vP/T+Hz/0zh + 8v9K4PL/SODy/0Xg8v9C3/L/QN/y/z3e8f863vH/N93x/zXd8f8y3PH/L9zw/yzb8P8p2/D/Jtrw/yHa + 7/8L1u7/ANTt/wDU7f8jTVL/HHN+/wDU7f8A1O3/ANTt/wDU7f8G1e7/POT5/0/p/P9RipH/T9/w/0/p + /P843vT/Ao6r/wCDof8Ag6H/AIOh/wCDobkAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGNAIOh/wCDof8BhKL/O8/l/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9v8vv9f/D5ey/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCXs/8At9H/ANLr/wDU7f8F1e3/Mdzw/1jj8/9Z4/P/W+Pz/1vj + 8/9Dydz/AIOh/wCDof8SlbH/XuT0/17k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1fj + 8/9W4/P/VOLz/1Li8/9R4vP/T+Hz/03h8v9K4PL/SODy/0bg8v9D3/L/Qd/y/z/f8f883vH/Od7x/zfd + 8f813fH/Mtzx/y/c8P8s2/D/Kdvw/yXa8P8X2O//BdXt/wDU7f8A1O3/ANTt/wg6QP8Emqv/ANTt/wDU + 7f8A1O3/Etnw/0Xn+v9P6fz/T+n8/0rM2/9Fo6//MNnv/wKKqP8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGhQQCDof4Ag6H/AIOh/ySxyv9P6fz/T+n8/0/p + /P9P6fz/T+n8/z7U6f8fqsT/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqo/wWpxP8Ax+H/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Dtbu/zve8f9W4/P/V+Pz/z7H2/8Ag6H/AIOh/w6Vsf9U5/f/WOT0/1nj + 8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9Q4fP/TuHz/03h8v9K4PL/SeDy/0fg + 8v9F4PL/Qt/y/0Df8v8+3vH/O97x/zne8f823fH/NN3x/zHc8P8u3PD/K9vw/yjb8P8V2O7/A9Xt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AEZO/wDN5f8A1O3/AtTt/yjf9P8yw9v/IK3G/0LZ7f9P6fz/Tuj8/x2L + m/8AhaP/AIOh/wCDof8Ag6H/AIOiygCGoRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH + pREAg6HiAIOh/wCDof8OlbH/Tef5/0/p/P9P6fz/SuP2/y6+1v8PlrH/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/E5y3/zPF2/9N5/n/TOj7/yTe9P8B1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W + 7v823fH/O8fb/wCDof8Ag6H/DpWx/0/p/P9P6Pv/UuX4/1Pj9P9U4vP/VOLz/1Pi8/9S4vP/UeLz/0/h + 8/9P4fP/TeHy/0zh8v9K4PL/SODy/0fg8v9F4PL/Q9/y/0Hf8v8+3vH/PN7x/zre8f833fH/Nd3x/zLc + 8f8w3PD/Ldzw/yvb8P8b2e//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Afoz/ANTt/xPZ + 8P9C5vr/SeL1/wCDof8Ag6H/AIOh/0jn+v8VwNn/AGZ+/wCBnv8Ag6H/AIOh/wCEobwAgKoMAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhpQCDof8Ag6H/AoWk/0HX7P9P6fz/PtPo/x6q + xP8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xe50v9M6Pv/T+n8/0/p/P9P6fz/T+n8/z7l + +f8N1/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Gvtf/AIOh/wCDof8OlbH/T+n8/0/p + /P9P6fz/T+n7/0/l+P9P4fT/TuHz/03h8v9N4fL/S+Hy/0rg8v9J4PL/SODy/0bg8v9E3/L/Q9/y/0Hf + 8v8+3vH/Pd7x/zve8f853vH/Nt3x/zTd8f8y3PH/L9zw/y3c8P8h2u//Cdbu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/weyx/8y4fb/T+n8/0/p/P9D2u7/AIOh/wCDof8BhaL/B6fC/wCD + of8AgqD/AHWP/wCDof8Ag6GrAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + olgAg6H/AIOh/wCDof8ntc7/Lr3V/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4AgqLrAIOh/wCD + of8Ag6H/AIOh/w6nwf9C5fj/T+n8/0/p/P9P6fz/T+n8/07o/P8v4fb/CNfv/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wC91v8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o+/9M4/f/SeDy/0jg + 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Df8v8+3vH/PN7x/zve8f853vH/N93x/zXd8f8y3PH/MNzw/y7c + 8P8f2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v8q3/X/TOj7/0/p + /P9P6fz/T+n8/03n+v8BhaL/AIOh/wCDof8Ag6H/AIOh/wCDof8Af5z/AIKgjwCqqgMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wKFpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoekAgqGLAIahJgCqqgMAgqCJAIOh/gCDof8Ag6H/AIOh/wKNqv8q0un/Tuj8/0/p + /P9P6fz/T+n8/0/p/P9M6Pv/K+D1/wbV7v8A1O3/ANTt/wDU7f8A1O3/AL3W/wCDof8Ag6H/DpWx/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/SOP2/0Pf8v9B3/L/QN/y/z/f8f8+3vH/PN7x/zre + 8f853vH/N93x/zXd8f8y3PH/K9vw/x7Z7/8R1+7/BdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wfW7/8o3/T/Suf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCD + of8Ag6H/AIOh8wCCoFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD + oM0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDob0AhKFXAJKSBwAAAAAAAAAAAAAAAAAA + AAAAd5ZcAH+c/wCDof8Ag6H/AIOh/wCDof8QqsX/QeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+j7/y7g + 9f8O1+//ANTt/wDU7f8Avdb/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/R+f6/yfe8/8S0un/CanE/wqvyf8S1uz/Etfu/w7W7v8M1u7/BtXt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y7/8x4fb/Ten8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDodYAhp8oAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFsAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCC + oIkAgKMkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabn8AXnP/AHeS/wCDof8Ag6H/AIOh/wCD + of8ChqT/J8ff/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P895Pn/Hdzz/wK91/8Ag6H/AIOh/w6V + sf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKeuP8Ag6H/AIOh/wC5 + 0/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/B9bv/yPd + 9P8/5Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8jsMr/AIOh/wCD + of8Ag6HYAICqDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + ocMAg6H/AIOh/wCDof8Ag6H+AIOhuwCEolUAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgIACAFpw4ABab/8AW3D/GZiw/wOGpP8Ag6H/AIOh/wCDof8Ag6H/BpGu/yzP5f9M6Pv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Ncvg/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8uvtb/AIOh/wCDof8Ag6H/AL7Z/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8O2O//JN70/zvk+P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun8/03o + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDofwAg6H/AIOhmACDoCMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabkoAWm//AFpv/wJdcv9I3fD/RNrv/xeh + vP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmSr/8vz+X/TOj7/0/p/P9P6fz/T+n8/0/p/P84y+H/AIOh/wCD + of8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wOHpf8Ag6H/AIOh/wCd + uv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1u7/FNrw/yTe9P814vf/R+b6/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/z7T6f8ltM3/Qdrv/0Di9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD + of8Ag6H/AIOh4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAImdDQCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvrQBab/8AWm//Gomd/0/p/P9P6fz/T+n8/znM4v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCD + of8Fi6n/Ir/Z/0Lj9v9P6fz/T+n8/zjL4f8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07o+/8SmrX/AIOh/wCDof8EjKn/Jdju/yfe9f8s4PX/MOH2/zbi9/8+5fn/Sej7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/A4el/wCDof8Ag6H/Qdfs/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G3vH/AIOh/wCDof8Ag6H/AICqDAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVahgAWm/7AFpv/wBab/85wtX/T+n8/0/p + /P9P6fz/T+n8/03n+v8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xOivP8wz+X/Mcje/wCD + of8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KrnR/wCDof8Ag6H/AIOh/zfK + 4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9D5fn/KdXq/w2uyf8Ag6L/AIOh/wCDof8Jj6v/Rt3y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8Dh6X/AIOh/wCDof8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFlveABab/8AWm//CmyB/07o+/9P6fz/T+n8/0/p/P9P6fz/T+n8/znA1P8IaX7/AGqE/wB+ + mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0HX7P8ChaT/AIOh/wCDof8eqsT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/SOf6/zbf9P8gy+P/CqnF/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8OlbH/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAWm/dAFpv/wBab/8ppLj/T+n8/0/p + /P9P6fz/T+n8/0zj9v8gk6j/AFtw/wBab/8AWm//AFpv/wBrgtIAgp/MAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/r/D5ax/wCDof8Ag6H/Co+s/0vj + 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/PuP4/zPc8f8p0uj/GsHa/wqlwf8Aiaj/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbT/TOX4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/GqW//wCDof8Ag6H/AIOiewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtuQwBab/8AWm//AV1x/0fa7f9P6fz/T+n8/0/p/P89yNz/C26D/wBab/8AWm//AFpv/wBa + b/YAW3BwAAAAAQAAAAAAg58lAISifgCCodcAg6H/AIOh/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/ya0zf8Ag6H/AIOh/wCDof8Knbn/EKzF/xCuyP8Sr8j/EavG/wymwP8InLn/A5Sy/wCK + p/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HMAIShegCD + oYoAg6H/AIOh/wCDof8Yor3/Tuj7/0/p/P9P6fz/T+n8/0/p/P8ltM3/AIOh/wCDof8Ag6GiAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+oAFpv/wBab/8YhZr/T+n8/0/p + /P9N5fj/JZ2x/wFccf8AWm//AFpv/wBab/8AWm/LAFdwKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oTEAg6GrAIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+n/AYSi/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKg/wB6lv8AeZOgAICfIAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8eq8T/T+n8/0/p + /P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDocYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFlzFABab/kAWm//AFpv/ze/0v9P6fz/QM/i/w90if8AWm//AFpv/wBab/8AWm/7AFpwgABA + gAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/w6Vsf9P6fz/T+n8/0/p + /P9P6fz/TOb5/wySr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP8Xobz/I67H/wBke/8AXHH/AFpv/gBddBYAAAAAAAAAAAAA + AAAAAAAAAKqqAwCDoK8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/PdHn/wCDof8Ag6H/AIOh7QAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29zAFpv/wBab/8Jan//Teb5/yqm + uv8CXnP/AFpv/wBab/8AWm//AFpv1wBYcTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIWgSwCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCDof8Ag6H+AIKhxgCD + ocAAg6HMAISh2wCDoeUAg6HjAISh2wB+m+YAepX/AHiT/wZ+mP8ot8//M8Xc/z7T6P9I4PT/T+n8/0/p + /P8xssb/AFpv/wBab/8AWm/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqBgCDob0Ag6H/AIOh/wCD + of8uv9b/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAQBab9cAWm//AFpv/xyNof8Se4//AFpv/wBab/8AWm//AFpv/gBacJAAVXEJAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaBLAIOh/wCDof8OlbH/T+n8/0/p + /P9P6fz/O8/l/wCDof8Ag6H/AIOh/wCCooMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdtIwBa + b/4AWm//AFpv/0DN4f9P6fz/T+n8/0/p/P9P6fz/T+n8/x2Po/8AWm//AFpv/wBabpQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDotIAg6H/AIOh/wCDof8zxdv/T+n8/0/p/P8Giqj/AIOh/wCD + of8AgqE5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXHE9AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab+EAWXBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCoj8Ag6H/AIOh/wyTsP9P6fz/T+n8/0vj9/8Jj6z/AIOh/wCDof8Ag6HQAICqBgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvwQBab/8AWm//Ipis/0/p/P9P6fz/T+n8/0/p + /P9P6fz/CWqA/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCE + odsAg6H/AIOh/wKFo/87z+X/T+n8/xKatf8Ag6H/AIOh/wCEol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABbb6MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXCgAFV3DwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/0/p + /P9P6fz/HqrE/wCDof8Ag6H/AIOh+ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnBgAFpv/wBab/8GZHr/Teb5/0/p/P9P6fz/T+n8/0TW6f8AWm//AFpv/wBab/4AWXMUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICjJACDoeoAg6H/AIOh/wWJp/8/1On/HajC/wCD + of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWmkRAFpv/wBab/8AWm//AFpv/wBa + b/8AWm/pAFpwUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Jj6z/T+n8/zfK4P8Ag6H/AIOh/wCDof8Ag6F3AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVagwAWm/zAFpv/wBab/81us7/T+n8/0/p + /P9P6fz/MLLF/wBab/8AWm//AFpv1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKiNwCDofMAg6H/AIOh/weLqf8eqsP/AIOh/wCDof8Ag6GqAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABacGkAWm//AFpv/wBab/8AWm//AFlvsQBZbxcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wmP + rP9J4fX/B42q/wCDof8Ag6H/AIOhxQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABab58AWm//AFpv/xeFmP9P6fz/T+n8/0/p/P8djqP/AFpv/wBab/8AWm6UAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiQgCDofkAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDodEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/wBb + cGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/xqkv/8Ag6H/AIOh/wCDofQAgKMkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AV1x/0fb + 7v9P6fz/T+n8/wlqgP8AWm//AFpv/wBbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISgWwCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh9QD//wEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAVW0VAFpupABab6UAWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOiawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAFpv3ABab/8AWm//KqW5/0/p/P9E1un/AFpv/wBab/8AWm/+AFlzFAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCD + of8Ag6H/AIOh/wCDof8Ag6H/AISeHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA//8BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBa + b/8McIX/T+n8/zCyxf8AWm//AFpv/wBZb9MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACD + of8Ag6H/AIOh/wCDof8Ag6HvAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABebx4AWm/9AFpv/wBab/89yNz/HY6i/wBab/8AWm//AFpulAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA//8BAIOhowCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Ag6H/AIOh/wCCoF4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b7oAWm//AFpv/x+Sp/8JaoD/AFpv/wBab/8AW3BUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiygCDof8Ag6H/AIOgRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + ojwAg6H/AIOh/wCDof8Ag6GtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvWgBab/8AWm//AFpv/wBab/8AWm//AFpv/gBZ + cxQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgKoGAIOhZwCDo1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOjJwCDof8Ag6H/AIOh/wCAohYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVXEJAFpv8ABab/8AWm//AFpv/wBab/8AWm/SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIShcgCDoKcAhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+XAFpv/wBab/8AWm//AFpv/wBa + b5MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////4///4H///////////////+H + //+B///8f///////////g///gf//+H///////////4H//wH///B///////////+A//8B///gf/////// + ////wH/+AP//wH///////////8A//gD//4D////////////AH/AAA/8A////////////wA4AAAA+AP// + ///////8f8AAAAAAAAH//////////B/AAAAAAAAB//////////wP4AAAAAAAAf/////////+A+AAAAAA + AAH//////////gDAAAAAAAAB//////////8AAAAAAAAAAP//////////AAAAAAAAAAA//////////4AA + AAAAAAAAH//////5//+AAAAAAAAAAAf/////8H//wAAAAAAAAAAD//////Af/4AAAAAAAAAAAP/////4 + B/8AAAAAAAAAAAB/////+AH+AAAAAAAAAAAAP/////wA/AAAAAAAAAAAAB/////+ADgAAAAAAAAAAAAP + /////gAAAAAAAAAAAAAAB/////8AAAAAAAAAAAAAAAP/////AAAAAAAAAAAAAAAB/////4AAAAAAAAAA + AAAAAP////+AAAAAAAAAAAAAAAB/////wAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAB/////gAAAA + AAAAAAAAAAAP//n/8AAAAAAAAAAAAAAAB//wH/AAAAAAAAAAAAAAAAP/8AfwAAAAAAAAAAAAAAAD//AD + 8AAAAAAAAAAAAAAAAf/wAeAAAAAAAAAAAAAAAAD/8AHgAAAAAAAAAAAAAAAAf/AAwAAAAAAAAAAAAAAA + AD/wAMAAAAAAAAAAAAAAAAA/8ACAAAAAAAAAAAAAAAAAH/AAgAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAA + AAAAAAAP8AAAAAAAAAAAAAAAAAAAB/AAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAD4AAAAAAA + AAAAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAIAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAA + AAAAAAAAAAAAAQA4AAAAAAAAAAAAAAAAAAMAeAAAAAAAAAAAAAAAAAADAPwAAAAAAAAAAAAAAAAAAwH8 + AAAAAAAAAAAAAAAAAAcH/AAAAAAAAAAAAAAAAAAHD/wAAAAAAAAAAAAAAAAADx/wAAAAAAAAAAAAAAAA + AA9/4AAAAAAAAAAAAAAAAAAP/4AAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAB/8AAAAAAAAAAAA + AAAAAAAf8AAAAAAAAAAAAAAAAAAAH+AAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAA/gAAAAAAA + AAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///+AAAAAAAAAAAAAAAAAf/// + gAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAA + Af//+AAAAAAAAAAAAAAAAAP///AAAAAAAAAAAAAAAAAD///wAAAAAAAAAAAAAAAAB///4AAAAAAAAAAA + AAAAAAf//8AAAAAAAAAAAAAAAAAP//+APAAAAAAAAAAAAAAAH///g/4AAAAAAAAAAAAAAB//////AAAA + AAAAAAAAAAA//////4AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAH//////AAAAAAAAAAAAAAD///// + /gAAAAAAAAAAAAAB//////wAAAAAAAAAAAAAA//////8AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAP + //////AAAAAAAAAAAAAAH//////wAAAAAAAAAAAAAD//////4AGAAAAAAAAAAAD//////8AP4AAAAAAA + AAAB///////AP+AAAAAAAAAAA///////gf/AAAAAAAAAAAP//////8f/wAAAAAAAAAAD/////////4AA + AAAAAAAAA/////////+AAAAAAAAAAAP/////////gAAAAAAAAAAD/////////wAAAAAAAAAAA/////// + //8AB8AAAAAAgAH////////+AA/wAAAAA8AB/////////gAf+AAAAAfgAf////////4Af/gAAAAH8AH/ + ///////8AP/4AP8AB/gB/////////AP/+AH/AA/8Af////////gH//gD/4AP/gD////////4H//4B/+A + D/8A////////+D//+Af/gA//gP////////D///gP/8Af/8D////////5///4H//AH//g//////////// + +B//4B//4P////////////g//+Af//D////////////4f//gP//4////////////+H//8D////////// + //////j///A////////////////9///wP///////////////////+H///////ygAAAAwAAAAYAAAAAEA + IAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKHfAIOh/wCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAACXXSnAFpv/wBcb44AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAgp81AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6CMAIOh/wOHpMQAgJ8IAAAAAAAA + AAAAAAAAAAAAAABVcRIBXHH6G4uf/wRhdssAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAoajoACD + of8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYBoqo+QuT + sPcDh6S3AKqqAwAAAAAAgIACAIakKgBrhZYId475I6zF/wFshvsAgp9aAIOiQgCAphQAAAAAAAAAAACJ + nQ0Dh6S/AoWj/gCDof8AgJ0aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJcb3UAW2+BAAAAAQAA + AAAAgp8tAYSh/Ubd8f8Jj6z4AoWj0QCGpLcAhaL0AIOh/wCDof8AhKL/Aouo/wCHpf8AhKL+AIOh/wCD + of8AhaTgAIOhqQKGpOIPl7P4KrnR/wOIptwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb + cMgAWm//A15zyQBbbSoAjo4JAYSh/UHX7P9F3PD/Boyo/w2fu/4p0Ob/M9jw/yfA1/8cqsP/Qub5/z/k + +f875Pj/Md/0/yXU6v8Qssv/Aoik/xiivf9M5vn/GaS+/ACFoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABecRsAWm//DnOH/QFkefwAgJzmA4ek/zTG3f9M6Pr/RN7x/0Xc8P8kts7/BoSf/zSe + tf8Yi6T/FrzT/yHS6P8q1ur/Ndzv/0bk9/8Mk6//I7DK/03o+/9O6Pv/BIim/wCEou0AhKFXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKgMwCD + oZAAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAADXnaiC4We/AGEov8UqcP/FaK9/ye1zv9B4/n/HtHo/wiW + sf8smrP/qd/w/8jw/v98xNb/BJSv/wDA1/8AwNf/AMDX/wDA1/8Kxt3/ItPo/0Di9/9B2O3/A4el/xu6 + 0/4AgqH/AISirACAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOiUgCDof8Ag6H/AISgbgCAqgYAAAAAAAAAAACImQ8Ag6DHAoem/yzS5/9P6fz/LMjf/w+8 + 1f8AxNv/BZiy/z6mvv/A7f7/tev+/6no/v+u5vX/Doai/wC50P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Syt//LtHm/0/p/P8+4PX/DZq3+wCFo+UAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOHpcMBhKP/CI6r9wKGo+gAgqJgAIiiHgCFpNcFkq76PeH2/03p + /P8i2/L/AMjg/wDF3P8AtMz/CIah/7Dm+f+v6v7/oub+/4nj/P+b6vz/SbHG/wCdt/8AxNv/AMTb/wDE + 2/8Aw9r/AMPa/wDD2v8Aw9r/AMPa/xPN5P9B4/b/TOj7/yC40v0AhaL2AISiTQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCny0BhKL8P9Tp/y6+1v8Giqj4Aoek+QWS + r/xA4/j/Sej7/xHV7f8AyeH/AMjg/wDI4P8Bn7r/RKrD/7js/v+g5f3/h+P8/23g+/9r4/r/edrs/wOF + oP8AxNv/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG3f8DyN//Ldrv/0/p/P8ryuD/AISi/ACC + oFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChKOgFJy3+E/p + /P9M5fj/KrnR/wWJp/8Xo73/DdTs/wDM5P8AzOT/AMvj/wDL4/8GmbT/YrvT/67q/f+E4/z/a+D6/1Le + +f862/j/d+j6/yeft/8AqsP/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/T + 6f9O5/v/Ls7k/wCDof0AhKFXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgJ8YAYWh+DjL4f9P6fz/T+n8/0ni9v8Nx9//AM7m/wDO5v8Azub/AM7m/wDO5v8En7n/UbPN/5no + /f9p4Pr/T935/zbb9/8n2vf/VuL5/17O4v8Bi6b/AMzk/wDN5f8AzeX/AM3l/wDN5f8AzOT/AMzk/wDM + 5P8AzOT/AMzk/yPT6P995fL/r/T9/4/j7/8nl7D8AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfSACD + of8Ag6KhAISjOgAAAAAAAAAAAoWijgqQrf9O5/v/T+n8/yPd8/8A0ur/ANLq/wDS6v8A0ur/ANHp/wDR + 6f8AsMn/CYej/3jg9v9r4/r/QN34/yfa9/8n2vf/Mtz3/33o+f8Qjqj/ALfQ/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8W0uj/VNbn/1HE1f8rqb//Ip63/1C6zf+R1+P/Sae99wCAoyQAAAAAAAAAAAAA + AAAAAAAAAIKhagCDof8AlLH7AIOh/QCDokoAAAAAAISiugOIpv8uvtb/QOX5/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Azef/BJSw/waHo/8yu9T/VuH3/07h+P8w3Pf/J9r3/2bl+f9GvdT/ApSv/wDS + 6/8A0uv/ANLr/wDS6v8A0ur/ENTp/zDF2v8lts3/AKjD/wC2z/8At8//AK3H/w6ZtP8Bgp//W7XI/iaW + r9gAkpIHAAAAAAAAAAAAAAAAAIKgVgCDof8A1O3/AKK9/gCFo9oAhKE2AIOh/x250v8Gi6n/BtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wO50/8Ci6f/DJGr/0HK4/9h5Pn/TuH4/2rl + +f985PX/AoCc/wDD3f8A1O3/ANTt/wTR6P8byuH/FcLb/wDO5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV + 7v82z+X/CpCt/zKdtf0AhaKWAAAAAAAAAAAAAAAAAIOiZQCIpfoA1O3/ALnU/wCDof8AhKGeCZq3+03p + /P8Nqsb/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/BtXt/wnW7v8M1u7/Dtbu/xDX7v8R1ez/Cq/J/wKF + oP8an7n/W9nu/2jh9f8orsf/AH+b/wO2z/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8P2PD/SOP2/xGduP8KiKT/AISiVQAAAAAAAAAAAIShmwCSsPkA0+z/ALHM/wCD + of8Ag6H/Kc/l/0fn+v8B1e3/ANTt/wDU7f8A1O3/BNXt/wnW7v8O1u7/Edfu/xXY7v8Y2O//Gtjv/xzZ + 7/8d2e//Htnv/xzS6P8MpL//AYCb/wKAnP8Im7X/EMvi/xDX7v8M1u7/B9Xt/wLU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Id30/03n+v8No77+AISi7wCAnxAAjqoJAIWi8ACu + yf8A0en/AMbf/wCfu/4Aj6z8R+b6/yrf9f8A1O3/ANTt/wPV7f8J1u7/Dtbu/xTX7v8Y2O//Hdnv/yHa + 7/8k2vD/Jtrw/ynb8P8p2/D/Ktvw/yrb8P8p2/D/Iczi/xvB2f8i2u//H9nv/xrY7/8V2O7/ENfu/wvW + 7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk+f853vP/AIqm+gCC + oYUAg6FnAIWj/ADL5P8A0en/ANPs/wDT7P8P1+//FZ65/wKLqP8Cob3/AqTA/waqxf8Krsj/DrLL/xO2 + z/8XvNT/Icvi/yzb8P8w3PD/Mtzx/zXd8f813fH/Nt3x/zbd8f813fH/Mtzx/zDc8P8t3PD/Kdvw/yXa + 8P8f2e//Gtjv/xTX7v8O1u7/B9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXZ + 8f9P6fz/DbjS/wCFo+oAhaLuAKbB/gDT7P8A0+z/ANLq/wDS6/8i3fT/GKO9/wCDof8ChaP/Ia7H/x2p + w/8ZpL//FJ66/xGatv8OlrL/C5Wx/zfd8f873vH/Pt7x/0Hf8v9B3/L/Qt/y/0Lf8v9B3/L/Pt7x/zze + 8f843fH/NN3x/y/c8P8p2/D/JNrw/x3Z7/8X2O//ENfu/wjV7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f9N6fz/IdPq/wCDof8Aiqf6ANDo/wDT7P8A0ur/ANHp/wDS6v8u3/X/Qeb6/wC+ + 2P8Diqj/GaS//0vj9/9P6fz/T+n8/0/p/P9P6fz/P9/y/0Pf8v9H4PL/SuDy/03h8v9O4fP/TuHz/07h + 8/9M4fL/SuDy/0fg8v9D3/L/P97x/0/i8/9c4/T/ZOX0/2Lk9P9X4/P/Qd/y/yDa7/8H1e3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9E5vr/Ktft/wCDof8Awdv/ANHp/wDN5v8AyOD/AMzk/wDP + 5/864/f/OOP4/wDU7f8OzeX/CJaz/wuRrv9B2O3/T+n8/0/p/P9M5/r/SeDy/07h8/9S4vP/VuPz/1nj + 8/9a4/P/WuPz/1nj8/9Y4/P/VeLz/1Li8/9W4/P/duj1/3Th9v9gyPT/Xrby/1qw8f9ZufL/Vczz/1ni + 9P883vH/Cdbt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9L6Pv/JM3k/wCEovcA0ur/ANLq/wC3 + 0v8Ag6H/AISi/gCHpf074ff/M+L3/wDU7f8T1+7/Jtru/xSpw/8Dh6X/MsTb/0/p/P9O5ff/U+Lz/1nj + 8/9e5PT/YeT0/2Tl9P9m5fT/ZuX0/2Xl9P9j5fT/YOT0/2Ll8/9/6vb/Z8vy/06d7f9tqeH/krTT/5m3 + 0P+Ksdb/WaPn/06j7P9K1fH/Od7x/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wnW7/9P6fz/GLPM/ACE + orIA0uv/ALvV/wCEov0Ag6CvAIKiWACDof832u//LtTp/wDN5/8U1+7/Ltzw/zfd8f8qwNb/A4il/yCs + xv9S4/P/XeT0/2Pl9P9p5vT/beb1/3Dn9f9y5/X/cuf1/3Hn9f9v5/X/a+b1/33p9v9lxen/SZfl/6S7 + zv/Dycn/wcjI/8LIyP/Fy8v/ys/Q/5C22f9Dl+T/Otfw/xrZ7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c + 8/9O6Pz/BYuo/QCEoFkAp8P/AISi/gCEoo8A//8BAAAAAACFo/AVpsL/AYWi/we91v8P1+7/Nd3x/z3e + 8f9G4PL/Q9Pm/w6Trv8xts3/ZuX0/23m9f9z5/X/eOj1/3zp9v9+6fb/fun2/33p9v966fb/fOn1/3ne + 9/80gL//qsDS/8vQ0P++xcX/vcTE/7m1rf+0oY7/vbeu/9jc3P+Br9v/OKTi/yTa7/8C1O3/ANTt/wDU + 7f8A1O3/ANTt/zji+P882u//AISj6wCAqgYAhaPxAIShXwAAAAAAAAAAAIGjRQKHo/oNlLH/P9Tq/zfj + 9/8H1e3/Od7x/0Pf8v9M4fL/VeLz/1zj8v9l4/L/b+f1/3bo9f996fb/g+r2/4fr9/+K6/f/iuv3/4nr + 9/+E6vb/hur2/1S27v86mt7/wcTE/8DHx//Ax8f/vLCj/6JlNv+IVzH/omU2/8Gwof/a4uj/MILX/xvQ + 7P8H1e3/ANTt/wDU7f8A1O3/B9bv/03o/P8htMz8AIKikQAAAAAAgJwSAAAAAACAnBIChaSlAYWj/Cm5 + 0f5N5/r/T+n8/0/p/P8X2/H/M93x/0jg8v9R4vP/W+Pz/2Tl9P9t5vX/duj1/37p9v+H6/f/juz3/5Pt + 9/+W7fj/lu34/5Pt9/+O7Pf/iev3/0Ko6v8+r/z/gpek/8TKyv/O09P/p3lU/0suGP8AAAD/QSgV/6Nw + SP/4+fn/O4fU/xG95f8C1O3/ANTt/wDU7f8A1O3/Id30/03o/P8Gi6j/AIOgRgAAAAAAAAAAAIKiWAKG + o+8RmbX4Q9nu/0/p/P9P6fz/T+n8/0/p/P9B4fX/INXt/0vh8v9U4vP/XuT0/2jm9P9x5/X/e+n2/4Tq + 9v+O7Pf/lu34/53u+P+i7/j/ou/4/53u+P+W7fj/juz3/0Go6v9Is/z/MlVt/3Z5ef/a3t7/sIZk/3RS + Of+DgoL/uKqf/7GIZ///////LX3M/wm65P8A1O3/ANTt/wDU7f8A1O3/PuX5/zfc8P8Ag6L8AIiZDwAA + AAAAgqJgAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYel/0nf8P9W4/P/YeT0/2vm + 9f916PX/f+n2/4nr9/+S7ff/nO74/6bw+f+t8fn/rfH5/6Tw+f+a7vj/kez3/1C38P9LpuP/PnWa/yw2 + Pf/Lzs7/5NzV/6lzSP/Xu6b/1Lmk/9zSyf/n7fH/Fm3D/wHO6v8A1O3/ANTt/wDU7f8L1/D/T+n8/xu5 + 0/4AhaLDAAAAAAAAAAAAgJkKAIWgSwCCoDMAgqAzAIKgMwCCoDMAhKHJC6XB/0Tb7/8PzOX/AMzm/yvb + 8P9X4/P/YeT0/2zm9f926PX/f+n2/4nr9/+T7ff/nO74/6bw+f+u8fn/rPH5/6Tw+f+a7vj/kOz3/3rg + 9v9auPn/Tomx/zJNXv8qLTD/4+Tk//b19P/j29T/6erp//39/f9imc3/EZTQ/wDU7f8A1O3/ANTt/wDU + 7f8q3/X/S+f7/wOJp/sAg6JjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8BaoTdAISi/zfh + 9v834/f/ANTt/wXV7f9O4fP/YOT0/2rm9P9z5/X/fen2/4fr9/+P7Pf/mO74/5/v+P+i7/j/oe/4/5zu + +P+V7fj/i+v3/4Lq9v9cve3/b7zw/0Zof/8xQUz/JSkr/6usrP/4+Pj/+vv8/3qn0v8PcL3/Cc/r/wDU + 7f8A1O3/ANTt/wfW7v9L6Pv/LdHn/wCFo/IAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgNf + dMYPdor4Dpe0/w2wzP9O6Pz/Ftrx/wC81v8EjKr/XuT0/2fl9P9w5/X/eOj1/4Hq9v+J6/f/j+z3/5Tt + 9/+X7fj/le34/5Lt9/+M7Pf/hOr2/33p9v905/X/Xrfk/1+Rs/9mk7L/KD1M/w8VG/8AAQH/AQUJ/wc2 + Vf8axuH/AdTt/wDU7f8A1O3/ANTt/y/h9v9K5/v/BpGv+QCEoXoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVYAGA11zyABab/8glKn+C3qR9wCEof8s2u//QNzx/wKKqP8tvdT/Q+T3/2Hl9P9q5vT/cuf1/3rp + 9v+A6vb/her2/4nr9/+L6/f/iev3/4fr9/+C6vb/fOn2/3Xo9f9t5vX/ZeX0/2DZ8f8+d5P/PG2M/y1T + a/8jUmv/ECsz/wkzOP8EOD//AM7m/wDU7f8A1O3/Fdnx/07o/P8ixt3+AIaj5ACAmQoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBbcP0DX3WvAFlubQCBnq4Dk7D4FKC7/xagu/9P6fz/T+n8/0rm + +v9g5fX/aub0/3Hn9f926PX/e+n2/33p9v9/6fb/fen2/3vp9v936PX/cuf1/2zm9f9l5fT/XeT0/1Xi + 8/9Bvsz/Lpaj/0fBz/9BtMP/R4GI/yq0xP8jjJj/HoKO/wDU7f8L1/D/Sej7/zbc8f8Ag6H9AIOgRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXMUAFhxNAAAAAAAAAAAAAAAAACApA4AhKLsBImm/0ff + 8/9P6fz/T+n8/0/p/P9N6Pv/StTm/1bV5/9s5vX/b+f1/3Hn9f9y5/X/cef1/2/n9f9s5vX/Z+X0/2Ll + 9P9b4/P/VOLz/03h8v9F4PL/OM3f/zvW6P9Dydn/RMnZ/1mzvv8A1O3/RKy4/xnN4/9E5vr/QeP4/wOO + q/kAhKKRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG + oSYBhKP5Ncfe/0/p/P9P6fz/S+T4/y/A1/8Nl7P/AYWk/zC6z/9h5PT/YuTy/0jJ2/9m5fT/ZeX0/2Tl + 9P9g5PT/XeT0/1jj8/9S4vP/TOHy/0Xg8v893vH/Nt3x/y3c8P8l0OX/D8bc/yiPm/8A1O3/Edfu/1TB + z/9B5Pn/CJez+gCFo8IAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIC/BAKIpM8cqMH7T+n8/z/U6v8eqsT/BIil/wqRrv8GsMz/AM3n/wbV7f8z3fH/Qszg/wWJ + p/9X5fX/WePz/1jj8/9V4vP/UuLz/03h8v9I4PL/Qt/y/zze8f813fH/LNvw/xbY7/8E1e3/ANDo/wGa + q/8j2PD/Ncrg/zW/0f8Fkq76AISj0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISigQSJp/squtL/DZSw9gGEovwChqP5Cpm1/kTk+P9M6Pv/Jd70/wPV + 7v8A1O3/BcLb/wSIp/9P6fz/Tuf6/03j9P9J4PL/RuDy/0Lf8v893vH/N93x/y/c8P8a2O//BdXt/wDU + 7f8A1O3/Fdnx/zzX6/85zeP/Aoek/wKIpfwAgZ7FAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCDof8Dh6ThAIOicwCAphQAgKEmAH2a/ASK + qP8sz+X/Tuj8/0fm+v8k3vT/BcPc/wOIp/9P6fz/T+n8/0/p/P9A5fn/Fr3V/xLK4v8Q1+7/Ctbu/wHU + 7f8A1O3/A9Xu/xrb8v8+5fn/T+n8/0nm+v9F3PD/AIOh/wCDopYAmZkFAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh+gCCoj8A//8BAAAAAAAA + AAAAW3BUBWR6+Ci3z/8DiKX/DZu3/zLU6v9M6Pv/O9Hn/wSJp/9P6fz/T+n8/0/p/P87z+X/AIOh/wi/ + 2f8L1/D/Ftrx/yPd9P814vf/TOj7/0fe8v8uyeD/R+H1/0/p/P9O6Pv/AYWi/QCCoDMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAADX3bBJZ2x/k/p/P9C1ur/DYmj/QCCoP8Gjar9GLDK/gSIp/9P6fz/T+n8/0vk + 9/8Kj6z/KbjP/0/p/P9P6fz/T+n8/0rn+/874fX/I8vj/wiat/0BhKL/N8rg/0/p/P9P6fz/CpCt9QCE + oFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbCEAW3D9RNXo/zvF2f8IaXz4AmB00ABogDYAg6BpAISj0gOH + pf9P6fz/T+n8/x2pw/8Gjqz/GLbQ/xWzzv8Ur8r/C566/wGJpv8Ag6H/AIKg/wCCoKwAgqKPAoWj+z7T + 6P9P6fz/F5+7+ACEon4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcb4UQd4z4I5mt/AFccfsCXHB/AECABAAA + AAAAAAAAAISiPAOHpfpP6fz/N8vh/wGEov0Ag6K3AISjtwCBnMIAdZH+I67H/zDC2f8pq8H/A2B13AAA + AAAAAAAAAoajegOHpfpB1+z/JbPM/wKGpKkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABab/8AWm//Al913QBc + cDIAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfpJ4vX/BYuo+ACEonAAAAAAAAAAAAAAAAADYHXfLq7C/0/p + /P8cjaL7AlxwlgAAAAAAAAAAAAAAAAKEopcGjKn4KLbO/wSIptQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhuOgBa + b/8CXnKWAGBgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfoao735A4ikxQD//wEAAAAAAAAAAAAA + AAAAW252D3SI9k/p/P8HaHz2AFtwVAAAAAAAAAAAAAAAAAD//wEDhqSpAIOh/wCDof8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAGZmBQBccSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8BhKP3AISlHwAA + AAAAAAAAAAAAAAAAAAAAXHAZAVxw/Ti+0/8AWm/+AFlzFAAAAAAAAAAAAAAAAAAAAAAAgKoGAYSj2gCD + of8Ahp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACD + of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA190uwZlef8DYHXcAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIiZDwCDoqEAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICfCACEoFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpwUgBab/8CXHGZAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD//n8f7/8AAP/+Px/P/wAA//8eH4//AAD/9wAAD/8AAP/jAAAP/wAA//AAAA//AAD78AAAA/8AAPnw + AAAB/wAA+GAAAAD/AAD8AAAAAH8AAPwAAAAAPwAA/gAAAAAfAADOAAAAAA8AAMYAAAAABwAAwgAAAAAD + AADAAAAAAAMAAIAAAAAAAQAAgAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAACAAAAAABAAAYAAAAAAEAAHgAAAAAAQAA4AAAAAADAADAAAAAAAMAAIAAAAAAAwAA/AAAAAAH + AAD8AAAAAAcAAPgAAAAADwAA8AAAAAAPAADhAAAAAB8AAP+AAAAAHwAA/4AAAAA/AAD/AAAAAH8AAP4A + AAAA/wAA/jgAAAH/AAD++AAAA/8AAP/wAAAD/wAA//BgAAP/AAD/4fAA4f8AAP/j8eDh/wAA/+fx8fH/ + AAD///Px+f8AAP//9/H9/wAA////+f//AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDof8Giql2AAAAAAAA + AAAAAAAAAAAAAAVjeegGZXrcAAAAAAAAAAAAAAAAAAAAAAD//wEBhqS4AIKjPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACY2qrwWJ + pvoGi6daAAAAAACOqgkAb4haFIif8Qhzie4AhJ46AIWmFwAAAAAAgKoGB4ypsgCDof8AhKUfAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXnJyA2F1swBV + gAYFiaZwLsDX/g2UsfYBiqfhAYqo8Amct/oTq8b/D6vF/gehvPEBj6ztAIin6geNqOstu9T5CI2r4AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABc + bycAWm//B2qA4gCEoM8grsf+RN/y/irC2f8ru9P/JZu0/xaSrP8z2u7/O9/z/0Xl+P8Rnbj/N8vh/zvQ + 5f8GjKrnAIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhKLkAIKiNwAA + AAAAAAAAAAAAAAV1jdIIkq7/Ksri/xijv/8l1Ov/Ep23/3jG2//E7v3/W7bL/wCzzP8AwNf/AMDX/w7I + 3v8m1ev/L8Tb/yTD2v8KlbPxAIakcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASJ + psMBhKP+CI2qwwCFoiwAiad3DJ659UPk+P8p3PH/A8fe/waqwv9rwNf/ser+/5bk/f+Q3vD/CZu2/wDD + 2v8Aw9r/AMPZ/wDD2f8DxNv/ItPp/0bm+P8as8z2AIimqQCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICmFAqRrug+1On/Epu28gqUsPs+3PD/F9ft/wDK4v8AyeH/D5ax/6vl+f+T5P3/beD6/2zk + +f8qpL3/AMDY/wDI3/8AyN//AMfe/wDH3v8Ax97/Csvi/z/h9v8qyd/6AImmtQCqqgMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAABoupfyq60vhP6fz/O9Dn/xHA2f8Azeb/AM3m/wDN5v8QmLP/muX5/2ng + +v9D3Pj/L9v3/17P5P8CpcD/AMzk/wDM5P8AzOT/AMvj/wDL4/8Ay+P/E8/m/3Pp9/902ur8F5SvsAD/ + /wEAAAAAAAAAAAAAAAAAg6H/AIumwQCAoyQAi6ILB42q+0ri9v8x4fb/ANLr/wDS6/8A0ur/ANLq/wio + w/88s8v/XuH3/zXc+P8n2vf/YuP4/xWXsv8AzeX/ANDo/wDQ6P8A0Oj/CNHo/zLM4P87vdH/G5+4/zOm + vP90ydj7Lp+2lwAAAAAAAAAAAAAAAACHpPIAvNb/AImm3ACGpWkQp8L5ILHK/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANLr/wOtyP8LlrH/QMri/1Hh+f9W4vn/TsTa/wGyzf8A1O3/AdLr/xXK4f8YxN3/AMni/wDT + 7P8A0+z/Csvj/xunwf82pbz5AIOkRgAAAAAAgIACAIim8QDU7f8Ah6T1AIin3z/i9/8Lw93/ANTt/wDU + 7f8A1O3/BtXt/wzW7v8Q1+7/E9fu/xPR5/8KpL//G6G7/0jF3P8SlbH/BrHN/wLU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/yvA1/8Kj6ruAIaeFQCEojwAmLTtANLq/wCTsPUMnLj6P+X5/wDU + 7f8A1O3/CNXu/xDX7v8X2O//Hdnv/yHa7/8l2vD/Jtrw/yfb8P8gy+L/EKfB/xvQ5/8Y2O//Etfu/wrW + 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/x3B2/wAiqejAIqnwgC71v8A0ur/ANLr/yDP + 5/8Ag6H/B5az/wqduP8NoLz/EafB/xSrxP8q1er/M93x/zfd8f853vH/Od7x/zfd8f803fH/L9zw/ynb + 8P8h2u//GNjv/w7W7v8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u//QeX6/wCJp+4AlbHwANPs/wDS + 6v8A0uv/N+P4/w650/8LlrL/QNXr/0Tb7/9B1+z/Nszi/z3d7v9F4PL/SeDy/0vh8v9L4fL/SeDy/0Xg + 8v8/3/H/Qd/y/07h8v9M4fP/P9/y/x/a7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f9I5/v/AY6s7QDL + 5f8AzuX/AK3I/wC81v9A5fr/Ctbv/xHI3/8Ml7P/N8vh/0/p/P9K5Pf/T+Hz/1bj8/9b4/P/XeT0/13k + 9P9a4/P/VeLz/2rm9f9v2fX/Y7fu/1+w7f9ete3/Vc/y/znd8f8B1O3/ANTt/wDU7f8A1O3/AdXt/0jn + +v8AiaXvAM/n/wCVsvEAh6TCAIOh/zve8/8Mzuf/G9nv/zDX7P8VoLv/JbTM/1Tk9P9f5PT/Z+X0/2zm + 9f9v5/X/b+f1/2vm9f9y5/X/Z8jr/2io3/+0wsr/wcjI/73HzP93r93/R7np/yDa7/8A1O3/ANTt/wDU + 7f8R2PD/O9zx/wCKp58Aka3wAIimgQAAAAAAiaamD5i0/x2+1/8Z2O//PN7x/0rg8v8vuM//TM/i/27n + 9f936PX/fun2/4Lq9v+A6vb/fOn2/3TZ9P9EisL/y9HS/77Fxf+yo5X/oYBl/8rEvf9tq97/Jszs/wLU + 7f8A1O3/ANTt/yrf9f8lu9P2AIWfMACCoz0AjqoJB4ypkg+WsvRD2u7/Tun8/xfZ8P9E3/L/UuLz/2Dk + 9P9u5/X/e+n2/4br9v+P7Pf/k+33/5Lt9/+L6/f/Vbfq/0ag3/+5vr7/x8W//4pcOP8mFwz/mmxH/8ra + 5/8freD/A9Xt/wDU7f8B1O3/Reb5/wqUsOwAAAAAA4SiTQCDof8Unbj8NMbd/zXH3v81x97/JcPb/zzW + 6v9Y4/P/Z+X0/3Xo9f+E6vb/kez3/57v+P+m8Pn/oe/4/5Xt+P9UuOr/RJzY/0RRWf/b2dX/mm1J/8S9 + t/+7nIP/z93q/xOp3P8A1O3/ANTt/xTa8P873vL/AIqorAAAAAAChKJuA4immAaLp4AGi6eAA4mn8yW6 + 0v8HtND/H8/m/1rj8/9p5vT/eOj1/4fr9/+V7fj/pPD5/7Ly+v+n8Pn/mO74/3ba9v9Xr+n/N1Vo/3l+ + gP/r4Nf/zLWi/+zo5P9bm8//BMXm/wDU7f8A1O3/MuH2/x+80/YAg6BOAAAAAAAAAAAAAAAAAAAAAABc + cSQFepPxHMLZ/zDh9v8C0ur/QNPm/2fl9P926PX/g+r2/5Ds9/+b7vj/oO/4/5vu+P+Q7Pf/hOr2/1q2 + 5f9WgqD/OUxY/01UWf+Xmp3/YI61/xik1f8A1O3/ANTt/wvX8P9F5vn/BI2q5wCAgAIAAAAAAAAAAAAA + AAAAW20cAVxy+iGZrfwDi6j8PuP4/wyqxf8iuNH/W+X1/27n9f966fb/hOr2/4vr9/+O7Pf/i+v3/4Pq + 9v946PX/bOb1/1m73f9Og6T/JkRY/wsfK/8HKC//CIub/wDU7f8A1O3/OeP4/x+91vYAhaRiAAAAAAAA + AAAAAAAAAAAAAAFdccwDYHbfAl52bAB2lGYJmrb1GKK9/0/p/P9K6Pv/W+X2/23m9f916PX/eun2/3vp + 9v956Pb/c+f1/2vm9f9g5PT/VeLz/0HH1/87s8H/Qr7M/z+eqf8ol6T/CMPZ/yrf9f8z2u/+AIuqvwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQiNq/JI4PT/T+n8/z/U6v8bq8X/Ja/H/2Xl + 9P9Z2On/aub0/2fl9P9i5fT/W+Pz/1Pi8/9I4PL/Pt3w/zLa7/8i0OT/LaGu/wDT7P9FxdT/O9/0/wON + qegAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjKm4M8Xa+y291v0NlbH+GqfB/xTG + 3/8D1e3/KNvw/xObtf9T5ff/VOLz/1Li8/9L4fL/Q9/y/zve8f8t3PD/Etfu/wHU7f8JtMn/Kcbe/yrB + 1v8CiqjoAICeIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIWlggCDof8IjqrfBoundwCA + mpwQnbj+O9/0/zvk+P8V2fH/CJi0/0/p/P9K5/r/M9vx/xzL4v8b2e//D9fu/wLU7f8M2PD/L+H2/0nn + +/8outH+AIek4gCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChqNnAIOfSAAA + AAAAAAAACWp+sjG70f8PnLf/HLLL/jfc8f8Rm7b/T+n8/0/p/P8cq8T/FbvU/yLd9P8v4fb/Qeb6/zjX + 7f8ludL9T+n8/zfL4f8HjKmMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABVdw8Lb4LpRtns/xF6j+wEcIqvAIuosgmOq/1P6fz/N8vg/xGfuv4qz+X/J8jg/x26 + 0/8MmLX/AIej6geLquE1yN75Q9nu/wqQrboAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAABWJ5cBB3i/oIaH3hAFxyOgAAAAAAAAAACY+s3Eni9f8JjqvtAIOhdQB+ + m4AKe5TwOc3j/xeHm+sAW3EtAICZCgeMqNU4zOP9CI2q3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm//BmV6tABddAsAAAAAAAAAAAAAAAAJj6rcF6C77QOI + o0sAAAAAAAAAAAlpf8dF1+n/CWp/5wAAAAEAAAAAAIaeFQiNquEBhaP7AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVcRIAAAAAAAAAAAAAAAAAAAAAAAAAAACD + of8IjKupAAAAAAAAAAAAAAAAA19yUxuKnvUJa4DEAAAAAAAAAAAAAAAAAISeHQCDof8AiJkPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAoOjoQCAqgwAAAAAAAAAAAAAAAAAVYAGAFpv/wRidnUAAAAAAAAAAAAAAAAAAAAAAJmZBQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/fPv//zzz//2AA//8AAP/vAAB/4wAAH/AAAA/4AAAHmAAAA4gA + AAOAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABwAAAAYAAAAGAAAAD8AAAA+AAAAfMAAAH/AAAD/gA + AB/xAAA//wAAP/8AAD//OIY//nnHP//557//++//KAAAABAAAAAgAAAAAQAgAAAAAAAABAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4yp0wuVqhgAcI8ZCnKI5QCAnwgA//8BCI2rtwCG + nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX3UjC2yAwRunw9khtc7vKLfQ+ia91f0nyeDxGarF8Ruo + wegAgJ8IAAAAAAAAAAAAAAAAAAAAAAeNqWsSmLSaAIOoIxmyy/IjyuD/K7bP/5/e8f9AutH/AMHY/w7J + 3f8gyeD/HLrU2gCMsCoAAAAAAAAAAAAAAAAAmZkFIa7H0ia40vgc0ej/AMvj/0++1f9r4Pr/Qcvj/wG/ + 2f8AyeH/AMnh/xbR5v9GzuHtFJuvMwAAAAAAlLCSAJu4qAyducYv0+v/ANTt/wDU7f8VttH/MMfg/03g + +P8ct9H/AdLr/wzM4/8cxt3/G7fP/z+2y/AAh6URAJu5pACuyegmyOHuBNLr/wTV7f8R1+7/Gtjv/xnK + 4v8hts7/ErrU/wfV7f8A1O3/ANTt/wDU7f8i1Oz/C6S8uwCtydwA0uv/F7XQ/xOmwf8mvtb/LMzh/z7e + 8f9C3/L/Pt7x/zbd8f803fH/G9nv/wDU7f8A1O3/Bdbu/yPG3uoAwNr1AJq21ibZ8f8cxd3/L8HZ/1Lj + 9P9h5PT/ZuX0/2Pl9P9wzev/fbvd/2+/4f8o0+7/ANTt/wfW7v8jw9vhAJWyexCatZ8sw9v/J9rw/0vY + 6f9p4vH/g+r2/4rr9/911vL/c6PF/6WYi/+NdWH/a77h/wHU7f8c2/L/EanHpwaKqaQapb/EJLfP+xrG + 3f9h5PT/f+n2/5zu+P+t8fn/e9rz/0Jzk/+mnpb/wbKm/2W42v8A1O3/KNXt+wCJqUEAAAAAEHiNYBGh + uvgbz+b/Ptbp/3jo9f+P7Pf/le34/4Tq9v9XoMP/O1Zo/zVVZv8IvNj/Edjw/x+/19cAAAAAAAAAAApq + gGoFdIs3HrHJ90nj+P81yN3/bOXz/3Hn9f9n5fT/VOLz/zzP4P9GxNP/J8XX/yfQ6PEAkbA6AAAAAAAA + AAAAAAAACpGsrB2pxNMWqcP3HtXs/xi40P9C5Pf/Ntrv/yfb8P8T2O//ItPq/x+40PIAmLZNAAAAAAAA + AAAAAAAAAAAAAACJoycAQIAEJJ6x1ROju+wbrsb2SeL1/x250f4n0+n/IsTa+Sa3z+4ks83FAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAABWZ8axJ6j7oAW20OFJ24jB+qw8wAhJw+Ip+13hqJn5oWmrdHD5ey4QAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVVQMAAAAAAAAAAAqProQPlLMyAAAAABJ7kbQMcodVAAAAAAiK + qGYAqqoDAAAAAAAAAAD7bwAA8A8AANAHAADAAwAAAAEAAAAAAAAAAAAAAAAAAIAAAAAAAQAAwAEAAOAD + AADABwAA8AcAAPSXAAD9vwAA + + + + + AAABAAUAAAAAAAEAIAAoIAQAVgAAAICAAAABACAAKAgBAH4gBAAwMAAAAQAgAKglAACmKAUAICAAAAEA + IACoEAAATk4FABAQAAABACAAaAQAAPZeBQAoAAAAAAEAAAACAAABACAAAAAAAAAABAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDovAAg6H/AIOh/wCDof8Ag6H/AIOhwQCA + mQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbiwAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1sIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AgqK0AICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+TAFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBablgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8IAICeIgCG + oRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JKAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoaYAqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYKAFpv7wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm+OAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJaAIOh5gCD + of8Ag6H5AIOijgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfIACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AISglwD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtwYgBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvxQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6F/AIOh/gCD + of8Ag6H/AIOh/wCDof8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + bskAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/YAQIAEAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6GgAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOhsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiygCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEongAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABY + bTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpuMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACImQ8AhKG8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oaAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW2+YAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Q9Pn/wRhdv8AWm//AFpv/wBab/8AWm//AFpv/wBa + cGkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoh4Ag6HTAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6JzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJ2AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAgqJaAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVWoMAFpv8wBab/8AWm//AFpv/wBab/8AWm//FYGV/0/p/P8Uf5P/AFpv/wBab/8AWm//AFpv/wBa + b/8AWm+fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoTEAg6HlAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIWgSwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCE + ok0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtuaABab/8AWm//AFpv/wBab/8AWm//AFpv/za7z/9P6fz/JZyx/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv1wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoUkAg6HzAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoiEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H1AIOfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABab88AWm//AFpv/wBab/8AWm//AFpv/whoff9O5vr/T+n8/za8z/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/0AVXcPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoWcAg6H8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoZ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAA//8BAIOh9ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDovAAhKI0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFpv/wBab/8mn7T/T+n8/0/p/P9G2ez/AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpxRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCDoYgAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKFXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDocsAg6H/AIOh/wCDof8Ag6H/AIOh/wKFo/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh6gCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+eAFpv/wBab/8AWm//AFpv/wBab/8BW3D/Rtnr/0/p/P9P6fz/T+n8/wlq + f/8AWm//AFpv/wBab/8AWm//AFpv/wBab3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDoagAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AICcEgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KhAIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/JrPN/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAYHAQAFpv9QBab/8AWm//AFpv/wBab/8AWm//F4WY/0/p/P9P6fz/T+n8/0/p + /P8Zh5z/AFpv/wBab/8AWm//AFpv/wBab/8AWW+xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICcEgCDoMIAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhyAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdwCDof8Ag6H/AIOh/wCDof8Ag6H/C5Kv/0/p + /P8hrcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhaYXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFlubQBab/8AWm//AFxx/wBedf8AYXf/AGR7/yeuxP81x97/M8bc/y/A + 1/8vwNf/GJev/wBqg/8AaoP/AGqD/wBqg/8AaIH/AGmA7wCDoUQAhKE2AIShGwCAvwQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgIwCDodgAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoYIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoUwAg6H/AIOh/wCDof8Ag6H/AIOh/wGE + ov9N5vn/T+n8/xynwv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKEbAIKiPwCDomMAgqGFAIOioQB5lfQAfJj/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIOh5gCD + ocwAgqCyAISglwCEon4Ag6BhAISiNACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiNwCDoekAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x6qxP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqA7AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/QNbs/0/p/P9N5/r/GKK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQCDn0AAg6FvAIShnQCD + ocwAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIKh1wCDoakAg6B5AIWgSwCAnRoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShUQCDofYAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/BImm/zrN5P8mtM3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAKqqAwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCDofQAg6H/AIOh/wCD + of8Ag6H/AIOh/zPF3P9P6fz/T+n8/0zl+P8Unbj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + obsAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKgKwCCoWQAhKGdAIOh1gCDof4Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh3ACD + oZoAgqJYAICiFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCDofwAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/CY+r/0HX7P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKirAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAF10FgBbcGIAWm5dAGBwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6HMAIOh/wCD + of8Ag6H/AIOh/wCDof8mtM3/T+n8/0/p/P9P6fz/SuP2/xGZtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKhrgCqqgMAAAAAAICkHACComAAg6KkAIOh5wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof4Ag6LSAIOhkACFoEsAkpIHAAAAAAAAAAAAqqoDAIOhkACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/D5ax/0fe8v9P6fz/SeH1/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + omUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFttRgBab/UAWm//AFpv/wBab+8AW3BrAICAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhogCD + of8Ag6H/AIOh/wCDof8Ag6H/GaO9/0/p/P9P6fz/T+n8/0/p/P9I4fT/DpSw/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HQAISh0ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACDoqEAhKFRAIKhsACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/FqC7/0vk9/9P6fz/T+n8/zPF3P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AgKIeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBab+gAWm//AFpv/wBab/8AWm//AFpv/wBab8sAXHAyAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + ongAg6H/AIOh/wCDof8Ag6H/AIOh/wySr/9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8v8Lka7/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/H6vF/07n+/9P6fz/T+n8/0/p/P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCD + of8AgqHXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXbSMAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+wBa + cJAAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJNAIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/Tef5/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wiO + q/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6L/AImn/wCOrP8Ak7D/AJi0/wCZtf8AmbX/AJ26/wCeuv8Anrr/AJ66/wCeuv8Anbn/AJm1/wCV + sv8Akq//AI2q/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0/p/P9P6fz/T+n8/0/p/P9P6fz/CI6r/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFlv5ABbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICeIgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/0HX7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9B1+z/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCLqf8Al7T/AKK+/wCuyf8At9H/AL/a/wTJ + 4v8H0uv/C9fv/w3X7/8Q2fD/FNrw/xXZ8f8V2fH/Fdnx/xHY8P8Q2fD/ENnw/xDZ8P8L1/D/B9bu/wLV + 7f8A1O3/ANTt/wDU7f8A1O3/AM7m/wDG3/8AvNf/ALXQ/wCsyP8AoL3/AJKv/wCEo/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ChaT/M8Xc/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab6gAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFtvtABdbCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACAgAIAg6H1AIOh/wCDof8Ag6H/AIOh/wCDof8zxtz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/z7T6f8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhaP/AJWy/wCkwP8Ft9H/EMri/xvZ7/8m3vX/MOH2/zfj+P8+5fn/Ruf6/0zo + +/9P6fz/T+n8/0/p/P9O6Pv/RNru/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/0fn+v9B5vr/OOL4/zDh9v8n3vX/Hdzz/xTZ8f8J1u//ANTt/wDU7f8A0uv/AMXf/wC2 + 0f8AqMP/AJm1/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Fiqf/O8/m/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yu60v8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDofgAjqoJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWG0xAFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/2AFlveABVgAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOgzQCDof8Ag6H/AIOh/wCDof8Ag6H/JrTN/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/O8/l/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ai6j/AaC9/w261P8d0Oj/K9/0/zvk+P9J6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/SN/z/zDA1v8aorv/Boei/wGAnP9E2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8/5fn/MuH2/yXe + 9f8W2vH/BtXu/wDU7f8A1Oz/AMPd/wCuyf8AmbX/AIak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8KkK3/Q9nt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Vnrn/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb7cAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/UAFlvPAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaMAg6H/AIOh/wCDof8Ag6H/AIOh/xmjvv9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83yuD/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSP + rP8n1+7/O+T4/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DV + 6/8pts3/EZaw/wGAnP8Af5v/AH+b/wB/m/8Af5v/Iq3F/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9E5vn/M+L3/yLd9P8Q2PD/AdXt/wDR6f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8RmLT/SOD0/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/AoWk/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AISgdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW0/AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/4AWm+cAF5rEwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Mk6//T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLE2/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8ChaP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I4PP/KrjP/wuO + qf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWGof9L5Pf/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fi9/8Pnrr/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Yor3/TOX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OMvh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b8YAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b+sAW25fAAAAAQAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh2ACDof8Ag6H/AIOh/wCDof8Ag6H/AYWi/03n + +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Lb3V/wCDof8Ag6H/AIOh/wCD + of8Ag6H/D5ay/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+j8/zvP5P8bo73/AoKe/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/LbvS/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8Pl7L/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ir8j/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKv + yf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofEAkrYHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWm5PAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab8EAV3ApAAAAAACEojQAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of9B1+z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puM//AIOh/wCD + of8Ag6H/AoWj/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N8ne/w6SrP8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wuOqf9O6Pv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl+P8ChaP/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wGEov8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8Mk6//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAQBab9UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AGB1+gB6l9UAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/NMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7U + 6f8ltM3/J7XO/0Ta7v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P87zuT/Epew/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/D4eh/xOJov8Af5v/AH+b/wB/m/8Af5v/NcTa/03k + 9/9N5Pf/TeT3/07l+P9O5vn/Tuf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8yxNv/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wOGpP82yN//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9F3fH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOivwCCoTEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm9eAFpv/wBab/8AWm//AFpv/wBab/8AWm//IJSo/x2PpP8AW3D/AFpv/wBa + b/8AWm//AFpv/wBab/8AXHH/AG6I/wCBn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/ya0zv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bd8f8Xnrf/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wKAnP8kkan/Tqa6/2ezw/9Mpbn/AH+b/wB/m/8Af5v/AH+b/wGQ + qv8Fwtj/BcLY/wXC2P8Jwtn/DcTZ/xHF2f8Wxdr/Gsjd/x7K3/8nzuP/L9Po/znY7P9B3fD/S+L0/03k + 9/9P6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KLfP/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/weLqf8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/L8DX/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H9AIShnQCF + phcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAECABABab+EAWm//AFpv/wBab/8AWm//AFpv/wVjeP9L4vX/O8XZ/wxx + hv8AWm//AFpv/wBab/8AZ3//AH+b/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ZpL7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLB2P8FhqH/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/CoSf/zSZr/94v9D/oNbl/7zp9/+Y0uH/Z7PD/w6GoP8Af5v/AH+b/wB/ + m/8Af5v/ALPM/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8Iwtn/FMXa/yDM4f8s0uf/ONns/0fh8/9O5/r/T+n8/0/p/P9P6fz/T+n8/znM4/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuSr/9E2+//T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xmkvv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HtAIShXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW5tAFpv/wBab/8AWm//AFpv/wBab/8AWm//K6e7/0/p + /P9N5vn/KaS3/wNieP8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/DJOw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SN/z/xefuP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8MhaD/SKS5/5HO3v+55/X/yPD+/8jw/v/I8P7/wOv6/2u1xv83m7H/AH+b/wB/ + m/8Af5v/AH+b/wCYsv8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/CsPZ/xrJ3v8p0+f/O9vv/0vk9/9P6fz/IK3G/wCD + of8Ag6H/AIOh/xWeuf9J4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07n+/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhKG4AICfIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFVxCQBab+sAWm//AFpv/wBab/8AWm//AFpv/who + ff9N5vn/T+n8/zDC2f8EiKb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGFov9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/854/j/G8Xc/wGHov8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wiDnv9Pqr//pNrp/8Xu/f/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v+Ny9v/X6/A/wKA + nP8Af5v/AH+b/wB/m/8Agp7/ALvS/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8CwNf/Esfb/ybS + 5v8uyeD/Lr7W/zvQ5f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P880ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAg6FtAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//K6zA/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCF + o/8Prcf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Qdfs/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9K5/v/M+L3/xrZ8P8EzeX/AKzF/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/zqetv+g2Of/x/D+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/t+X0/2ez + w/8kkan/AH+b/wB/m/8Af5v/AH+b/wChuf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wTB2P8Vy+D/Ldbr/0bk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/J7XO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wCIpv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq + qgMAgqCBAIOh5gCDoewAg6CnAIakKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBwEABab/MAWm//AFpv/wBa + b/8AWm//AGd//wGCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wql + wf823/T/Tuj7/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/zTH3f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/zPi9/8W2vH/AtDn/wDH3v8Av9f/AKC6/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/GY2n/4/P4f/F7/3/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw + /v9/wtL/Taa5/wB/m/8Af5v/AH+b/wB/m/8AhqH/AL7W/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/EMje/yjV6v9B4/b/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xijvv8s3fL/B6rG/wCE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh8QCEol0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqKDAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6KhAIOiIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBa + b/8AXHL/AHSO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5Ku/yfT + 6v9M6Pv/T+n8/0/p/P8PlrL/AIOh/wCDof8Ag6H/AIOh/wCDof8ntc7/T+n8/0/p/P9P6fz/T+n8/0/p + /P895Pn/INzz/wXV7f8Ay+P/AMPZ/wDA1/8AwNf/AJq0/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/S6vB/7vp+P/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw + /v/I8P7/qt3s/2ezw/8Qh6H/AH+b/wB/m/8Af5v/AH+b/wCowf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/w7J3v8q2Oz/R+b5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8vwNf/T+n8/0vo + +/8n1+7/BJi1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOipACA + pA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOh5ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKGTAIWjGQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhsGgBa + b/kAYXf/AHyZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaT/GL/Y/0Tm + +f9P6fz/T+n8/0/p/P9P6fz/HKfB/wCDof8Ag6H/AIOh/wCDof8Ag6H/GqS//0/p/P9P6fz/TOj7/y7g + 9f8M2PD/ANLr/wDJ4f8Awdj/AMDX/wDA1/8AwNf/AKK7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8IhKD/hczg/8bw/v/G8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8jw + /v/I8P7/yPD+/8Xv/f90u8z/OZyx/wB/m/8Af5v/AH+b/wB/m/8AjKb/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHD + 2f8VzuT/Mt7z/03p+/9P6fz/T+n8/0/p/P9P6fz/DZSw/wCDof8Ag6H/AIOh/wCDof8ChaT/Rt3y/0/p + /P9P6fz/T+n8/0jn+v8hyOD/Aoim/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKLdAIKiNwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAP//AQCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgqGHAICfEAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAbYa/AIGe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Gm7j/Nd3z/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yKvyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xGZtf9G5vr/Jd70/wXW + 7v8A0uv/AMnh/wDC2f8Awdj/AMHY/wDB2P8Awdj/AKnC/wCAm/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Ui6b/otzu/8Xv/v/F8P7/xvD+/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/8Hu/v+u6f7/oub+/8Du + /v/I8P7/yPD+/8Xw/v/D8P7/mtXl/1+vwP8DgZz/AH+b/wB/m/8Af5v/AH+b/wCwyP8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8Gxdz/JNfs/0Xm+v9P6fz/T+n8/zPF2/8Ag6H/AIOh/wCDof8Ag6H/KbjQ/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8XsMn/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDofsAg6F1AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6GpAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oe8Ag6B5AIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq + qgMAg6GQAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCGpP8Xwtv/SOf6/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8puND/AIOh/wCDof8Ag6H/AIOh/wCDof8Lor7/AtTt/wDT + 7P8AyuL/AMPZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/ALXN/wCBnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8gk63/reP1/8Pv/v/E7/7/xe/+/8bw/v/G8P7/x/D+/8jw/v/I8P7/xO/+/7Dq/v+i5v7/oub+/6Lm + /v+x6v7/yPD+/8Xw/v/C8P7/wO/+/7ns+v9stsf/JZKq/wB/m/8Af5v/AH+b/wB/m/8Ala//AMHY/wDB + 2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Bw9n/F9Hp/zni9/9P6fz/N8rg/xihvP8Vn7n/Lr7W/07n + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfZ7v8KlLH/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoagAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIafKACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoecAg6FtAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA + nxAAhKG4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSZtf8w3fL/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/w+Xsv8Ag6H/AIOh/wCDof8Ag6H/ALrW/wDM + 5P8Aw9n/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMDX/wCKpf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8dkKv/suX3/8Lv/v/D7/7/xO/+/8Tv/v/F7/7/xvD+/8bw/v/H8P7/uOz+/6Tn/v+i5v7/oub+/6Lm + /v+i5v7/pOb+/8Lv/v/C8P7/v+/+/7zv/f+57v3/i87e/0+nuv8Af5v/AH+b/wB/m/8Af5v/AICc/wC5 + 0f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8Awtn/Ds3j/zTg9f9O6Pz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/ye/2P8ChqT/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QCDoCMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKGTAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEod8AhKFfAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + nyUAg6HYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/w+20P9E5vn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3vL/D6S//wCJp/8Aj63/ALTO/wDF + 3P8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wCdtv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8QiaT/qOL0/8Hu/v/B7v7/wu/+/8Pv/v/E7/7/xe/+/8Xw/v/C7/7/q+j+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oub+/53l/f+x6/3/v+/+/7zv/f+57v3/tu79/6nm9f9os8T/EYii/wB/m/8Af5v/AH+b/wB/ + m/8Anbf/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB2P8Awtn/Cs3l/zHf + 9P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qt/z/xCZ + tP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HuAIOfSAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICmFACDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoNUAg6JSAP//AQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + ojcAg6HqAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIuo/x3P5/9M6Pv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P864/j/Ddfw/wDU7f8A0uv/AMnh/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wC40P8AgJz/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Gg5//mNjs/8Du/v/A7v7/we7+/8Lv/v/C7/7/w+/+/8Tv/v+97f7/pef+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oeX9/5zl/f+X5f3/m+b9/7zv/f+57v3/tu79/7Pu/f+x7v3/fcXV/zudsv8Af5v/AH+b/wB/ + m/8Af5v/AIOf/wC/1v8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awtn/DM3l/zfh9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9O6Pv/Kb3V/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofwAhKJ4AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6F1AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDosoAg6FEAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oUwAg6H0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Apq2/y/d8/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0bn+v8a2/L/ANTt/wDU7f8Azub/AMXc/wDE + 2/8AxNv/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8AlbD/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/bcDY/77u/v+/7v7/wO7+/8Hu/v/B7v7/wu/+/8Pv/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oeX9/5zl/f+X5P3/k+T9/47k/P+u7P3/tu79/7Pu/f+w7v3/re39/5rf7v9hsMH/BIGd/wB/ + m/8Af5v/AH+b/wB/m/8Apr7/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Aw9n/EdDm/zzj+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9B3PH/D5ey/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oZoAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDod4Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ob0AhJ84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oGEAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/B7DK/z7l+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDS6/8AyeH/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8Ats//AICb/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/MZ64/7rs/v++7f7/vu7+/7/u/v/A7v7/we7+/8Hu/v+67P7/o+b+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43k/P+J4/z/l+f8/7Pu/f+w7v3/re39/6rt/f+n7fz/c77O/yeT + qv8Af5v/AH+b/wB/m/8Af5v/AImk/wDC2f8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDC2f8AxNv/GtTq/0fm+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o+/8isMr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIShuACJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqBWAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDobEAhaIsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oWoAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhqT/DMHa/0Xn+v9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOf6/xja8v8A1O3/ANTt/wDQ6P8Ax97/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AJOu/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/CoWh/6Lg9v+87f7/ve3+/77t/v+/7v7/wO7+/8Du/v+87f7/pOb+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/oOX9/5vl/f+W5P3/kuT9/43j/P+I4/z/g+P8/4Di/P+t7f3/re39/6rt/f+n7fz/pO38/4zX + 5/9Qp7r/AH+b/wB/m/8Af5v/AH+b/wB/m/8Arsb/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Ex97/Ktvw/03p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbL4f8Fiqf/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HQAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD + ocUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ru9L/Iq/J/wKFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCDoaMAgJ4iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + onMAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/Eszk/0no+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/NeL3/wbV7v8A1O3/ANTt/wDM5P8Axt3/AMbd/wDG + 3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/ALnR/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/1i3z/+67P7/vO3+/73t/v+97f7/vu7+/7/u/v++7v7/puf+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/n+X9/5rl/f+V5P3/keT9/4zj/P+I4/z/g+P8/37i+/954vv/lej8/6rt/f+n7fz/pO38/6Hs + /P+d6/v/bLnJ/xOJov8Af5v/AH+b/wB/m/8Af5v/AJOt/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8MzOT/O+L2/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rd7y/xGZtf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeMAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhJ84AIOh/gCDof8Ag6H/AIOh/wCDof8Ag6H/CY+r/0zl+P9G3fH/HqvE/wGFov8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDopYAhaMZAAAAAAAAAAAAAAAAAAAAAACD + oXUAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak6//GNXt/0zo+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6fz/Id3z/wDU7f8A1O3/ANLr/wDJ4f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wCguf8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wyHov+o5fv/uu3+/7zt/v+87f7/ve3+/77t/v++7v7/run+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/n+X9/5rl/f+V5P3/kOT9/4zj/P+H4/z/guL8/33i+/954vv/dOH7/3rj+/+n7fz/pO38/6Hs + /P+e7Pz/m+v7/4HQ4P89nrP/AH+b/wB/m/8Af5v/AH+b/wCAm/8AutL/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDE + 2/8g1er/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/H6vF/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACEojwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoagAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8puM//T+n8/0/p/P9D2e3/GqW//wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9gCCoIkAgJwSAAAAAACD + oW0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/INrx/07o/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/Edjw/wDU7f8A1O3/ANHp/wDI4P8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDG3f8Ah6L/AH+b/wB/ + m/8Af5v/AH+b/wB/m/9Eq8X/tuv+/7rs/v+77f7/vO3+/7zt/v+97f7/uez+/6Lm/v+i5v7/oub+/6Lm + /v+i5v7/nuX9/5nl/f+V5P3/kOT8/4vj/P+H4/z/guL8/33i+/944fv/dOH7/2/h+/9q4Pr/ler8/6Hs + /P+e7Pz/m+v7/5jr+/+R5ff/YrHC/wWCnf8Af5v/AH+b/wB/m/8Af5v/AJu1/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wnK4P863/X/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8tvdX/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIOgRgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgJ8gAIOh9wCDof8Ag6H/AIOh/wCDof8Ag6H/BIil/0jf8/9P6fz/T+n8/0/p + /P8/1Or/FqC6/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wCE + oa4Ag6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AoLz/Jd3z/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P875Pj/B9bu/wDU7f8A1O3/AM/n/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Atc7/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/h9Pr/7ns/v+57P7/uu3+/7vt/v+87f7/ve3+/6vp/v+i5v7/oub+/6Lm + /v+i5v7/nuX9/5nl/f+U5P3/j+T8/4vj/P+G4/z/geL8/33i+/944fv/c+H7/2/h+/9q4Pr/ZeD6/3fk + +/+d7Pz/m+v7/5jr+/+V6/v/k+v7/3bH2P8plKv/AH+b/wB/m/8Af5v/AH+b/wCCnv8AwNj/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AcTb/yHU6f9N6Pr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/znN4/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofkAhKFRAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoIkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8fqsT/T+n8/0/p + /P9P6fz/T+n8/0/p/P880Ob/Epu2/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ApsH/Jd70/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u4PX/AdXt/wDU7f8A1O3/AM3l/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AKW+/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/GY+r/63p/v+47P7/uez+/7rs/v+67f7/vO3+/7rt/v+j5v7/oub+/6Lm + /v+i5v7/neX9/5jl/f+U5P3/j+T8/4rj/P+G4/z/geL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/2Df + +v9d3/n/lur7/5jr+/+V6/v/kuv7/4/q+/+F3/D/Uqi7/wB/m/8Af5v/AH+b/wB/m/8Af5v/AKS9/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/Dcrg/0Lh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Qtru/wqQrf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACE + ol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQOAIOh6gCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0HX + 7P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P83y+H/D5ax/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqMT/Jd71/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A0+z/AMzk/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCU + rv8Af5v/AH+b/wB/m/8Af5v/AH+b/0Oqxf+y6v7/uOz+/7js/v+57P7/uuz+/7vt/v+y6v7/oub+/6Lm + /v+i5v7/neX9/5jl/f+T5P3/juT8/4rj/P+F4/z/gOL8/3zi+/934fv/cuH7/27g+/9p4Pr/ZOD6/1/f + +v9a3/n/Vt75/3jl+v+U6/v/kuv7/4/q+/+M6vv/iur7/2/A0f8ViqP/AH+b/wB/m/8Af5v/AH+b/wCH + ov8Axd3/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Ex97/Mdnt/0/o+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9H4PT/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H+AIOgaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWoAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Vnrn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8zxtz/C5Ku/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AqsX/Jt71/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8d3PL/ANTt/wDU7f8A0+z/AMzk/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AhJ//AH+b/wB/m/8Af5v/AH+b/wB/m/9rw9z/tuv+/7fs/v+47P7/uez+/7ns/v+67P7/q+j+/6Lm + /v+h5f3/nOX9/5fl/f+T5P3/juT8/4nj/P+F4/z/gOL8/3vi+/924fv/ceH7/23g+/9o4Pr/ZN/6/1/f + +v9a3/n/Vt75/1He+f9X3/n/kev7/47q+/+M6vv/ier7/4bq+v972On/Pp6z/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AK3H/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AMbd/wDG3f8c0eb/S+T3/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9/8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAIOh1gCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/zjM4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8vwNf/CY+r/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CJWy/znN4/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vo+/8V2fH/ANTt/wDU7f8A0+z/AMvj/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AxNz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/lNzz/7br/v+37P7/uOz+/7js/v+57P7/uuz+/6Xn + /v+h5f3/nOX9/5fk/f+S5P3/jeT8/4nj/P+E4/z/f+L8/3vi+/924fv/ceH7/23g+v9o4Pr/Y9/6/17f + +v9Z3vn/Vd75/1De+f9L3fn/R934/3vn+v+L6vv/ier7/4bq+v+D6fr/gOj6/2a4yf8Ggp3/AH+b/wB/ + m/8Af5v/AH+b/wCPqv8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/w7K + 4f9E3/L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/xegu/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDoWcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Nk7D/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zm + +f8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/FJ23/z3S + 6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fn+v8P2O//ANTt/wDU7f8A0uv/AMvj/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/ALzU/wB/m/8Af5v/AH+b/wB/m/8Af5v/CIWg/6fn/v+16/7/tuv+/7fs/v+47P7/uOz+/7js + /v+g5f3/m+X9/5bk/f+S5P3/jeP8/4jj/P+E4/z/f+L8/3ri+/914fv/cOH7/2zg+v9n4Pr/Y9/6/17f + +v9Z3vn/VN75/1De+f9L3fn/Rt34/0Hc+P9Y4fn/iOr7/4Xq+v+D6fr/gOn6/37p+v900uL/KpSr/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/ALjR/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//CMnh/zvb7v9O5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/GaO+/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOiYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0vj9/8nts//BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/GKG8/0Xc8P9P6fz/T+n8/0fn+v8M2PD/ANTt/wDU7f8A0uv/AMzk/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wC0zf8Af5v/AH+b/wB/m/8Af5v/AH+b/xuRrP+n5/7/tev+/7br/v+26/7/t+z+/7js + /v+z6v3/m+X9/5bk/f+R5P3/jOP8/4jj/P+D4/z/fuL8/3ri+/914fv/cOH7/2zg+v9n4Pr/Yt/6/13f + +v9Y3vn/VN75/0/d+f9L3fn/Rt34/0Hc+P883Pj/Otv3/3/o+v+C6fr/f+n6/37p+v9+6fr/feX3/1Ws + v/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCYs/8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8CyOD/MNbp/07l+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o + +v8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4Ag6BhAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF + oS4Ag6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Hi6n/S+P3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nh9f8jsMr/Aoak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Jjqz/Rdzw/0fn+v8M2PD/ANTt/wDU7f8A0+z/AMzk/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8As83/AH+b/wB/m/8Af5v/AH+b/wB/m/8wnrj/qOj+/7Tr/v+16/7/tuv+/7fs + /v+16/3/q+n9/5Xk/f+R5P3/jOP8/4fj/P+D4/z/fuL7/3ni+/904fv/cOH7/2vg+v9m4Pr/Yt/6/13f + +v9Y3vn/U975/0/d+f9K3fn/Rd34/0Dc+P883Pj/N9v3/zPb9/9b4vn/f+n6/37p+v9+6fr/fun6/37p + +v9xytv/F4uk/wB/m/8Af5v/AH+b/wB/m/8AgJ3/AMDZ/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8l0uj/TeP1/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Tef6/xWfuf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACEoFsAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIShnQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be8f8fq8X/AYWi/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xilv/8M1/D/ANTt/wDU7f8A0+z/AM3l/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/ALDK/wB/m/8Af5v/AH+b/wB/m/8Af5v/OaO+/6jo/v+06/7/tev+/7Xr + /v+z6/3/sev9/6fp/f+Q5P3/i+P8/4fj/P+C4vz/feL7/3ni+/904fv/b+H7/2rg+v9m4Pr/Yd/6/1zf + +v9Y3vn/U975/07d+f9K3fj/Rdz4/0Dc+P873Pj/Ntv3/zLb9/8t2vf/NNz3/37p+v9+6fr/fun6/37p + +v9+6fr/e+Hy/0Cgtf8Af5v/AH+b/wB/m/8Af5v/AH+b/wChu/8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/R5v9L4PL/T+j7/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9M5vr/FJ23/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H8AIOgTgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACFoxkAg6HzAIOh/wCDof8Ag6H/AIOh/wCDof8ChqT/Rd3x/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pa7v8bpcD/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ckq//ANTt/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wCxy/8Af5v/AH+b/wB/m/8Af5v/AH+b/zmjvv+o6P7/s+v+/7Tr + /v+x6v3/r+v9/6zq/f+k6f3/i+P8/4bj/P+C4vz/feL7/3jh+/9z4fv/b+H7/2rg+v9l4Pr/Yd/6/1zf + +v9X3vn/Ut75/07d+f9J3fj/RNz4/z/c+P872/j/Ntv3/zLb9/8t2vf/KNr3/yfa9/9n5fn/fun6/37p + +v9+6fr/fun6/37p+v9qwtT/BoKd/wB/m/8Af5v/AH+b/wB/m/8AhJ//AMff/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/GtDm/0ne8P9O5vn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rl+P8RmbT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofUAhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISifgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xumwP9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0DW + 6/8Xobv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKK+/wDU7f8A0+z/AM3m/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8Assv/AH+b/wB/m/8Af5v/AH+b/wB/m/8rmrb/p+f+/7Pr + /v+v6v3/rer9/6rq/f+n6v3/oOj9/4bj/P+B4vz/feL7/3jh+/9z4fv/buD7/2ng+v9l4Pr/YN/6/1vf + +v9X3vn/Ut75/03d+f9J3fj/RNz4/z/c+P862/j/Ndv3/zHb9/8s2vf/J9r3/yfa9/8n2vf/RN/4/37p + +v9+6fr/fun6/37p+v9+6fr/eNvt/yyVrP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCsxf8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8V0Ob/SN3v/03k + 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOP3/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zzR5/8NlLD/AIOh/wCDof8Ag6H/AI2q/wDM5v8A1O3/AM/n/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8AzeX/AM3l/wDN5f8AzeX/ALbP/wB/m/8Af5v/AH+b/wB/m/8Af5v/HZGt/6bn + /v+u6v3/q+r9/6np/f+l6f3/oun9/5/p/f+A4vz/fOL7/3fh+/9y4fv/buD7/2ng+v9k4Pr/YN/6/1vf + +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z/c+P862/j/Ndv3/zHa9/8s2vf/J9r3/yfa9/8n2vf/J9r3/yna + 9/925/r/fun6/37p+v9+6fr/fun6/37p+v9btsj/AH+b/wB/m/8Af5v/AH+b/wB/m/8AjKf/AMvk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xLP + 5f9I3e//TeP1/0/p/P9Q6fz/YOv8/3Ht/f967v3/eu79/3Ht/f9Y4vP/Bomn/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HpAICjJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiYACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKb + tv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zjM4/8Lscz/ALTP/wDR6v8A1O3/ANDo/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wC81v8Af5v/AH+b/wB/m/8Af5v/AH+b/w2H + o/+e5v3/qen9/6bp/f+j6f3/oOj9/53p/f+b6P3/gOP7/3fh+/9y4fv/beD7/2jg+v9k4Pr/X9/6/1rf + +f9W3vn/Ud75/0zd+f9I3fj/Q9z4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/VeL5/37p+v9+6fr/fun6/37p+v9+6fr/ddXl/xiLpP8Af5v/AH+b/wB/m/8Af5v/AH+b/wC3 + 0P8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8By+P/ONfr/47q9v+28/r/z/n+/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/8Py+f+Cw9L/RKS6/weG + pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEodsAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6DNAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8c2/L/ANTt/wDU7f8A1O3/ANLq/wDP5/8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Axd3/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/idr0/6To/f+h6f3/nuj9/5vo/f+Y6P3/luj8/4Hj+/9x4fv/beD6/2jg+v9k3/r/X9/6/1rf + +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z7c+P852/j/NNv3/zDa9/8r2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/zLc9/996fr/fun6/37p+v9+6fr/fun6/37n+P9Epbr/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8AlbD/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/xLQ + 5v9c3u7/qO72/73y+P/C8/n/0Pf8/9H3/P/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5/v/R+f7/yvL4/73f + 5/+u2OH/YLLE/wyJpf8Ag6H/AIOh/wCDof8Ag6H/AIOhyACAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuRrv9N5/n/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM3l/wCDn/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/2jH4f+b5/z/nOj8/5ro/f+X5/3/lOj8/5Hn/P+E5fz/bOD6/2fg+v9j3/r/Xt/6/1ne + +f9V3vn/UN75/0vd+f9H3fj/Qtz4/z3c+P842/j/M9v3/y/a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/ZuX5/37p+v9+6fr/fun6/37p+v9+6fr/cMze/wiDnv8Af5v/AH+b/wB/ + m/8Af5v/AICb/wDA2v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/IdPn/3vl + 8f+58fj/vfL4/73y+P+98vj/vfL4/8Pz+f/Q9/z/0ff8/9H5/v/R+f7/0fn+/9H5/v/R+f7/0fn+/9H5 + /v/H7vT/vd/n/73f5/+u2OH/Sqe8/wGDof8Ag6H/AIOh/wCDof8Ag6GxAKqqAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGwAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/K7vS/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8w4fb/ANTt/wDU7f8A1O3/ANPs/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Akqz/AH+b/wB/ + m/8Af5v/AH+b/wB/m/9Is8z/kuX8/5fo/P+U5/3/kef8/47n/P+L5/z/ieb7/3Tj+v9i3/r/Xt/6/1ne + +f9U3vn/T935/0vd+f9G3fj/Qdz4/z3c+P842/f/M9v3/y7a9/8q2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Lf+P9+6fr/fun6/37p+v9+6fr/fun6/3zk9f8vmK//AH+b/wB/ + m/8Af5v/AH+b/wB/m/8An7n/AM7m/wDO5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8e0+j/feby/7zy + +P+98vj/vfL4/73y+P+98vj/vfL4/73y+P+98vj/xfT6/9H3/P/R9/z/0fn9/9H5/v/R+f7/0fn+/9H5 + /v/R+f7/0fn+/8Lp8P+93+f/vd/n/73f5/+IxdP/E4yo/wCDof8Ag6H/AIOh/wCCopEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKEbAIShTwCBo0UAg6IhAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIahJgCD + ofoAg6H/AIOh/wCDof8Ag6H/AIOh/wWKp/9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5vn/AtTt/wDU7f8A1O3/ANTt/wDR6f8A0en/ANHp/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AKK8/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/IJay/4jk/P+S5vz/kOf8/43m/P+K5vz/h+b7/4Tm+/+C5vv/eOT7/2Dg + +f9U3vn/T935/0rd+f9G3fj/Qdz4/zzc+P832/f/M9v3/y7a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8p2vf/def6/37p+v9+6fr/fun6/37p+v9+6fr/YMDU/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AIKe/wDI4P8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/xPR6P905PH/vPL4/73y + +f+98vn/vfL5/73y+f+98vn/vfL4/7Dw9/+X6/T/g+fy/3Tj8P+G5PH/leby/5/p8/+t7/j/xPj+/9H5 + /v/R+f7/0fn+/9H5/v/Q+f7/v+Tr/73f5/+93+f/vd/n/6rW4P8smLH/AIOh/wCDof8Ag6H/AIOh7AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGFAIOh+QCDof8Ag6H/AIOh/wCDofkAg6HYAIOhrQCDoHEAhKI0AKqqAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Iq/I/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdfv/wDU7f8A1O3/ANTt/wDS6v8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wC3 + 0P8Af5v/AH+b/wB/m/8Af5v/AH+b/wKBnP912/T/jOb8/4rm/P+I5vz/heX7/4Lm+/9/5fv/feX7/3vl + +v945fr/aOL6/07e+P9F3fj/QNz4/zzc+P832/f/Mtv3/y3a9/8p2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/1Pi+f9+6fr/fun6/37p+v9+6fr/fun6/3re + 8P8ajKX/AH+b/wB/m/8Af5v/AH+b/wB/m/8AqcP/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/Cc/n/2Lg8P+28fj/vfL5/73y + +f+98vn/vfL5/5jr9f9o3+7/Osne/xayyv8AnLf/AI2p/wCFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wOE + of8YmbH/Ub3Q/4rd6f/C9fv/0fn+/833/f++4Oj/vd/n/73f5/+93+f/ud3l/0Skuv8Ag6H/AIOh/wCD + of8Ag6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKFfAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe8AhKKZAISiNAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIahEwCDoe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9D2e7/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/FNnw/wDU7f8A1O3/ANTt/wDS6/8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8AzeX/AIKd/wB/m/8Af5v/AH+b/wB/m/8Af5v/RrjT/4Hk+/+F5vz/guX7/3/l+/995fv/euX7/3jk + +v915fr/c+T6/3Dk+v9s5Pr/VuD5/z3c+P822/f/Mtv3/y3a9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8x3Pf/fen6/37p+v9+6fr/fun6/37p + +v9+6fr/SrLG/wB/m/8Af5v/AH+b/wB/m/8Af5v/AIik/wDO5f8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Bzub/Sdzt/6rv9/+98vn/vfL5/73y + +f+Y6/X/V9fo/xi0zP8AlrH/AIOf/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/A4Og/0ezx/+d5e//yPT6/73f5/+93+f/vd/n/73f5/+83ub/VazA/wCD + of8Ag6H/AIOh6QCEoRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HJAISiTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKN0AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/GKK9/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LeD1/wDU7f8A1O3/ANTt/wDT7P8A0uv/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wCWsf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/9j0+3/fuT7/33l+/965Pv/eOX7/3Xk + +v9z5Pr/cOT6/27k+f9s4/n/aeT6/2jj+v9f4vn/Qd74/yza9/8o2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/2Tk+f9+6fr/fun6/37p + +v9+6fr/fun6/3XX6P8Ig57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Atc7/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8v2Ov/mez1/73z+f+98/n/rO/3/2Pd + 6/8atMz/AI+r/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zCiuf+M2eX/vd/n/73f5/+93+f/vd/n/73f + 5/9YrsH/AIOh/wCDof8Ag6HEAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCodkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhKC6AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKfcACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof87z+X/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/wHU7f8A1O3/ANTt/wDU7f8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8Atc7/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/y2oxP9n3PP/deT6/3Pj + +v9w5Pr/buP6/2vk+f9p4/n/Z+P5/2Tj+v9i4/n/YeP5/17j+f9P4fj/MNz3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9A3vj/fun6/37p + +v9+6fr/fun6/37p+v9+6Pn/NKC3/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJGs/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/xbU6v+A5/P/vfP5/73z+f+T6vT/RMnb/wWZ + tP8AgZ//AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/zyovv+o1d//vd/n/73f + 5/+93+f/vd/n/1Gqv/8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAhaFJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIahEwCDoewAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5ey/07o + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/wzX8P8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/AM/p/wCFof8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BoWh/zW1 + 0P9k4Pj/auP6/2jj+f9m4vn/ZOP5/2Hi+f9f4vn/XeL5/1ri+f9a4/n/XOP5/1zj+f9I4Pj/K9v3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/KNr3/3Pn + +v9+6fr/fun6/37p+v9+6fr/fun6/2fM3/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Av9j/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDQ + 6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/CdLp/2Li8f+28vj/vPP5/4Lk8P8pts3/AIml/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/Coej/33A + zv+93+f/vd/n/73f5/+23OT/FY2p/wCDof8Ag6H/AISiVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhmgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCopEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8Apb//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/C4yo/zvD3P9e4fn/YeL5/17i+f9c4vn/WuL5/1ji+f9W4vn/WOL5/1rj+f9c4/n/XuP5/1zj + +f9A3vj/KNr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/9R4fn/fun6/37p+v9+6fr/fun6/37p+v995ff/HZCq/wB/m/8Af5v/AH+b/wB/m/8Af5v/AJu2/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANHp/wDR6f8D0un/Sd7v/6rw+P+68/n/edzp/yCowP8AgqD/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCFov8AhqP/AIyo/wCIpv8AhKH/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/UKm9/7nd5f+93+f/vd/n/3m+zv8Ag6H/AIOh/wCDofIAg6MnAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAICjJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoCMAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/CI6r/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/AMnj/wCLpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/EZay/0DO6P9X4fj/V+H5/1Xh+f9U4vn/VuL5/1fi+f9Z4/n/W+P5/1zj + +f9e4/n/YOT5/1ji+f843fj/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/L9v3/3vo+v9+6fr/fun6/37p+v9+6fr/fun6/1G+0/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCA + nf8AyOH/ANLr/wDS6/8A0uv/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wHS6v8/3e//oO73/7fx+P9v0uH/GJqz/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIKg/wCTrv8AoLv/AKzG/wC40v8Axd3/AM7m/wDR6f8A0en/ANHp/wDR6f8A0en/ANHp/wDP + 5/8AxN3/ALrS/wCqxf8Am7X/AIek/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8wmrD/s9rj/73f5/+83ub/KJaw/wCDof8Ag6H/AIOh1ACAmQoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCi + vv8Aj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodAAgJkKAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ots7/T+n8/0/p/P9P6fz/T+n8/0/o/P8L1u//ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0uv/AK3I/wCEoP8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJz/F6S//0DX8P9R4fn/UuH5/1Ti+f9W4vn/V+L5/1nj + +f9c4/n/XeP5/1/k+f9h5Pn/YuT5/1Hh+f8v2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/9h5Pn/fen6/37p+v9+6fr/fun6/37p+v954fL/CoWg/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AKfB/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/zbc7v+Y7ff/s+31/2bH1/8Sjqn/AIGe/wCBnv8AgZ7/AISg/wCX + sv8AqsT/AL3X/wDQ6P8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDQ6P8Awdr/AKnD/wSPqv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ySTrP+r1uD/vd/n/5LK1/8Bg6H/AIOh/wCDof8Ag6KkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShNgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCM + qf8A1O3/ANTt/wDI4f8Ap8P/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOghAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaIsAIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/A4el/0ff8/9P6fz/T+n8/0/p/P8s4PX/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Azeb/AKK+/wCA + nf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Cg6D/H7LN/0jc9f9T4vn/VeL5/1bi + +f9Y4vn/WuP5/1zj+f9e4/n/YOT5/2Hk+f9j5Pn/Y+T5/0ng+P8q2/f/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/Pt74/3zp+v9+6fr/fun6/37p+v9+6fr/fun6/zuvxv8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wCGov8A0On/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8C0uv/QN3w/5js9v+r5+//WrnM/wyHo/8AgZ7/AIGe/wCJpv8AoLz/ALnT/wDP + 6P8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/KMrh/x6owv8Cg6H/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/G46o/6zX4P+93+f/QKK5/wCDof8Ag6H/AIOh/wCC + oWoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGoSYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ah6b/ANTt/wDU7f8A1O3/ANTt/wDL5P8AmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofUAgKQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKirACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8eq8T/T+n8/0/p/P9I5/v/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8AxuD/AJey/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Fiqb/K7/a/1Hg + +f9W4vn/V+L5/1nj+f9b4/n/XOP5/17j+f9g5Pn/YuT5/2Tk+f9m5fn/YeT5/0De+P8o2vf/J9r3/yfa + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yja9/9w5vr/fen6/37p+v9+6fr/fun6/37p+v9s1+r/AYCb/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/ALLM/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wfU7f9N4PL/o+v0/57d5/9Jqb7/BYOg/wCBnv8AjKj/AKfC/wDC3P8A0+z/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wbT7P9O5/r/R93x/yay + y/8EhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8hkav/r9ji/6bU3v8GhqP/AIOh/wCD + of8Ag6H5AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wCeuv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKhiwAAAAAAAAAAAAAAAAAAAAAAAAAAAIakKgCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ak7D/A4uo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/0DW7P9P6fz/ENjw/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8AvNb/AI2o/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8MlLD/OMzm/1Xi+f9X4vn/WeP5/1vj+f9d4/n/X+T5/2Hk+f9i5Pn/ZOT5/2bl+f9n5fn/W+P5/zbd + +P8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/TuH4/3vo+v996fr/fun6/37p+v9+6fr/fuj6/yKb + tP8Af5v/AH+b/wB/m/8Af5v/AH+b/wCNqP8A0+3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/I9nv/23l8/+r6PD/esnX/zGZsf8BgZ7/AI6r/wCpxP8Aw93/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Etfu/0zn + +v9O5/r/Rt3w/yCrxP8Bg6D/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/ymWrv+23OT/Wq/C/wCD + of8Ag6H/AIOh/wCDoeEAhqETAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAImdDQCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8A0On/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ev/AI2q/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoe8AjqoJAAAAAAAAAAAAAAAAAAAAAACDoqEAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/B8Ld/y2/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/NeL3/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AdTt/wHU7f8C1O3/AtTt/wLT6/8Bscr/AIai/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wCAm/8Xor3/Rdfv/1ji+f9a4/n/XOP5/13j+f9g5Pn/YeT5/2Pk+f9l5fn/Z+X5/2jl + +f9q5fn/U+L5/y7b9/8n2vf/J9r3/yfa9/8n2vf/J9r3/y/b9/966Pr/fOn6/33p+v9+6fr/fun6/37p + +v9Yyt//AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/ALzW/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W + 7v9M4fL/lOXw/5DX4/9RrsH/DYej/wCSrf8ArMf/AMfg/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8e3PL/T+j7/0/o+/9P6Pv/QdXr/xGXsv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/PaC2/7Pa + 4/8SjKj/AIOh/wCDof8Ag6H/AIKitAD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AMzm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC30v8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOiUgAAAAAAAAAAAAAAAACAoh4Ag6H7AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AJe0/y/h9v9O5/v/DZOw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wK91/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU + 7f8C1O3/AtTt/wPV7f8D1e3/BNXt/wTV7f8E1e3/BNXt/wTQ6P8CpsD/AIGd/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGDn/8isMv/UN71/1vj+f9c4/n/XuP5/2Dk+f9h5Pn/ZOT5/2bl + +f9n5fn/aeX5/2vm+v9p5fn/SuD4/yra9/8n2vf/J9r3/zLc9/9g5Pn/eOj6/3vo+v996fr/fun6/37p + +v9+6fr/fOb4/w6Lpv8Af5v/AH+b/wB/m/8Af5v/AH+b/wCXsv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//VOLz/3Tf + 7v900N//QKm//w2MqP8AnLj/ALTP/wDM5v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yzg9f9P6fz/T+j7/0/o+/9O5/r/Lr3U/wOFov8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv9cr8L/dLvM/wCDof8Ag6H/AIOh/wCDof8Ag6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDN5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/AI2q/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoJ8AAAAAAAAAAAAAAAAAg6GSAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wrD3v9O6Pz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ak7D/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/A9Xt/wTV + 7f8E1e3/BNXt/wXV7f8F1e3/BtXt/wbV7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wbJ4v8Cm7X/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wWIpP8wv9n/V+H4/1zj+f9f5Pn/YeT5/2Lk + +f9k5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9m5fn/Qd74/03h+P9y5/r/duf6/3fo+v956Pr/e+j6/33p + +v9+6fr/fun6/37p+v9CvNP/AH+b/wB/m/8Af5v/AH+b/wB/m/8AgJv/AMbg/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDR6v8Xwdn/IbPM/wme + uf8Aob3/ALTO/wDF3/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1e3/OeP4/0/p/P9P6fz/T+n8/0/p/P9C2ez/D5Sv/wCBnv8AgZ7/AIGe/wCB + nv8AgZ7/AYGe/37Az/8kla7/AIOh/wCDof8Ag6H/AIOh+gCCoTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCs + yP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LSAAAAAAAAAAAAgKoMAIOh7wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCTsP8v4fb/T+n8/0/p/P9P6fz/FZ+5/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wDL + 5f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wXV + 7f8G1e3/B9Xt/wfV7f8H1e3/B9Xt/wjV7v8I1e7/Cdbu/wnW7v8J1u7/Cdbu/wnW7v8J1u7/Ctbu/wfA + 2f8CkKv/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wyTrv8/y+X/XOP5/1/k + +f9h5Pn/Y+T5/2Xl+f9n5fn/aOX5/2rl+f9s5vr/beb6/2/m+v9y5/r/c+f6/3Xn+v936Pr/eOj6/3ro + +v986fr/fen6/37p+v9+6fr/Wdzy/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wCkvv8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT6/8AyeL/ANDp/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXW7v9E5vn/T+n8/0/p/P9P6fz/T+n8/03m+f8hrcb/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8MiKT/dbzM/wCDof8Ag6H/AIOh/wCDof8Ag6HgAICcEgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA + pBwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8AyOL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+wCSkgcAAAAAAIKiaACDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Jv9n/Ten8/0/p/P9P6fz/T+n8/0HX7P8BhKL/AIOh/wCDof8Ag6H/AIOh/wCD + of8AzOb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU7f8D1e3/BNXt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV + 7f8I1e7/CNXu/wnW7v8J1u7/Cdbu/wnW7v8K1u7/Ctbu/wvW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/wzW + 7v8M1u7/DNXt/we1z/8BiKP/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/xae + uP9M1ez/YOT5/2Hk+f9j5Pn/ZuX5/2fl+f9p5fn/a+b6/2zm+v9u5vr/cOb6/3Ln+v9z5/r/def6/3fo + +v946Pr/euj6/33p+v9z5/r/Qd32/xWxzv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ag5//AM/o/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/zDA + 1/8Cg6D/AIGe/wCBnv8AgZ7/AIGe/yqXr/84nrb/AIOh/wCDof8Ag6H/AIOh/wCCobAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6MnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIem/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wCHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAACCoNUAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Aj6v/LuD1/0/p/P9P6fz/T+n8/0/p/P9P6fz/D5ey/wCDof8Ag6H/AIOh/wCD + of8AmbX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AdTt/wLU7f8C1O3/A9Xt/wTV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW + 7v8J1u7/Ctbu/wrW7v8L1u7/DNbu/wzW7v8M1u7/DNbu/w3W7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W + 7v8O1u7/Dtbu/w7W7v8P1+7/DtPq/weqw/8Bg57/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AYCd/yatyP9Y3fT/YuT5/2Tk+f9m5fn/Z+X5/2nl+f9r5vr/bOb6/27m+v9x5/r/cuf6/3Tn + +v925/r/d+j6/3fo+v9R4fn/IMHd/waMqP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGu + yP8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vL/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/PNDm/weJpv8AgZ7/AIGe/wCBnv8AgZ//PqG4/wSFov8Ag6H/AIOh/wCDof8Ag6H/AIKgZgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKiPwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCQrf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Ah6b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKfLQCEoD4Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/CLjR/03p+/9P6fz/T+n8/0/p/P9P6fz/T+n8/07o+/8jsMr/AYak/wCE + ov8An7v/AM7m/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU + 7f8C1O3/AtTt/wPV7f8E1e3/BNXt/wXV7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8J1u7/Ctbu/wvW + 7v8M1u7/DNbu/wzW7v8N1u7/Ddbu/w7W7v8O1u7/Dtbu/w7W7v8P1+7/D9fu/xDX7v8Q1+7/ENfu/xDX + 7v8Q1+7/ENfu/xDX7v8R1+7/Edfu/xHX7v8R1+7/D83k/waeuP8AgJv/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/BIah/zS71f9g4vj/ZeX5/2fl+f9o5fn/auX5/2zm+v9t5vr/b+b6/3Hn + +v9z5/r/def6/1/k+f8szun/C5m1/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8BlrD/BNXt/wPV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yrf9f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9D2e7/Co6q/wCBnv8AgZ7/AIGe/wKDof8smLH/AIOh/wCDof8Ag6H/AIOh/wCD + ofQAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACComgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AnLj/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A0uv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoRsAhKCfAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIek/ybb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/POT5/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wLU + 7f8D1e3/BNXt/wTV7f8F1e3/BtXt/wfV7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/wzW + 7v8N1u7/Dtbu/w7W7v8O1u7/D9fu/w/X7v8Q1+7/ENfu/xDX7v8Q1+7/Edfu/xHX7v8S1+7/Etfu/xPX + 7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/E9fu/xPX7v8T1+7/D8Tc/wSSrf8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpCr/0TJ4f9l5fn/Z+X5/2nl+f9r5vr/bOb6/27m + +v9w5vr/Z+X5/zvY8v8QpcL/AYCd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGE + oP8Ersj/B9Pr/wbV7f8E1e3/BNXt/wPV7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/O+T4/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fe8v8Nkq7/AIGe/wCBnv8AgZ7/FI2o/wqIpf8Ag6H/AIOh/wCD + of8Ag6H/AIOhywCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqGNAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AKnF/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AMTe/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofQAgKQOAIOh8wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKlwf9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ + 8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV + 7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8J1u7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W + 7v8O1u7/D9fu/xDX7v8Q1+7/ENfu/xHX7v8R1+7/Etfu/xPX7v8T1+7/E9fu/xPX7v8U1+7/FNfu/xTX + 7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8V2O7/Fdju/xXY7v8W2O//Ftjv/xbY7/8W1+//DrnS/wKJ + pf8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/G523/1TV7P9n5fn/aeX5/2vm + +v9q5fn/St/3/xmz0P8ChaD/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wSj + vv8Iz+f/Cdbu/wjV7v8H1e3/B9Xt/wbV7f8F1e3/BNXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW + 7/9H5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/w+VsP8AgZ7/AIGe/wCCn/8Oiqb/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhuQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC10P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCzzv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HDAIKgXgCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8d0en/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z7l + +f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wTV + 7f8G1e3/B9Xt/wfV7f8I1e7/Cdbu/wnW7v8K1u7/C9bu/wzW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX + 7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xTX7v8V2O7/Fdju/xXY7v8V2O7/Ftjv/xbY + 7/8X2O//F9jv/xfY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY7/8Y2O//GNjv/xjY + 7/8X1Oz/DK3H/wGDn/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8BgJz/Kq3F/2De + 8/9W4vn/JsPe/waOqv8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/BJey/wvI + 4f8M1u7/DNbu/wvW7v8K1u7/Cdbu/wnW7v8I1e7/B9Xt/wfV7f8G1e3/BNXt/wTV7f8D1e3/AtTt/wHU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/E9nw/03o/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/DJOv/wCBnv8AgZ7/AYOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCCoy8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAJmZBQCDoPMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Axd7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOijgCE + obwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aka//PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8b3PL/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wPV7f8E1e3/BdXt/wbV + 7f8H1e3/CNXu/wnW7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8O1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX + 7v8S1+7/E9fu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8W2O//F9jv/xfY7/8Y2O//GNjv/xjY + 7/8Y2O//Gdjv/xnY7/8Z2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY7/8a2O//Gtjv/xrY + 7/8a2O//Gtjv/xrY7/8Xz+f/CqG7/wCAnP8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8CgJz/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Ci6f/C7vT/xDX + 7v8P1+7/Dtbu/w7W7v8N1u7/DNbu/wzW7v8L1u7/Ctbu/wnW7v8J1u7/B9Xt/wfV7f8G1e3/BdXt/wTV + 7f8D1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8m3vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jh9P8Ika3/AIGe/wCB + oP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HLAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAgqD/AIGf/wCBn/8AgqD/AIOh/wCDof8AiKX/ANTs/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJi0/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D7rU/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9G5/r/AdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wTV7f8F1e3/B9Xt/wfV + 7f8I1e7/Cdbu/wrW7v8L1u7/DNbu/wzW7v8N1u7/Dtbu/w7W7v8P1+7/ENfu/xDX7v8R1+7/Etfu/xPX + 7v8T1+7/FNfu/xXY7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xnY7/8a2O//Gtjv/xrY + 7/8a2O//G9nv/xvZ7/8b2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ + 7/8c2e//HNnv/xzZ7/8c2e//HNnv/xzZ7/8Xx9//B5ex/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wGFoP8Lr8n/E9Xs/xPX + 7v8S1+7/Edfu/xDX7v8Q1+7/D9fu/w7W7v8O1u7/Ddbu/wzW7v8M1u7/Ctbu/wnW7v8J1u7/CNXu/wfV + 7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AdXt/z3k+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R9/0/wKN + qf8AgZ7/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGFAIKg/wCBn/8AgZ7/AICe/wCAnv8AgZ//AJu4/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCNqv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi/yzd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Jt71/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BNXt/wXV7f8H1e3/B9Xt/wjV + 7v8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8O1u7/D9fu/xDX7v8Q1+7/Edfu/xLX7v8T1+7/E9fu/xTX + 7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8b2e//HNnv/xzZ + 7/8c2e//HNnv/x3Z7/8d2e//Htnv/x7Z7/8e2e//Htnv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z + 7/8f2e//H9nv/x/Z7/8f2e//H9nv/x/Z7/8f2e//H9nv/x7Z7/8WwNj/BY6o/wB/m/8Af5v/AH+b/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AICd/wqkvv8V0ej/Ftjv/xXY + 7v8V2O7/FNfu/xPX7v8T1+7/Etfu/xHX7v8Q1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8M1u7/C9bu/wnW + 7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1+//S+j7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P801uz/AIWi/wCCn/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H0AICfIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOh4ACCoP8AgZ//AIGe/wCAnv8AgJ3/AH+d/wCs + xv8A0Oj/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AhaP/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGbtv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/wbV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/AtTt/wTV7f8E1e3/BdXt/wfV7f8H1e3/Cdbu/wnW + 7v8K1u7/DNbu/wzW7v8N1u7/Dtbu/w/X7v8Q1+7/ENfu/xHX7v8S1+7/E9fu/xPX7v8U1+7/Fdju/xXY + 7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Hdnv/x7Z + 7/8f2e//H9nv/x/Z7/8f2e//H9nv/yDZ7/8g2e//INnv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa + 7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8h2u//Idrv/yHa7/8g2e//INnv/x/Y7f8TtMz/Aoai/wB/ + m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8Af5v/B5Ww/xXH4P8Z2O//Gdjv/xjY + 7/8Y2O//F9jv/xbY7/8V2O7/Fdju/xTX7v8T1+7/E9fu/xLX7v8Q1+7/ENfu/w/X7v8O1u7/Dtbu/wzW + 7v8M1u7/C9bu/wrW7v8J1u7/CNXu/wfV7f8G1e3/BdXt/wTV7f8D1e3/AtTt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xfJ4f8Agp//AIOg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/AIOh/wCCoP8AgZ//AICe/wB/ + nf8AwNj/AM3l/wDM5P8Azeb/ANLq/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJOv/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ywtv/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zLh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Cdbu/wrW + 7v8M1u7/DNbu/w3W7v8O1u7/D9fu/xDX7v8R1+7/Etfu/xPX7v8T1+7/FNfu/xXY7v8V2O7/Ftjv/xjY + 7/8Y2O//GNjv/xnY7/8a2O//Gtjv/xvZ7/8c2e//HNnv/x3Z7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ + 7/8g2e//Idrv/yHa7/8h2u//Idrv/yLa7/8i2u//Itrv/yPa7/8j2u//I9rv/yPa7/8j2u//JNrw/yTa + 8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/I9rv/yPa7/8j2u//I9rv/yLa7/8i2u//Itrv/x/U + 6v8PqMH/AYGd/wB/m/8Af5v/AH+b/wB/m/8Af5v/AH+b/wB/m/8EjKf/FLzU/xzZ7/8c2e//G9nv/xvZ + 7/8a2O//Gtjv/xnY7/8Y2O//GNjv/xfY7/8W2O//Fdju/xXY7v8T1+7/E9fu/xLX7v8R1+7/ENfu/w/X + 7v8O1u7/Dtbu/w3W7v8M1u7/C9bu/wnW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wTV7f8C1O3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/OOP4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9I5/r/A7bQ/wCDoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKhOQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ai6n/ANHp/wDO5v8AzOT/AMvj/wDJ4f8AyuL/AM/n/wDT7P8A1O3/ANTt/wC10P8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/Nd/1/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8U2fH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8J1u7/Ctbu/wzW + 7v8M1u7/Dtbu/w7W7v8P1+7/ENfu/xHX7v8S1+7/E9fu/xTX7v8V2O7/Fdju/xbY7/8X2O//GNjv/xjY + 7/8a2O//Gtjv/xvZ7/8c2e//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa + 7/8i2u//Itrv/yPa7/8j2u//JNrw/yTa8P8k2vD/JNrw/yTa8P8l2vD/Jdrw/yXa8P8m2vD/Jtrw/yba + 8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8l2vD/Jdrw/yXa8P8l2vD/JNrw/yTa + 8P8k2vD/JNrw/x7N4/8LnLb/AH+b/wB/m/8Af5v/AH+b/wKFoP8SsMn/H9ft/x/Z7/8f2e//Htnv/x3Z + 7/8c2e//HNnv/xzZ7/8b2e//Gtjv/xrY7/8Z2O//GNjv/xjY7/8W2O//Fdju/xXY7v8U1+7/E9fu/xLX + 7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/C9bu/wnW7v8J1u7/B9Xt/wfV7f8F1e3/BNXt/wPV + 7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX7/9N6fv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yrf9f8Anrn/AIKg/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ocUA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgwAg6HvAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AKjE/wDU7f8A1O3/ANLr/wDQ6P8AzOT/AMjg/wDI3/8AyN//AMzk/wDS6v8A1O3/AKzI/wCE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A5q3/0vo+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9G5/r/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wLU7f8E1e3/BdXt/wbV7f8H1e3/CNXu/wnW7v8K1u7/DNbu/wzW + 7v8O1u7/Dtbu/w/X7v8Q1+7/Edfu/xPX7v8T1+7/FNfu/xXY7v8W2O//F9jv/xjY7/8Y2O//Gdjv/xrY + 7/8a2O//HNnv/xzZ7/8d2e//Htnv/x/Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yLa7/8i2u//I9rv/yTa + 8P8k2vD/JNrw/yXa8P8l2vD/Jtrw/yba8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yfb8P8o2/D/KNvw/yjb + 8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yjb8P8o2/D/KNvw/yfb8P8n2/D/J9vw/yba + 8P8m2vD/Jtrw/yba8P8m2vD/Jdrw/xzD2/8Hkav/AICc/w6iu/8g0+j/Itrv/yLa7/8h2u//Idrv/yDZ + 7/8f2e//H9nv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8Z2O//GNjv/xjY7/8X2O//Ftjv/xXY + 7v8U1+7/E9fu/xPX7v8R1+7/ENfu/xDX7v8O1u7/Dtbu/wzW7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wbV + 7f8F1e3/BNXt/wLU7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/KN/0/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/Bdbu/wCMqf8Ag6D/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOiUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFqAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wDF3v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8Az+f/AMvj/wDH3v8Axdz/AMjg/wDP + 5/8AxN//AJ67/wCFpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISj/xjP5/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Kd/0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BtXt/wfV7f8I1e7/Cdbu/wrW7v8M1u7/DNbu/w7W + 7v8O1u7/D9fu/xDX7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xnY7/8a2O//Gtjv/xvZ + 7/8c2e//HNnv/x7Z7/8f2e//H9nv/yDZ7/8h2u//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa + 8P8m2vD/Jtrw/yba8P8n2/D/J9vw/yjb8P8o2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb8P8p2/D/Ktvw/yrb + 8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8q2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb + 8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8n2/D/Jtnv/yHO5f8m2vD/Jdrw/yTa8P8k2vD/JNrw/yPa + 7/8i2u//Idrv/yHa7/8g2e//INnv/x/Z7/8e2e//Hdnv/xzZ7/8c2e//G9nv/xrY7/8a2O//Gdjv/xjY + 7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xPX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW7v8L1u7/Ctbu/wnW + 7v8I1e7/B9Xt/wXV7f8E1e3/A9Xt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU + 7v9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8AwNv/AIOg/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoccAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIKg1QCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCRr/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANDo/wDL + 4/8Axdz/AMXc/wDM5P8A0On/AMHb/wCyzv8ApcH/AIOh/wCDof8Ag6H/AImn/wC10P8y4fb/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/wzX8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8C1O3/A9Xt/wTV7f8F1e3/B9Xt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W + 7v8P1+7/ENfu/xHX7v8T1+7/E9fu/xTX7v8V2O7/Ftjv/xjY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/xzZ + 7/8d2e//Htnv/x/Z7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8k2vD/JNrw/yXa8P8m2vD/Jtrw/yfb + 8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/ynb8P8q2/D/Ktvw/yvb8P8r2/D/K9vw/yvb8P8r2/D/LNvw/yzb + 8P8s2/D/LNvw/yzb8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/LNvw/yzb8P8s2/D/LNvw/yvb + 8P8r2/D/K9vw/yvb8P8r2/D/Ktvw/yrb8P8p2/D/Kdvw/ynb8P8o2/D/KNvw/yfb8P8m2vD/Jtrw/yba + 8P8l2vD/JNrw/yTa8P8j2u//Itrv/yHa7/8h2u//INnv/x/Z7/8f2e//Htnv/x3Z7/8c2e//HNnv/xrY + 7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8R1+7/ENfu/w/X7v8O1u7/Ddbu/wzW + 7v8L1u7/Cdbu/wnW7v8H1e3/BtXt/wXV7f8E1e3/AtTt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J6Pv/A9Xu/wCeuv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AISfOAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKg/wCDof8Ar8v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDQ6P8AyuL/AMTb/wDI3/8A0Oj/ANTt/wDU7f8AyOL/AMTe/wDQ6f8B1e3/Suf7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Dl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wHU7f8C1O3/A9Xt/wTV7f8G1e3/B9Xt/wjV7v8J1u7/C9bu/wzW7v8N1u7/Dtbu/w/X + 7v8Q1+7/Edfu/xLX7v8T1+7/FNfu/xXY7v8W2O//GNjv/xjY7/8Z2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z + 7/8f2e//H9nv/yDZ7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/Jtrw/yba8P8n2/D/KNvw/ynb + 8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb8P8s2/D/LNvw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ltzw/y7c + 8P8u3PD/Ltzw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y/c8P8v3PD/L9zw/y7c8P8u3PD/Ltzw/y7c + 8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/ynb + 8P8o2/D/J9vw/yba8P8m2vD/Jdrw/yTa8P8k2vD/I9rv/yLa7/8h2u//Idrv/yDZ7/8f2e//H9nv/x3Z + 7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Ftjv/xXY7v8U1+7/E9fu/xLX7v8Q1+7/ENfu/w7W + 7v8O1u7/DNbu/wzW7v8K1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f9B5fr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yLd9P8AyuP/AISi/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAACDobsAg6H/AIOh/wCD + of8Ag6H/AIOh/wCCoP8AhqP/AM3m/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0ur/AMri/wDI4P8AzeX/ANTt/wDU7f8A1O3/Etnw/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8n3vX/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wLU7f8C1O3/BNXt/wXV7f8H1e3/B9Xt/wnW7v8K1u7/DNbu/wzW7v8O1u7/Dtbu/xDX + 7v8R1+7/Etfu/xPX7v8U1+7/Fdju/xbY7/8Y2O//GNjv/xrY7/8a2O//G9nv/xzZ7/8d2e//Htnv/x/Z + 7/8g2e//Idrv/yHa7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/KNvw/ynb8P8p2/D/Kdvw/yrb + 8P8r2/D/K9vw/yzb8P8s2/D/Ldzw/y3c8P8t3PD/Ltzw/y7c8P8v3PD/L9zw/zDc8P8w3PD/MNzw/zDc + 8P8w3PD/MNzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/Mdzw/zHc8P8x3PD/MNzw/zDc + 8P8w3PD/MNzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c8P8t3PD/Ldzw/y3c8P8s2/D/K9vw/yvb + 8P8r2/D/Ktvw/ynb8P8p2/D/KNvw/yfb8P8m2vD/Jtrw/yXa8P8k2vD/JNrw/yPa7/8i2u//Idrv/yDZ + 7/8f2e//H9nv/x7Z7/8c2e//HNnv/xvZ7/8a2O//Gdjv/xjY7/8X2O//Fdju/xXY7v8T1+7/E9fu/xHX + 7v8Q1+7/D9fu/w7W7v8N1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/AdTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5Pn/ANTt/wCe + u/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H4AICkDgAAAAAAAAAAAAAAAACCojcAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqD/AKW//wDS6v8A0en/ANHp/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDN5v8A0Oj/ANPs/yfe + 9f9P6fz/T+n8/zvP5v8ms83/JbPM/yq50f8uvdX/CbfR/wC51P8Avdj/AMDb/wDE3/8AyeL/AM3n/wDQ + 6f8A1O3/AdTt/wLU7f8D1e3/BNXt/wbV7f8H1e3/CNXu/wnW7v8L1u7/DNbu/w3W7v8O1u7/D9fu/xDX + 7v8S1+7/E9fu/xTX7v8V2O7/Ftjv/xfY7/8Y2O//Gdjv/xrY7/8b2e//HNnv/x3Z7/8e2e//H9nv/yDZ + 7/8h2u//Itrv/yPa7/8k2vD/JNrw/yXa8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/yrb8P8r2/D/K9vw/yvb + 8P8s2/D/Ldzw/y3c8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zDc8P8w3PD/Mdzw/zHc8P8y3PH/Mtzx/zLc + 8f8y3PH/Mtzx/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd8f8z3fH/M93x/zPd + 8f8y3PH/Mtzx/zLc8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/MNzw/y/c8P8v3PD/Ltzw/y3c + 8P8t3PD/Ldzw/yzb8P8r2/D/K9vw/yrb8P8p2/D/Kdvw/yjb8P8n2/D/Jtrw/yba8P8l2vD/JNrw/yPa + 7/8i2u//Idrv/yHa7/8f2e//H9nv/x7Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xjY7/8W2O//Fdju/xTX + 7v8T1+7/Etfu/xHX7v8Q1+7/Dtbu/w7W7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8F1e3/BNXt/wLU + 7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f9E5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wvX + 8P8AxN7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAAAAAAg6GzAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIKg/wDG4f8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDQ6P8A0ur/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f895Pn/T+n8/yKvyf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCIpf8Ai6j/AY6s/wGUsP8Cl7T/A5q3/wOeuv8Fo77/BabD/weqxf8IsMr/CbPN/wq2 + 0f8Nu9X/Dr/Z/xDE3P8SyOD/FMzk/xbR6P8Y1Ov/Gtjv/xvZ7/8c2e//Hdnv/x/Z7/8f2e//INnv/yHa + 7/8i2u//I9rv/yTa8P8l2vD/Jtrw/yba8P8n2/D/Kdvw/ynb8P8q2/D/K9vw/yvb8P8s2/D/Ldzw/y3c + 8P8u3PD/Ltzw/y/c8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8y3PH/M93x/zPd8f803fH/NN3x/zXd + 8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd8f813fH/Nd3x/zXd + 8f813fH/Nd3x/zXd8f813fH/Nd3x/zTd8f803fH/M93x/zPd8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc + 8P8w3PD/L9zw/y/c8P8u3PD/Ldzw/y3c8P8s2/D/K9vw/yvb8P8q2/D/Kdvw/ynb8P8o2/D/Jtrw/yba + 8P8l2vD/JNrw/yTa8P8i2u//Idrv/yHa7/8f2e//H9nv/x3Z7/8c2e//HNnv/xrY7/8a2O//GNjv/xfY + 7/8W2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8I1e7/B9Xt/wXV + 7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8m3vX/ANTt/wCXtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GoAAAAAAAAAAAAgqExAIOh/gCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCbuP8A0+z/ANLq/wDR6f8A0Oj/AM/n/wDP5/8Azub/AM3m/wDN + 5v8Az+f/ANLr/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8E1e7/Tuj8/0DW6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8CiKX/A4uo/wSPrP8GlLH/CJi0/wqc + t/8MoLv/DqXA/xCoxP8SrMb/FbLL/xe1zv8ZudL/H8Xd/yva7/8r2/D/LNvw/y3c8P8t3PD/Ltzw/y/c + 8P8w3PD/MNzw/zHc8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zXd8f813fH/Nt3x/zbd + 8f833fH/N93x/zfd8f833fH/N93x/zfd8f833fH/ON3x/zjd8f843fH/ON3x/zjd8f843fH/ON3x/zfd + 8f833fH/N93x/zfd8f833fH/N93x/zfd8f823fH/Nt3x/zXd8f813fH/Nd3x/zXd8f803fH/M93x/zPd + 8f8y3PH/Mtzx/zHc8P8x3PD/MNzw/zDc8P8v3PD/Ltzw/y3c8P8t3PD/LNvw/yvb8P8r2/D/Kdvw/ynb + 8P8o2/D/J9vw/yba8P8l2vD/JNrw/yTa8P8i2u//Idrv/yDZ7/8f2e//H9nv/x3Z7/8c2e//G9nv/xrY + 7/8Z2O//GNjv/xfY7/8V2O7/FNfu/xPX7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8L1u7/Cdbu/wjV + 7v8H1e3/BdXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v9M6Pv/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/QOX6/wDU7f8AtdD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7wAAAAAAAAAAAIOgrwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Aw93/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN + 5v8AzeX/AMzk/wDL4/8AzeX/ANDo/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/GNrx/0/p/P8wwdj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fjqr/Isfd/y7c8P8v3PD/MNzw/zDc + 8P8x3PD/Mtzx/zLc8f8z3fH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f833fH/ON3x/zjd + 8f843fH/Od7x/zne8f853vH/Od7x/zne8f853vH/Ot7x/zre8f863vH/Ot7x/zre8f863vH/Ot7x/zre + 8f863vH/Od7x/zne8f853vH/Od7x/zne8f853vH/ON3x/zjd8f843fH/N93x/zfd8f833fH/Nt3x/zXd + 8f813fH/Nd3x/zTd8f8z3fH/M93x/zLc8f8y3PH/Mdzw/zDc8P8w3PD/L9zw/y7c8P8t3PD/LNvw/yvb + 8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ7/8f2e//Htnv/x3Z + 7/8c2e//Gtjv/xrY7/8Y2O//F9jv/xbY7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8O1u7/DNbu/wvW + 7v8J1u7/CNXu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/NeL3/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8H1u7/AM7m/wCDov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AAAAAAIKiPwCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ambb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A0uv/ANHp/wDO5v8AzOT/AMri/wDK4v8AyeH/AM3l/wDR6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yzg9f9P6fz/PdLo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSLqP8v2+7/Mdzw/zLc + 8f8y3PH/M93x/zTd8f813fH/Nd3x/zbd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Od7x/zre + 8f863vH/O97x/zve8f873vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze8f883vH/PN7x/zze + 8f883vH/PN7x/zze8f883vH/PN7x/zze8f873vH/O97x/zve8f863vH/Ot7x/zne8f853vH/Od7x/zjd + 8f843fH/N93x/zfd8f823fH/Nd3x/zXd8f813fH/NN3x/zPd8f8y3PH/Mtzx/zHc8P8w3PD/L9zw/y7c + 8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa7/8i2u//Idrv/yDZ + 7/8f2e//Hdnv/xzZ7/8b2e//Gtjv/xnY7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/D9fu/w7W + 7v8M1u7/C9bu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xrb8v9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/GNrx/wDU7f8AlrP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICAAgCD + oc4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AMDb/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0en/AM7m/wDL4/8AyOD/AMnh/wDN5v8A0uv/ANTt/wDU + 7f8A1O3/ANTt/wDU7f895Pn/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/KMrh/zPd + 8f803fH/Nd3x/zXd8f823fH/N93x/zfd8f843fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/PN7x/zze + 8f883vH/PN7x/z3e8f893vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e + 8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z7e8f8+3vH/Pt7x/z3e8f893vH/PN7x/zze8f883vH/PN7x/zve + 8f863vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/N93x/zbd8f813fH/Nd3x/zTd8f8y3PH/Mtzx/zHc + 8P8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/J9vw/yba8P8l2vD/JNrw/yPa + 7/8h2u//INnv/x/Z7/8e2e//Hdnv/xzZ7/8a2O//Gtjv/xjY7/8X2O//Fdju/xXY7v8T1+7/Etfu/xDX + 7v8P1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yfe9f8A1O3/AKfD/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC + oWIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDS6v8Azub/AMvj/wDM + 5P8A0Oj/ANTt/wDU7f8C1O7/Ten8/0/p/P9P6fz/Tef5/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y/T + 6f813fH/Nt3x/zfd8f833fH/ON3x/zne8f853vH/Ot7x/zve8f873vH/PN7x/zze8f893vH/Pd7x/z7e + 8f8+3vH/Pt7x/z/f8f8/3/H/P9/x/0Df8v9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf + 8v9B3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Hf8v9A3/L/QN/y/0Df8v8/3/H/P9/x/z7e8f8+3vH/Pt7x/z7e + 8f893vH/PN7x/zze8f883vH/O97x/zre8f853vH/Od7x/zjd8f833fH/N93x/zbd8f813fH/Nd3x/zTd + 8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yzb8P8r2/D/Ktvw/ynb8P8o2/D/Jtrw/yba + 8P8k2vD/I9rv/yLa7/8h2u//INnv/x/Z7/8d2e//HNnv/xvZ7/8a2O//GNjv/xjY7/8W2O//Fdju/xPX + 7v8S1+7/ENfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8C1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k + +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P834/j/ANTt/wC10f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HnAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wDE3v8A1O3/ANPs/wDS6/8A0+z/ANPs/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANLr/wDR6f8A0+z/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/M8Xb/wOHpf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP82yd//O8/m/zjM4v81x97/MMHY/y291P8quND/JbPM/yGu + x/8eqsT/GqW//xafuv8Tm7b/D5ey/wuRrv8HjKn/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKi + vP833fH/N93x/zjd8f853vH/Od7x/zre8f873vH/PN7x/zze8f893vH/Pt7x/z7e8f8+3vH/P9/x/z/f + 8f9A3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Lf8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf + 8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Pf8v9D3/L/Q9/y/0Lf8v9C3/L/Qd/y/0Hf8v9B3/L/Qd/y/0Df + 8v9A3/L/P9/x/z7e8f8+3vH/Pt7x/z3e8f883vH/PN7x/zve8f863vH/Od7x/zne8f843fH/N93x/zfd + 8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y/c8P8t3PD/Ldzw/yvb8P8r2/D/Kdvw/ynb + 8P8n2/D/Jtrw/yXa8P8k2vD/I9rv/yHa7/8g2e//H9nv/x7Z7/8c2e//G9nv/xrY7/8Z2O//GNjv/xbY + 7/8V2O7/E9fu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW7v8J1u7/B9Xt/wbV7f8E1e3/A9Xt/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8t4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+f6/wDU7f8AwNr/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wChvf8A1O3/ANTt/wDT7P8A0uv/ANLr/wDS + 6v8A0en/ANHp/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B2O3/AZCt/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Boqo/znM4/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/0fe8v9E2u//P9Tq/zvP + 5v84zOL/Ncfe/zDB2P8tvdT/KrjQ/yWzzP8hrsf/HqrE/xqlv/8Wn7r/Epu2/w+Xsv8Ag6H/CJCt/xuw + yf812e7/Od7x/zne8f863vH/O97x/zze8f883vH/Pd7x/z7e8f8+3vH/P9/x/0Df8v9A3/L/Qd/y/0Hf + 8v9C3/L/Qt/y/0Pf8v9D3/L/Q9/y/0Pf8v9E3/L/RN/y/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg + 8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9F4PL/ReDy/0Xg8v9E3/L/RN/y/0Tf8v9D3/L/Q9/y/0Pf + 8v9C3/L/Qt/y/0Hf8v9B3/L/Qd/y/0Df8v8/3/H/Pt7x/z7e8f893vH/PN7x/zze8f873vH/Ot7x/zne + 8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zLc8f8w3PD/MNzw/y7c8P8t3PD/LNvw/yvb + 8P8q2/D/Kdvw/yjb8P8m2vD/Jtrw/yTa8P8j2u//Itrv/yHa7/8f2e//Htnv/x3Z7/8c2e//Gtjv/xnY + 7/8Y2O//Ftjv/xXY7v8U1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W7v8M1u7/Ctbu/wnW7v8H1e3/BtXt/wTV + 7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/H9zz/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8G1e7/AMHb/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCHpP8AzOX/ANTt/wDU7f8A0+z/ANLr/wDS + 6/8A0ur/ANHp/wDQ6P8A0Oj/AM/n/wDP5/8A0Oj/ANLq/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8w4fb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sej7/wDQ + 6f8Anrr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhKL/J7XO/07o+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PeDz/zjd + 8f853vH/Od7x/zve8f883vH/PN7x/z3e8f8+3vH/Pt7x/z/f8f9A3/L/Qd/y/0Hf8v9C3/L/Q9/y/0Pf + 8v9D3/L/RN/y/0Tf8v9F4PL/ReDy/0Xg8v9G4PL/RuDy/0bg8v9H4PL/R+Dy/0fg8v9I4PL/SODy/0jg + 8v9I4PL/SODy/0jg8v9I4PL/SODy/0jg8v9I4PL/R+Dy/0fg8v9H4PL/R+Dy/0bg8v9G4PL/ReDy/0Xg + 8v9F4PL/RN/y/0Tf8v9D3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9A3/L/P9/x/z7e8f8+3vH/Pd7x/zze + 8f883vH/O97x/zne8f853vH/ON3x/zfd8f823fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y7c + 8P8t3PD/LNvw/yvb8P8p2/D/Kdvw/yfb8P8m2vD/KNvw/yrb8P8r2/D/J9vv/yPa7/8f2e//Hdnv/xzZ + 7/8a2O//Gdjv/xjY7/8X2O//Fdju/xTX7v8T1+7/Edfu/xDX7v8O1u7/Ddbu/wzW7v8K1u7/Cdbu/wfV + 7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DNjw/wDG3/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ArMj/ANTt/wDU7f8A1O3/ANPs/wDS + 6/8A0ur/ANLq/wDR6f8A0Oj/ANDo/wDP5/8Azub/AM3m/wDN5v8AzeX/AM3m/wDP5/8A0ur/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/PuX5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zvj + +f8A1O3/ANTt/wCxzP8AhKP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Wn7r/SOH0/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun7/zre + 8f863vH/O97x/zze8f883vH/Pt7x/z7e8f8/3/H/QN/y/0Hf8v9B3/L/Qt/y/0Pf8v9D3/L/RN/y/0Xg + 8v9F4PL/ReDy/0bg8v9H4PL/R+Dy/0jg8v9I4PL/SODy/0jg8v9J4PL/SeDy/0ng8v9K4PL/SuDy/0rg + 8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9K4PL/SuDy/0rg8v9J4PL/SeDy/0ng8v9I4PL/SODy/0jg + 8v9I4PL/R+Dy/0fg8v9G4PL/ReDy/0Xg8v9F4PL/RN/y/0Pf8v9D3/L/Qt/y/0Hf8v9B3/L/QN/y/z/f + 8f8+3vH/Pt7x/zze8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8z3fH/Mtzx/zHc + 8P8w3PD/Nd3w/0Lf8f9L4PL/VOLz/13k9P9k5fT/aOb0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuT0/1zj + 8/9T4vP/SODy/zze8f8w3PH/Htnv/xfY7/8V2O7/FNfu/xPX7v8R1+7/ENfu/w7W7v8N1u7/DNbu/wrW + 7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xHY8P8Axt//AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/ANLr/wDU7f8A1O3/ANTt/wDU + 7f8A0+z/ANPs/wDS6/8A0ur/ANHp/wDQ6P8Az+f/AM7m/wDN5v8Azeb/AM3l/wDM5P8Ay+P/AMvj/wDL + 4/8AzeX/AM/n/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8u4PX/ANTt/wDU7f8A1O3/AMLc/wCLqf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmP + rP8+0+j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0nm + +f873vH/PN7x/z3e8f8+3vH/Pt7x/0Df8v9B3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Tf8v9F4PL/ReDy/0bg + 8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9K4PL/SuDy/0rg8v9K4PL/S+Hy/0vh8v9M4fL/TOHy/0zh + 8v9M4fL/TOHy/0zh8v9N4fL/TeHy/0zh8v9M4fL/TOHy/0zh8v9M4fL/TOHy/0vh8v9L4fL/SuDy/0rg + 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Lf + 8v9B3/L/Qd/y/z/f8f8+3vH/Pt7x/z3e8f883vH/O97x/zre8f853vH/ON3x/zfd8f823fH/Nt3x/0Pg + 8v9V4vP/ZeX0/27m9P9t5vT/bOb0/2zm9P9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9l5fT/ZeX0/2Pl + 9P9i5PT/YuT0/2Dk9P9g5PT/X+T0/17k9P9R4vP/P9/y/ybb7/8V1+7/E9fu/xHX7v8Q1+7/Dtbu/w3W + 7v8M1u7/Cdbu/wjV7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/D9jv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8W2vH/AMLc/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/AMXe/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDN + 5v8AzOT/AMri/wDI4P8AyOD/AMnh/wDN5f8A0Oj/ANPs/wfW7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wDU7f8Azub/AJm1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AoWj/y691f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9F4/f/Pd7x/z7e8f8/3/H/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9E3/L/ReDy/0Xg8v9G4PL/R+Dy/0jg + 8v9I4PL/SeDy/0rg8v9K4PL/SuDy/0vh8v9M4fL/TOHy/03h8v9N4fL/TeHy/03h8v9N4fL/TuHz/07h + 8/9O4fP/TuHz/0/h8/9P4fP/T+Hz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TuHz/07h8/9N4fL/TeHy/03h + 8v9N4fL/TeHy/0zh8v9L4fL/S+Hy/0rg8v9K4PL/SeDy/0ng8v9I4PL/SODy/0fg8v9G4PL/ReDy/0Xg + 8v9E3/L/Q9/y/0Lf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/z3e8f883vH/O97x/zne8f9C3/L/V+Lz/2zm + 9f9x5/X/cef1/3Dn9f9v5vX/bub0/27m9P9s5vT/bOb0/2vm9P9q5vT/aeb0/2nm9P9n5vT/Z+X0/2Xl + 9P9l5fT/Y+X0/2Lk9P9i5PT/YeT0/2Dk9P9f5PT/XuT0/13k9P9c5PT/U+Lz/zrd8f8d2e//Edfu/xDX + 7v8O1u7/DNbu/wvW7v8J1u7/CNXu/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wvX7/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/G9zy/wC6 + 1P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AK7J/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANLr/wDS6v8A0Oj/AM7m/wDN5f8Q0+n/TuX4/0/o+/9P6fz/T+n8/0/p + /P9P6fz/T+n8/xja8f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7P8Aq8b/AISi/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/G6XA/0vk+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/QuHz/z/f8f9A3/L/Qd/y/0Lf8v9D3/L/Q9/y/0Xg8v9F4PL/RuDy/0fg8v9I4PL/SODy/0ng + 8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h8v9N4fL/TuHz/07h8/9P4fP/T+Hz/0/h8/9P4fP/UOHz/1Dh + 8/9Q4fP/UOHz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UeLz/1Hi8/9R4vP/UOHz/1Dh8/9Q4fP/T+Hz/0/h + 8/9P4fP/T+Hz/0/h8/9O4fP/TeHy/03h8v9N4fL/TOHy/0vh8v9L4fL/SuDy/0rg8v9J4PL/SODy/0jg + 8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/Qd/y/0Df8v8+3vH/Pt7x/0rg8v9l5fT/dej1/3To + 9f9z5/X/c+f1/3Ln9f9x5/X/cef1/2/m9f9u5vT/bub0/23m9P9s5vT/a+b0/2rm9P9p5vT/aeb0/2fm + 9P9n5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Lk9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk9P9b5PP/WuPz/0Tf + 8v8h2u//D9fu/w7W7v8M1u7/C9bu/wnW7v8I1e7/BtXt/wTV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8N1/D/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xja + 8v8Asc3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8P2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wO91v8Biaf/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Mk7D/Qtnt/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Tun8/0Df8v9B3/L/Qt/y/0Pf8v9E3/L/ReDy/0Xg8v9H4PL/SODy/0jg8v9J4PL/SuDy/0rg + 8v9L4fL/TOHy/03h8v9N4fL/TuHz/0/h8/9P4fP/T+Hz/1Dh8/9Q4fP/UeLz/1Li8/9S4vP/UuLz/1Li + 8/9S4vP/UuLz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9T4vP/U+Lz/1Pi8/9S4vP/UuLz/1Li + 8/9S4vP/UuLz/1Hi8/9R4vP/UOHz/0/h8/9P4fP/T+Hz/07h8/9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg + 8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9B3/L/UeLz/2vm9P946PX/d+j1/3bo + 9f916PX/dej1/3To9f9z5/X/c+f1/3Hn9f9x5/X/cOf1/2/m9f9u5vT/bub0/2zm9P9s5vT/aub0/2nm + 9P9p5vT/Z+b0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuT0/2Hk9P9g5PT/X+T0/17k9P9d5PT/W+T0/1vk + 8/9Z4/P/WePz/0fg8v8j2u//Dtbu/wzW7v8K1u7/Cdbu/wfV7f8G1e3/BNXt/wPV7f8B1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Etnw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8T2fD/AKjE/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AImo/wDK5P8A1O3/ANPs/wDS + 6v8A0en/ANHp/wDP5/8Az+f/AM/n/wDO5v8Azub/AM7m/wDO5v8Azub/AM/n/wDP5/8A0Oj/ANHp/wDS + 6v8A0uv/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yLe9P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/BtXu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8E1e3/Es3l/wSU + sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSIpf8zxdz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0zn+v9C3/L/Q9/y/0Tf8v9F4PL/RuDy/0fg8v9I4PL/SeDy/0rg8v9K4PL/S+Hy/0zh + 8v9N4fL/TeHy/07h8/9P4fP/T+Hz/1Dh8/9R4vP/UuLz/1Li8/9S4vP/U+Lz/1Pi8/9U4vP/VOLz/1Ti + 8/9U4vP/VOLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VeLz/1Xi8/9V4vP/VOLz/1Ti + 8/9U4vP/VOLz/1Ti8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Hi8/9Q4fP/T+Hz/0/h8/9P4fP/TuHz/03h + 8v9N4fL/S+Hy/0rg8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg8v9M4PP/aeb1/3rp9v966fb/eun2/3jp + 9f946PX/d+j1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn9f9w5/X/b+b1/23m9P9m5fT/YOT0/13k + 8/9a4/P/WuPz/1zk9P9g5PT/ZeX0/2bl9P9l5fT/ZOX0/2Pl9P9i5PT/YeT0/2Dk9P9e5PT/XuT0/13k + 9P9b5PT/W+Tz/1nj8/9Z4/P/V+Pz/z/e8f8X2O//DNbu/wrW7v8J1u7/B9Xt/wXV7f8E1e3/AtTt/wHU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Dtfv/wCYtP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wC/2v8A1O3/ANTt/wDS + 6/8A0ur/ANHp/wDP5/8Azub/AM3m/wDM5P8Ay+P/AMri/wDI4P8AyN//AMfe/wDF3P8AxNv/AMPa/wDC + 2f8Awdj/AMDX/wC+1f8AvtX/AL7V/wC+1f8Av9b/AMDX/wDA1/8Awdj/AMPZ/wDH3v8p1ur/TeT3/0/o + +/9P6fz/T+n8/0/p/P9P6fz/TOj7/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BdXt/xXY + 7v8X1uz/CqbA/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGvyP9N5/n/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9J5Pf/RN/y/0Xg8v9G4PL/R+Dy/0jg8v9J4PL/SuDy/0rg8v9M4fL/TeHy/03h + 8v9O4fP/T+Hz/0/h8/9Q4fP/UeLz/1Li8/9S4vP/U+Lz/1Ti8/9U4vP/VOLz/1Xi8/9V4vP/VuPz/1bj + 8/9W4/P/VuPz/1fj8/9X4/P/V+Pz/1fj8/9X4/P/WOPz/1jj8/9Y4/P/V+Pz/1fj8/9X4/P/V+Pz/1fj + 8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li8/9R4vP/UOHz/0/h + 8/9P4fP/TuHz/03h8v9N4fL/TOHy/0vh8v9K4PL/SeDy/0ng8v9g5PT/e+n2/3zp9v986fb/e+n2/3rp + 9v966fb/een2/3jo9f946PX/duj1/3Xo9f916PX/cuj1/2Ti9f9c1fT/Usjz/0+99P9NsfT/UKv0/1Ko + 9f9TpfX/VKT1/1Ol9f9RqPX/Tqv0/0ux8/9JvPP/S8bz/0/T8/9V4PP/YOT0/2Lk9P9g5PT/YOT0/17k + 9P9e5PT/XeT0/1vk9P9b5PP/WePz/1jj8/9X4/P/VOLz/y7c8P8N1u7/Cdbu/wjV7v8H1e3/BdXt/wPV + 7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8d3PL/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/wjW7v8AiKX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCzzv8A1O3/ANTt/wDU + 7f8A0uv/ANLq/wDR6f8Az+f/AM7m/wDN5f8AzOT/AMvj/wDJ4f8AyOD/AMjf/wDH3v8Axdz/AMTb/wDD + 2f8Awtn/AMLZ/wDD2f8Aw9r/AMXc/wDH3v8AyOD/AMri/wDN5f8Az+f/ANLq/wDU7f8A1O3/NOH3/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Pl+f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wXV + 7f8X2O//Gdjv/xrY7/8Su9P/AYek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0bd + 8f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/SOP0/0bg8v9H4PL/SODy/0ng8v9K4PL/S+Hy/0zh8v9N4fL/TuHz/0/h + 8/9P4fP/UOHz/1Hi8/9S4vP/UuLz/1Pi8/9U4vP/VOLz/1Xi8/9W4/P/VuPz/1bj8/9X4/P/WOPz/1jj + 8/9Z4/P/WePz/1nj8/9Z4/P/WePz/1nj8/9a4/P/WuPz/1rj8/9a4/P/WuPz/1rj8/9a4/P/WePz/1nj + 8/9Z4/P/WePz/1nj8/9Z4/P/WOPz/1jj8/9X4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9U4vP/U+Lz/1Li + 8/9S4vP/UeLz/1Dh8/9P4fP/T+Hz/07h8/9N4fL/TOHy/1Di8v9y5/X/f+n2/3/p9v9+6fb/fen2/3zp + 9v986fb/e+n2/3rp9v966fb/eOn1/3Tn9f9j3/T/Vcnz/06x9P9So/T/U6L0/1Oi9P9TovT/U6L0/1Oi + 9P9TovT/U6L0/1Oi9P9TovT/U6L0/1Oi9P9TovT/UqLz/1Ki8/9SovP/UaPz/0ew8v9HxvL/Tdzz/13k + 9P9g5PT/XuT0/17k9P9d5PT/W+Tz/1rj8/9Z4/P/WOPz/1fj8/9W4vP/Qd/y/xLX7v8J1u7/B9Xt/wbV + 7f8E1e3/A9Xt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07o/P8Cwt7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh7gCmwv8A1O3/ANTt/wDU + 7f8A0+z/ANLr/wDS6v8A0Oj/AM/n/wDO5v8AzeX/AM3l/wDM5P8AzeX/AM3l/wDN5v8Azub/AM/n/wDQ + 6P8A0ur/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk + +f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8G1e3/GNjv/xrY7/8c2e//Hdnv/xrL4v8FkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Giqj/Oczj/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8v9I4PL/SeDy/0rg8v9L4fL/TOHy/03h8v9O4fP/T+Hz/1Dh + 8/9R4vP/UuLz/1Li8/9T4vP/VOLz/1Ti8/9V4vP/VuPz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WePz/1rj + 8/9a4/P/W+Pz/1vj8/9b4/P/W+Pz/1vj8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk8/9c5PP/XOTz/1zk + 8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj8/9a4/P/WePz/1nj8/9Z4/P/WOPz/1fj8/9W4/P/VuPz/1Xi + 8/9U4vP/VOLz/1Pi8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/1rj9P9+6fb/ger2/4Hq9v+A6fb/f+n2/3/p + 9v9+6fb/fOn2/3zp9v976fb/c+f1/1zT9P9PtPL/UqLz/1Kh8/9SofP/UqHz/1Kh8/9RofL/UaHy/1Gh + 8v9RofL/UaHy/1Gh8v9RofL/UaHy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Gg8v9RoPL/UaDy/1Ch + 8v9GsfL/Rs7y/1fi8/9e5PT/XeT0/1zk9P9b5PP/WuPz/1nj8/9X4/P/V+Pz/1bi8/9P4fP/Gtnv/wnW + 7v8H1e3/BdXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zHh9v9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9D5fn/AKvG/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA1Oz/ANTt/wDU + 7f8A1O3/ANPs/wDT7P8A0uv/ANPs/wDT7P8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDP + 6f8Avtn/AKbC/wDH4f8A0er/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/B9Xt/xrY7/8c2e//Hdnv/x/Z7/8g2e//H9Xr/wygu/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wGEov8ntc7/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03n+v9I4PL/SuDy/0vh8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi + 8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj8/9W4/P/V+Pz/1jj8/9Z4/P/WePz/1rj8/9b4/P/W+Pz/1vj + 8/9c5PP/XOTz/13k9P9d5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k9P9e5PT/XuT0/17k + 9P9e5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj + 8/9X4/P/VuPz/1bj8/9V4vP/VOLz/1Ti8/9S4vP/UuLz/2Hl9P+B6fb/g+r3/4Lq9/+B6vb/ger2/3/p + 9v9/6fb/fun2/33p9v946Pb/Xs7z/0+r8f9QoPH/UKDx/1Cg8f9QoPH/UJ/x/1Cf8f9Qn/H/UJ/x/1Cf + 8f9Qn/H/UJ/x/1Cf8f9Qn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/H/T5/x/0+f8f9Pn/D/T5/w/0+f + 8P9Pn/D/T5/w/0+f8P9IqO//R8nx/1ni8/9c4/T/W+P0/1rj8/9Y4/P/WOPz/1bj8/9V4vP/VOLz/1Hi + 8/8g2vD/CNXu/wfV7f8F1e3/A9Xt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8+5fn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/NuL3/wCTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqCBANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCz + zf8AiKb/AIOh/wCDof8Ag6H/AIOh/wCKp/8Al7T/AKTA/wCxzP8Avtj/AMrk/wDU7P8A1O3/ANTt/wDU + 7f8A1O3/POT5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zji+P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wbV7f8b2e//Hdnv/x/Z7/8g2e//Idrv/yPa7/8k2vD/FbTO/wGEo/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xafuv9J4fX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5ff/SuDy/0zh8v9N4fL/TuHz/0/h8/9Q4fP/UeLz/1Li + 8/9T4vP/VOLz/1Xi8/9W4/P/VuPz/1fj8/9Y4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XeT0/17k + 9P9e5PT/XuT0/1/k9P9f5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/2Dk + 9P9g5PT/YOT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9e5PT/XeT0/13k9P9c5PP/W+Pz/1vj + 8/9a4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/2fl9P+C6vb/g+r2/4Lq9v+B6fb/gen2/3/p + 9v9/6fb/fun2/3zp9v9m1vT/Uarw/0+e8P9PnvD/T57w/0+e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e + 8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06e8P9OnvD/Tp7w/06d7/9One//Tp3v/06d + 7/9One//Tp3v/06d7/9One//TZ3v/02d7/9Kp+//StDy/1jj8/9Y4/T/V+Pz/1bi8/9U4vP/U+Lz/1Li + 8/9R4vP/T+Hz/yXb8P8H1e3/BtXt/wTV7f8C1O3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S+j7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/ynb8P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgOwDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC2 + 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKL/AIyq/wCa + t/8AudP/ANLq/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P844vj/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8E1e3/HNnv/x7Z7/8g2e//Idrv/yPa7/8k2vD/Jtrw/yjb8P8gx97/BYyp/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY+s/z7T6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOP0/03h8v9N4fL/T+Hz/0/h8/9R4vP/UuLz/1Pi + 8/9U4vP/VeLz/1bj8/9W4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/XOTz/13k9P9e5PT/XuT0/1/k + 9P9g5PT/YOT0/2Dk9P9h5PT/YeT0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9j5fT/Y+X0/2Pl + 9P9i5fT/YuX0/2Ll9P9i5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k + 9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Y4/P/V+Pz/2bm9P+C6vb/gur2/4Lq9v+B6vb/ger2/3/p + 9v9/6fb/fun2/3jk9f9Xt/H/TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d7/9Nne//TZ3v/02d + 7/9Nne//TJzu/0yc7v9Mne7/VJ/r/1qh6f9couj/WqHp/1Sf6/9Mne7/TJzu/0yc7v9MnO7/TJzu/0yc + 7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9MnO7/TJzu/0yc7v9Js+//Ut3z/1Tj8/9U4vP/U+Lz/1Hi + 8/9Q4vP/T+Hz/07h8/9L4fP/Idrw/wfV7f8F1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Dtfv/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cxd3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCA + gAIA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDA + 2/8AhaP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCZtf885Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/OOL4/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/A9Xt/x7Z7/8f2e//Idrv/yPa7/8k2vD/Jtrw/yfb8P8p2/D/K9vw/yjU + 6f8Mm7b/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ChaP/Lr3V/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03h8v9O4fP/T+Hz/1Hi8/9S4vP/U+Lz/1Ti + 8/9V4vP/VuPz/1fj8/9Y4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Dk + 9P9h5PT/YuX0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl + 9P9l5fT/ZeX0/2Xl9P9l5fT/ZeX0/2Xl9P9k5fT/ZOX0/2Pl9P9j5fT/YuX0/2Ll9P9h5PT/YeT0/2Dk + 9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/2bl9P+D6vb/g+r2/4Lq9v+C6vb/ger2/4Dq + 9v9/6vb/f+r2/27X8/9Po+//TJzu/0yc7v9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0ub7f9Lm+3/S5vt/0uc + 7f9gouX/fK3b/5i20f+pvcv/usPG/73ExP+9xMT/vcTE/73ExP+9xMT/usPG/6q9y/+XttH/fK3b/2Ci + 5f9LnO3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprt/0qa7f9Kmu3/Sprs/0ih7P9L0vH/UuLy/1Hi + 8v9Q4vL/T+Ly/03h8v9N4fL/S+Hy/0nh8v8b2O//BtXt/wTV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yHd + 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/DqK9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oaAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDI + 4/8Aiaj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Otzx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrk+P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb8P8p2/D/Ktvw/yzb + 8P8t3PD/Ltvv/xivyP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8bpsD/S+T4/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/n+v9P4fP/UOHz/1Li8/9S4vP/VOLz/1Ti + 8/9W4/P/VuPz/1jj8/9Z4/P/WuPz/1vj8/9c5PP/XeT0/17k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll + 9P9i5fT/Y+X0/2Tl9P9l5fT/ZeX0/2Xl9P9m5fT/ZuX0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl + 9P9n5fT/Z+X0/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2bl9P9l5fT/ZeX0/2Xl9P9k5fT/Y+X0/2Pl + 9P9i5fT/YuX0/2Hk9P9g5PT/YOT0/17k9P9e5PT/XeT0/2Xl9P+C6vb/g+r2/4Pq9v+C6vb/ger2/4Dq + 9v+A6fb/f+n2/2bL8v9Km+3/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/Sprs/0qa7P9Kmuz/X6Ll/4qy + 1v+xwMj/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/scDI/4uy1f9fouT/SZnr/0mZ6/9Jmev/SZnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/R5rr/0bG + 7/9P4fL/TuHy/03h8v9L4fL/SuDy/0ng8v9H4PL/RODy/xXX7v8F1e3/BNXt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f804ff/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKJVAAAAAADP5/8Azub/AM7m/wDO5v8Azub/AM7m/wDN5v8Azeb/AM3m/wDN5v8Azub/AM3m/wDK + 4f8Ajqz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/zbT6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/5fn/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/H9nv/yLa7/8k2vD/Jdrw/yfb8P8p2/D/Ktvw/yvb + 8P8t3PD/L9zw/zDc8P8y3PH/JMPZ/wSJp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wyT + sP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5ff/UeLz/1Li8/9T4vP/VOLz/1Xi + 8/9W4/P/WOPz/1nj8/9a4/P/W+Pz/1zk8/9d5PT/XuT0/1/k9P9g5PT/YOT0/2Hk9P9i5fT/Y+X0/2Tl + 9P9l5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2jm9P9p5vT/aeb0/2rm9P9q5vT/aub0/2rm + 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9p5vT/aeb0/2jm9P9o5vT/Z+X0/2fl9P9n5fT/ZuX0/2Xl + 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/YOT0/2Pl9P+B6vb/hOr3/4Pq9v+C6vb/ger2/4Hq + 9v+A6vb/fur2/1/A8f9Imev/SJnr/0iZ6/9Imev/SJnr/0iZ6/9Imev/SJnr/0iY6/9iouL/nbjP/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/vsXF/77F + xf++xcX/v8bG/7/Gxv+/xsb/wMbG/6C5z/9ioeL/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX + 6v9Gl+n/RLru/0vg8v9L4fL/SeHy/0jg8v9H4PL/RuDy/0Tg8v893vL/Ddbt/wTV7f8C1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrY7f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H0AICqDAAAAAAAzOT/AMvj/wDK4v8AyOD/AMjf/wDH3v8Ax97/AMjg/wDK4v8AzeX/AM/n/wDN + 5v8Aka//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xy+L/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Q+X5/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x3Z7/8k2vD/Jdrw/yba8P8p2/D/Ktvw/yvb + 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f8v0ef/C5Wx/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/BIil/zPF3P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/UeT1/1Li8/9U4vP/VeLz/1bj + 8/9X4/P/WePz/1nj8/9b4/P/W+Pz/13k9P9e5PT/X+T0/2Dk9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl + 9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/a+b1/2vm9f9s5vX/bOb1/2zm + 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/a+b1/2vm9f9r5vX/aub0/2rm9P9q5vT/aeb0/2jm + 9P9n5fT/Z+X0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Pl9P976fb/hOr3/4Pq9/+D6vf/gur3/4Dp + 9v+A6fb/f+n2/13A7/9Hl+r/R5fq/0eX6v9Hl+r/R5fq/0eX6v9Gl+n/Rpfp/1Gb5f+QstL/vMTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP++xcX/vsXF/77Fxf+/xsb/v8bG/7/Gxv/Ax8f/wMfH/8DH + x//Bx8f/wcjI/8HIyP/CyMj/wsnJ/8LJyf/Dycn/wsnJ/5W21P9SmuX/RZbp/0WW6f9Flun/RZbp/0WV + 6P9Flej/RZXo/0WV6P9Auez/SODy/0fg8v9G4PL/Rd/y/0Pf8v9C3/L/Qd/y/zLc8P8G1e3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Ddfw/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8mudL/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhogAAAAAAAAAAAM3l/wDN5f8Azeb/AM/n/wDR6f8A0uv/ANTt/wDU7f8A1O3/ANTt/wDQ + 6P8AlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/LMbd/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn + +v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8c2e//JNrw/yba8P8o2/D/Kdvw/yvb + 8P8t3PD/Ltzw/zDc8P8y3PH/M93x/zXd8f823fH/N93x/zfc7v8YqcH/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Ia/I/03n+f9P6fz/T+n8/0/p/P9P6fz/T+n8/1Pi8/9U4vP/VuPz/1bj + 8/9Y4/P/WePz/1rj8/9b4/P/XeT0/17k9P9f5PT/YOT0/2Hk9P9i5fT/YuX0/2Tl9P9l5fT/ZeX0/2bl + 9P9n5fT/aOb0/2nm9P9q5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/23m9f9u5/X/buf1/27n + 9f9u5/X/b+f1/2/n9f9v5/X/b+f1/27n9f9u5/X/buf1/27n9f9t5vX/beb1/2zm9f9s5vX/bOb1/2vm + 9f9q5vT/aub0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P916PX/hOv2/4Tr9v+C6vb/gur2/4Hq + 9v+B6vb/f+r2/2DF8P9Flun/RZbp/0WW6f9Flun/RZbp/0WV6P9Flej/RZXo/2uk3f+zwcf/vcTE/77F + xf++xcX/vsXF/7/Gxv+/xsb/v8bG/8DGxv/Ax8f/wMfH/8HHx//ByMj/wcjI/8LIyP/CyMj/wsnJ/8PJ + yf/Dycn/w8rK/8TKyv/Eysr/xMrK/8XLy//Fy8v/xcvL/8bMzP/GzMz/vcjN/2+m3f9DlOf/Q5Tn/0OU + 5/9DlOf/Q5Tn/0OU5/9DlOf/Q5Tn/z6+7f9F4PL/RODy/0Lf8v9B3/L/QN/y/z7f8v893vL/Itrv/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yPd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/E5u2/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCBo0UAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO + 5v8Ak7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HOAICZCgCAnRoAg6JCAIKhagCD + oZIAg6G7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yjA1/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9M6Pv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/F9jv/yba8P8n2/D/Kdvw/yvb + 8P8s2/D/Ltzw/zDc8P8x3PD/Mtzx/zXd8f823fH/N93x/zne8f873vH/PN7x/yi91P8Ch6T/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbX/Rt3x/0/p/P9P6fz/T+n8/1Dn+v9U4vP/VuPz/1fj + 8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k9P9g5PT/YOT0/2Ll9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2fl + 9P9o5vT/aub0/2rm9P9r5vX/bOb1/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9v5/X/cOf1/3Dn + 9f9w5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cOf1/3Dn9f9w5/X/b+f1/2/n9f9v5/X/b+f1/27n + 9f9t5vX/bOb1/2zm9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9s5vT/hOr2/4Xq9v+D6vb/g+r2/4Lq + 9v+B6vb/gOr2/2rM8f9ElOf/RJTn/0SU5/9DlOf/Q5Tn/0OU5/9DlOf/RZXm/4ux1P+/xsb/wMbG/8DH + x//Ax8f/wcfH/8HIyP/ByMj/wsjI/8LIyP/Cycn/w8nJ/8PJyf/Dysr/xMrK/8TKyv/Eysr/xcvL/8XL + y//Fy8v/xszM/8bMzP/GzMz/x83N/8fNzf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8nPz//Jz8//lbfW/0SU + 5f9Ck+b/QpPm/0KT5v9Ck+b/QZLm/0GS5v9Bkub/QcTu/0Lf8v9C3/L/QN/y/z/f8v893/L/PN/y/zre + 8f8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f875Pj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOb5/wKFpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoeQAgIACAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDJ + 4/8Aj6z/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8juNH/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPX7v8n2/D/Kdvw/yvb + 8P8s2/D/Ldzw/y/c8P8x3PD/Mtzx/zTd8f823fH/N93x/zne8f863vH/PN7x/z7e8f8/3/H/NtDk/wqR + rf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP85zeP/T+n8/0/p/P9S5vf/VuPz/1jj + 8/9Z4/P/WuPz/1vj8/9d5PT/XuT0/1/k9P9g5PT/YuX0/2Ll9P9k5fT/ZeX0/2bl9P9n5fT/Z+X0/2nm + 9P9q5vT/aub0/2zm9f9s5vX/beb1/27n9f9v5/X/b+f1/2/n9f9w5/X/cef1/3Hn9f9x5/X/cuf1/3Ln + 9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9y5/X/cuf1/3Hn9f9x5/X/cef1/3Hn + 9f9w5/X/b+f1/2/n9f9u5/X/beb1/2zm9f9s5vX/a+b1/2rm9P9p5vT/f+r2/4Xq9v+E6vb/g+r2/4Lq + 9v+C6vb/ger2/3DO5f9CleX/QpPm/0KT5v9Ck+b/QpPm/0KT5v9Ck+b/SJXk/5250P/CyMj/wsjI/8LJ + yf/Dycn/w8nJ/8PKyv/Eysr/xMrK/8TKyv/Fy8v/xMrK/8LJyf/Ax8f/v8bG/77Fxf+9xMT/vcTE/73E + xP++xcX/v8bG/8HIyP/Fy8v/yM3N/8nPz//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8vR0f/M0dH/zNHR/8zS + 0v+qwtb/SJXj/0CR5P9AkeT/QJHk/0CR5P9AkeT/QJHk/0CU5f9A0e//P9/x/z3e8f883vH/PN7x/zne + 8f843fH/Kdvw/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8F1u7/Tej8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zrP5f8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6GIAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDG + 3/8Ai6n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HpAIahJgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Gq7I/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zXH3v8DmbX/AJOw/wCmwf8Azuf/ANTt/wDU7f8A1O3/ANTt/wDU7f8O1u7/KNvw/yrb + 8P8r2/D/Ldzw/y/c8P8w3PD/Mtzx/zTd8f813fH/N93x/zne8f863vH/PN7x/z3e8f8/3/H/Qd/y/0Lf + 8v8/2u7/F6K8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/ye2z/9O6Pv/VOX1/1jj + 8/9Z4/P/W+Pz/1zk8/9e5PT/XuT0/2Dk9P9h5PT/YuX0/2Pl9P9l5fT/ZeX0/2fl9P9n5fT/aeb0/2rm + 9P9r5vX/bOb1/2zm9f9t5vX/b+f1/2/n9f9w5/X/cef1/3Hn9f9y5/X/cuf1/3Pn9f9z5/X/c+f1/3To + 9f906PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dej1/3Xo9f916PX/dOj1/3To9f9z5/X/c+f1/3Pn + 9f9z5/X/cuf1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m9f9s5vX/dej2/4Xr9v+F6/b/hOv2/4Pq + 9v+C6vb/ger2/37l9f84dqz/OYHM/0CR5f9AkeX/QJHl/0CR5f9AkeX/SJTk/6m+z//Eysr/xMrK/8XL + y//Fy8v/xcvL/8bMzP/GzMz/xszM/8fNzf/Eysr/wMbG/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/x83N/83S0v/N0tL/zdPT/87T0//O09P/ztTU/8/U + 1P/P1NT/z9TU/7fI1v9GleL/PpDj/z6Q4/8+kOP/PpDj/z6Q4/8+j+P/P5rl/z/a8P873vH/Ot7x/zne + 8f833vH/Nt7x/zDc8P8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H9AICjJAAAAAAAAAAAAAAAAADU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC5 + 0/8AhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfLQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISiNACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xKguv9P6fz/T+n8/0/p + /P9P6fz/Rt3x/xegu/8Ag6H/AIOh/wCDof8Ag6H/AJaz/wDT7P8A1O3/ANTt/wDU7f8A1O3/CNXu/ynb + 8P8r2/D/Ldzw/y7c8P8w3PD/Mtzx/zPd8f813fH/N93x/zjd8f853vH/PN7x/z3e8f8+3vH/QN/y/0Lf + 8v9D3/L/ReDy/0bg8v8ot87/AYSj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fp+6/1Db + 7v9Z4/P/W+Pz/1zk8/9e5PT/X+T0/2Dk9P9i5fT/YuX0/2Tl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm + 9f9s5vX/beb1/27n9f9v5/X/b+f1/3Hn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/duj1/3bo + 9f926PX/duj1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3fo9f936PX/d+j1/3bo9f926PX/duj1/3bo + 9f916PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn9f9v5/X/b+f1/4Pq9v+F6vf/her3/4Tq + 9v+D6vb/gen2/3LV+f9Zr+H/JleJ/zBurf8/kOP/P5Dj/z+Q4/8/kOP/QZLj/6W90f/GzMz/x83N/8fN + zf/Hzc3/yM3N/8jOzv/Izs7/yc7O/8bMzP+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Bx8f/y9HR/9DV1f/Q1dX/0dbW/9HW + 1v/R1tb/0tfX/9LX1//S19f/s8fX/0GQ4v89juL/PY7i/z2O4v89juL/PY7i/z2O4v9Asej/Od7x/zjd + 8f833fH/Nd3x/zPd8f8t2/D/Jtrw/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zPi9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/C5Gu/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOitwAAAAAAAAAAAAAAAAAAAAAA1O3/ANTt/wDU7f8A1O3/ANTs/wCp + xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6LwAISiNAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoRsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj63/T+n8/0/p + /P9O6Pv/Lb3V/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Awtz/ANTt/wDU7f8A1O3/ANTt/wPV + 7f8r2/D/LNvw/y3c8P8w3PD/Mdzw/zLc8f813fH/Nt3x/zfd8f853vH/O97x/zze8f8+3vH/QN/y/0Hf + 8v9D3/L/ReDy/0bg8v9I4PL/SeDy/zrM4P8Ijar/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Lj6v/TtXn/13k9P9e5PT/X+T0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2zm + 9f9s5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9z5/X/c+f1/3To9f916PX/duj1/3bo9f936PX/d+j1/3jo + 9f946PX/eOj1/3no9v956Pb/eej2/3no9v966fb/eun2/3rp9v956Pb/eej2/3no9v956Pb/eOj1/3jo + 9f946PX/d+j1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Pn9f9x5/X/cef1/3jo9v+G6/f/hev3/4Tq + 9/+E6vf/gur2/3/l9/9au/z/M3q7/yRVh/8mWY3/PY7g/z2P4v89juL/PY7i/5K11v/Jzs7/yc/P/8nP + z//Kz8//ytDQ/8rQ0P/L0ND/y9DQ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP/Hzc3/0tfX/9PY + 2P/U2Nj/1NnZ/9TZ2f/V2dn/1dra/9Xa2v+fvdr/O4zg/zuM4P87jOD/O4zg/zuM4P87jOD/PIzg/z3M + 7f823fH/Nd3x/zPd8f8x3fH/Kdvw/ynb8P8P1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f9K5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qtjt/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoUwAAAAAAAAAAAAAAAAAAAAAANTt/wDU7f8A1O3/AMvk/wCZ + tf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HsAIKfNQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/07p + /P9C2O3/Epq1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/J9vw/y3c8P8v3PD/MNzw/zLc8f803fH/Nd3x/zfd8f853vH/Ot7x/zze8f8+3vH/P9/x/0Hf + 8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg8v9N4fL/SNns/xWct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/w+Srv9d4/P/YOT0/2Hk9P9i5fT/ZOX0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm + 9f9u5/X/b+f1/3Dn9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3bo9f926PX/d+j1/3jo9f946PX/eej2/3no + 9v966fb/e+n2/3vp9v976fb/e+n2/3vp9v986fb/fOn2/3zp9v986fb/fOn2/3vp9v976fb/e+n2/3vp + 9v976fb/eun2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3bo9f906PX/c+f1/3Pn9f+E6vb/h+v3/4br + 9/+F6/f/hOv3/4Lq9/9u0fn/SbT8/zKCw/8kVIb/I1SG/zR7xf87jeH/O43h/3Cl2//L0ND/y9HR/8vR + 0f/M0dH/zNLS/83S0v/N0tL/zNHR/8HHx/+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/8LJ + yf/U2Nj/1tvb/9fb2//X29v/19zc/9jc3P/Y3Nz/2Nzc/3us3f85i9//OYvf/zmL3/85i9//OYvf/zmL + 3/89nuT/Ndzx/zPc8f8x3PH/MNzx/yfb8P8n2/D/INnv/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8X2/H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq5 + 0f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDod4AgIACAAAAAAAAAAAAAAAAAAAAAADU7f8A1O3/ALfR/wCI + pv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HiAIafKAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8jtc3/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIak/wDO5v8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yDZ7/8u3PD/MNzw/zLc8f8z3fH/Nd3x/zfd8f843fH/Ot7x/zze8f893vH/P9/x/0Hf + 8v9C3/L/RN/y/0Xg8v9H4PL/SeDy/0rg8v9M4fL/TeHy/0/h8/9Q4fL/KLHJ/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/TtLl/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9q5vT/bOb1/23m + 9f9v5/X/b+f1/3Hn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f946PX/eOj1/3no9v966fb/e+n2/3vp + 9v986fb/fOn2/33p9v996fb/fen2/33p9v9+6fb/fun2/37p9v9+6fb/fun2/37p9v9+6fb/fen2/33p + 9v996fb/fen2/3zp9v976fb/e+n2/3vp9v966fb/eej2/3jo9f936PX/duj1/3bo9f956fX/h+r2/4fq + 9v+F6vb/her2/4Pq9v+C6fb/V7r7/z+v/P85oen/I1OF/yNThf8qZaP/Oovf/0eS3//CzdT/ztPT/87T + 0//O1NT/z9TU/8/U1P/P1NT/zdLS/7/Gxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/wMfH/9Xa2v/Z3d3/2t7e/9re3v/a3t7/297e/9vf3//S2+D/S5Tf/ziK3v84id7/OIne/ziJ + 3v84id7/OYne/znE7P8w3PD/L9zw/y3c8P8k2vD/JNrw/yTa8P8H1e3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAzOb/AJ26/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAISeHQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqHXAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Aoak/wC61f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8W2O//L9zw/zHc8P8y3PH/Nd3x/zbd8f843fH/Od7x/zve8f893vH/Pt7x/0Df + 8v9C3/L/Q9/y/0Xg8v9H4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/UuLz/1Ti8/88x9v/Boqn/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/1LV5/9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/bOb1/27n + 9f9v5/X/cOf1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo9f946PX/eun2/3vp9v976fb/fOn2/33p + 9v996fb/fun2/37p9v9/6fb/f+n2/3/p9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/4Dq9v+A6vb/gOr2/3/p + 9v9/6fb/f+n2/3/p9v9+6fb/fen2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f946PX/gOr2/4jr + 9v+G6/b/her2/4Tq9v+D6vb/ddr4/0iz/P8+r/z/Pq/8/yZlnP8hUoT/IlSH/zeH2f+cvNr/0NXV/9DV + 1f/R1tb/0dbW/9HW1v/S19f/0NXV/8DGxv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+6t7H/spqD/6yDYf+peFH/pm9D/6dxRv+qe1X/rYho/7Sh + kP+7vrv/vcTE/73ExP/Ax8f/2d3d/9zg4P/d4OD/3eDg/93h4f/e4eH/3uHh/7DJ4P82iNz/Nojc/zaI + 3P82iNz/Nojc/zaI3P85m+H/Ltzw/y3b8P8q2/D/Idrv/yHa7/8h2u//E9fu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9H3/P/AYSi/wCDof8Ag6H/AIOh/wCDof8Ag6HzAICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAIek/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKG8AICcEgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhuwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/E5y2/0PZ7v8U2fH/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/DNbu/zDc8P8y3PH/NN3x/zXd8f833fH/Od7x/zre8f883vH/Pt7x/0Df + 8v9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9L4fL/TeHy/0/h8/9Q4fP/UuLz/1Ti8/9V4vP/VuPz/03X + 6f8SlrL/AIOh/wCDof8Ag6H/AIOh/xWXs/9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9r5vX/bOb1/27n + 9f9v5/X/cef1/3Ln9f9z5/X/dOj1/3bo9f926PX/eOj1/3jo9f966fb/e+n2/3vp9v996fb/fen2/37p + 9v9/6fb/f+n2/4Dq9v+B6vb/ger2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq9v+C6vb/gur2/4Lq + 9v+C6vb/gur2/4Lq9v+B6vb/gOr2/3/p9v9/6fb/f+n2/33p9v996fb/fOn2/3vp9v976fb/eej2/4bq + 9v+H6/b/huv2/4Xr9v+D6vb/g+r2/1/C+f8/r/z/Pq/8/z6v/P8xh8f/IVKE/yBRhP9IgLn/0NbY/9LX + 1//T2Nj/09jY/9TY2P/U2Nj/1NnZ/8PKyv+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/7izqv+sgmH/pGY2/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pmo7/7CRdv+7vbn/vcTE/8bMzP/f4uL/3+Pj/+Dj4//g4+P/4OPj/+Dk5P/g4+T/Y6De/zWH + 2/81h9v/NYfb/zSG2/80htv/NYbb/zHL7P8q2/D/Jtvw/x7Z7/8e2e//Htnv/xzZ7/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xnb8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Lb3U/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAKqqAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEoJcAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiaf/Lr/W/0/p/P9P6fz/Qub5/wLV + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f8x3PD/M93x/zXd8f833fH/ON3x/zne8f883vH/Pd7x/z/f + 8f9B3/L/Q9/y/0Tf8v9F4PL/SODy/0ng8v9K4PL/TeHy/07h8/9P4fP/UuLz/1Pi8/9U4vP/VuPz/1jj + 8/9Z4/P/WeHw/yquxv8GiKb/AYSi/x+iu/9Y2+v/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Ln9f9z5/X/dej1/3bo9f936PX/eOj1/3rp9v976fb/fOn2/33p9v996fb/f+n2/3/p + 9v+A6vb/ger2/4Lq9v+C6vb/g+r2/4Pq9v+E6vb/hOr2/4Tq9v+E6vb/her2/4Xq9v+F6vb/her2/4Tq + 9v+E6vb/hOr2/4Tq9v+E6vb/g+r2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/f+n2/37p9v996fb/fOn2/37p + 9v+I6/f/h+r3/4bq9v+F6vb/her2/4Dp9v9KtPv/Pq/8/z6v/P8+r/z/O6bx/yBShf8gUIP/b4KU/9XZ + 2f/V2tr/1tra/9ba2v/W2tr/19vb/8zS0v+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73E + xP+9xMT/vcTE/77ExP++xcX/vcLB/66KbP+kZjb/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pWo8/7Oei/+9xMT/0tfX/+Ll5f/i5eX/4+bm/+Pm5v/j5ub/5Obm/77S + 5P8zhdr/M4Xa/zOF2v8zhdr/M4Xa/zOF2v8zreT/KNrw/yLZ8P8b2e//G9nv/xvZ7/8b2e//BdXt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f804vf/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xKatf8Ag6H/AIOh/wCDof8Ag6H/AIOh/QCAoyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofUAgqJaAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqKBAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Yor3/R97y/0/p/P9P6fz/T+n8/0/p + /P8k3vT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ktvw/zTd8f813fH/N93x/zne8f873vH/PN7x/z7e + 8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TOHy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1fj + 8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Hk9P9i5fT/ZOX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3bo9f946PX/eej2/3vp9v976fb/fen2/33p9v9/6fb/f+n2/4Hq + 9v+C6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr9/+H6/f/h+v3/4fr9/+H6/f/h+v3/4fr + 9/+H6/f/h+v3/4fr9/+G6/b/huv2/4Xq9v+E6vb/hOr2/4Pq9v+C6vb/gur2/4Hq9v+A6vb/f+n2/37p + 9v+D6vb/iev3/4jr9/+G6vf/her2/4Tq9v9z2vj/Q7H8/z6v/P8+r/z/Pq/8/z6v/P8obqj/LViD/36C + g//FyMj/2Nzc/9jc3P/Y3d3/2d3d/9fb2/+/xsb/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/77F + xf+/xcX/v8bG/8DHx//Bx8f/wMG+/6p5Uv+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/r49z/7/Gxv/h5OT/5ejo/+Xo6P/m6Oj/5unp/+bp + 6f/l6er/V5nd/zGE2P8xhNj/MYTY/zGE2P8xhNj/M43b/yfb8P8f2e//Gdjv/xnY7/8Z2O//Gdjv/wzW + 7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/TOj7/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0bd8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDocYAg58lAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh8gCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/weMqf81x97/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/S+j7/wjX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/Z7/813fH/N93x/zjd8f863vH/PN7x/z7e + 8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UOHz/1Li8/9U4vP/VeLz/1fj + 8/9Z4/P/WuPz/1zk8/9e5PT/X+T0/2Hk9P9i5fT/ZOX0/2Xl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v986fb/fen2/3/p9v9/6fb/ger2/4Lq + 9v+C6vb/hOr2/4Tq9v+F6vb/huv2/4fr9/+H6/f/iOv3/4jr9/+J6/f/iev3/4nr9/+J6/f/iev3/4nr + 9/+J6/f/iev3/4nr9/+J6/f/iOv3/4jr9/+H6/f/h+v3/4br9v+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq + 9v9/6fb/h+r3/4jr9/+H6/f/huv3/4Xr9/+E6vb/YMb3/z6v/P8+r/z/Pq/8/z6v/P8+r/z/NZXa/1Vs + g/+ChIT/oqWl/9ve3v/b39//29/f/9zf3//L0dH/vcTE/73ExP+9xMT/vcTE/73ExP++xMT/v8XF/8DG + xv/Bx8f/wcjI/8LJyf/Dycn/w8nI/61/W/+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+ymIH/0NXV/+fq6v/o6ur/6Ovr/+jr + 6//p6+v/6ezs/6bF4v8wgtf/MILX/zCC1/8vgtf/L4LX/y+C1/8ny+z/G9nv/xbY7/8W2O//Ftjv/xbY + 7/8Q1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8ru9P/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ84AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoHkAqqoDAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnyAAhKG4AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/x6qxP9K4vb/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P814vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8S1+7/Nt3x/zfd8f853vH/O97x/zze + 8f8+3vH/QN/y/0Lf8v9D3/L/ReDy/0fg8v9J4PL/SuDy/03h8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1bj + 8/9Y4/P/WuPz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p9v+A6vb/gur2/4Lq + 9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iev3/4rr9/+K6/f/i+v3/4vr9/+L6/f/i+v3/4vr + 9/+L6/f/i+v3/4vr9/+L6/f/i+v3/4vr9/+K6/f/iev3/4nr9/+I6/f/h+v3/4fr9/+G6/b/hOr2/4Tq + 9v+C6vb/gur2/4nr9/+J6/f/iOv3/4fr9/+F6/f/hOv3/062+P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z6v + +/9uhZf/hIaG/4WHh//T19f/3uHh/97h4f/c4OD/v8XF/73ExP+9xMT/vcTE/73ExP+/xsb/wMfH/8HI + yP/Cycn/w8rK/8TKyv/Fy8v/xszM/7KRdP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGY1/7eqm//n6ur/6+3t/+vt + 7f/r7e3/7O7u/+zu7v/c5Ov/LoHW/y6A1v8ugNb/LoDW/y6A1v8ugNb/KbTl/xjY7v8T1+7/E9fu/xPX + 7v8T1+7/Etfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/EJiz/wCDof8Ag6H/AIOh/wCDof8Ag6H/AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6H/AIOh/wCDof8Ag6H/AIOitwCGpCoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCEonAAg6H0AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Gu/zrO5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/xXZ8f8A1O3/ANTt/wDU7f8A1O3/BNXt/zbd8f843fH/Ot7x/zze + 8f8+3vH/P9/x/0Hf8v9D3/L/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Hi8/9S4vP/VOLz/1bj + 8/9Y4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9r5vX/beb1/2/n + 9f9w5/X/cef1/3Pn9f916PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fun2/3/p9v+B6vb/gur2/4Pq + 9v+E6vb/huv2/4fr9/+H6/f/iev3/4nr9/+K6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+O7Pf/juz3/47s + 9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iuv3/4nr9/+J6/f/h+v3/4fr + 9/+F6vb/hOr2/4Tq9v+K6/f/iev3/4fq9/+G6vf/her3/4Dp9/9Esvv/Pq/8/z6v/P8+r/z/Pq/8/z6v + /P8+r/z/XZ3H/4WHh/+Fh4f/r7Gx/+Dj4//g5OT/0tfX/73ExP+9xMT/vcTE/77Fxf/Axsb/wcjI/8PJ + yf/Eysr/xcvL/8bMzP/Hzc3/yM7O/8C1qf+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/oWMz/25E + I/8/JxT/JxgM/y0cDv9LLhj/gU8p/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+nbkL/19jV/+3v + 7//t7+//7vDw/+7w8P/u8PD/7/Dx/1CU2f8sf9T/LH/U/yx/1P8sf9T/LH/U/yub3P8T2O7/ENfu/xDX + 7v8Q1+7/ENfu/xDX7v8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW7/9O6Pz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/RNrv/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISgnwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOh/wCDof8AgqDVAIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDockAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8BhaL/JLHL/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/AtTt/wDU7f8A1O3/ANTt/wDU7f8q2/D/Od7x/zve + 8f883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9H4PL/SeDy/0rg8v9N4fL/TuHz/1Dh8/9S4vP/VOLz/1Xi + 8/9X4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9h5PT/YuX0/2Xl9P9m5fT/Z+X0/2rm9P9r5vX/bOb1/2/n + 9f9w5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq + 9v+F6vb/h+v3/4fr9/+J6/f/iev3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/4/s9/+P7Pf/kOz3/5Ds + 9/+Q7Pf/kOz3/5Ds9/+Q7Pf/kOz3/5Ds9/+P7Pf/j+z3/47s9/+O7Pf/jez3/4zs9/+L6/f/iuv3/4nr + 9/+I6/f/h+v3/4br9v+H6vb/iuv2/4jr9v+H6/b/hur2/4Xq9v933/f/QbD8/z6v/P8+r/z/Pq/8/z6v + /P8+r/z/Pq/8/0Gt9v+Di5D/h4mJ/42Pj//e4eH/4+bm/8bMzP+9xMT/vcTE/7/Gxv/Bx8f/wsnJ/8TK + yv/Fy8v/x8zM/8jOzv/Jz8//ys/P/8vQ0P+uf1r/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/jVct/x0S + Cf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP88JRP/nmEy/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7id + hv/w8fH/8PLy//Hy8v/x8vL/8fPz//Lz8/+Ot9//K37T/yt+0/8rftP/Kn3T/yp90/8rh9b/D9bu/w3W + 7v8N1u7/Ddbu/w3W7v8N1u7/BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8i3vT/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/yi3z/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoXIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDodMAgqFcAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkpIHAIKigwCDofkAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8PlrL/P9Tq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yXe9P8A1O3/ANTt/wDU7f8A1O3/Gtjv/zne + 8f883vH/Pt7x/z/f8f9B3/L/Q9/y/0Xg8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h8/9R4vP/U+Lz/1Ti + 8/9W4/P/WOPz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n + 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq + 9v+G6/b/h+v3/4jr9/+J6/f/i+v3/4vr9/+M7Pf/juz3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Lt + 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Hs9/+Q7Pf/kOz3/4/s9/+O7Pf/jez3/4zs + 9/+L6/f/iuv3/4nr9/+I6/f/iev3/4rr9v+J6/b/h+v2/4br9v+F6vb/bNT3/0Cw/P8+r/z/Pq/8/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/ZZvA/4mKiv+Jior/ury8/+Xo6P++xcX/vsXF/8DGxv/ByMj/w8nJ/8XL + y//GzMz/yM7O/8nPz//K0ND/zNHR/83S0v/JyMP/pGY1/6RlNP+kZTT/pGU0/6RlNP+kZTT/ilUs/wsH + A/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/yUXDP+hYzP/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+pd0//8fPz//P09P/z9PT/8/X1//T19f/09fX/u9Lo/yl80v8pfNL/KXzS/yl80v8pfNL/K33S/w7R + 7P8L1u7/C9bu/wvW7v8L1u7/C9bu/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/P+T5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq7/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaNFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIKh1wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOGpP8quND/Tuf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M6Pv/Cdbv/wDU7f8A1O3/ANTt/wrW + 7v873vH/PN7x/z7e8f9A3/L/Qt/y/0Pf8v9F4PL/R+Dy/0ng8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti + 8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9l5fT/Z+X0/2jm9P9q5vT/bOb1/23m + 9f9v5/X/cef1/3Ln9f906PX/duj1/3fo9f946PX/e+n2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Tq + 9v+G6/b/h+v3/4nr9/+K6/f/i+v3/4zs9/+O7Pf/juz3/5Ds9/+Q7Pf/kez3/5Lt9/+T7ff/k+33/5Tt + 9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt+P+V7fj/lO33/5Pt9/+T7ff/k+33/5Lt9/+R7Pf/kOz3/4/s + 9/+O7Pf/jez3/4vr9/+L6/f/iev3/4rr9/+L7Pf/iev2/4jr9v+G6/b/hev2/13E9f8+r/z/Pq/8/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/0es8f+JjI7/iouL/5KTk//d4OD/vsXF/8DGxv/CyMj/xMrK/8bM + zP/Hzc3/yc7O/8rQ0P/M0dH/zdLS/87T0//Q1dX/v6mW/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/y4c + Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Xjoe/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/pGU0/+Pf2v/19vb/9vf3//b39//29/f/9/j4/9rm8P8ne9D/J3vQ/yd70P8ne9D/J3vQ/yd7 + 0P8Qw+j/CNXu/wjV7v8I1e7/CNXu/wjV7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/DNjw/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWmFwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApA4AhKGVAIOh/QCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/xScuP9D2u7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zbi9/8A1O3/ANTt/wDU + 7f8A1O3/Nd3x/z3e8f8/3/H/Qd/y/0Pf8v9E3/L/RuDy/0jg8v9K4PL/S+Hy/03h8v9P4fP/UeLz/1Pi + 8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/23m + 9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq + 9v+H6/f/iOv3/4nr9/+K6/f/i+v3/43s9/+O7Pf/j+z3/5Ds9/+R7Pf/k+33/5Pt9/+U7ff/le34/5Xt + +P+W7fj/lu34/5ft+P+X7fj/l+34/5ft+P+X7fj/l+34/5bt+P+V7fj/le34/5Xt+P+U7ff/k+33/5Lt + 9/+R7Pf/kOz3/47s9/+O7Pf/jOz3/4vr9/+K6/f/iuv3/4nr9/+H6vb/hur2/4Tq9v9TvfX/Pq/8/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/bpy7/4yNjf+MjY3/sbW1/8DGxv/CyMj/xMrK/8bM + zP/Izc3/yc/P/8vR0f/N0tL/ztPT/9DV1f/R1tb/0tfX/7aSdP+kZTT/pGU0/6RlNP+kZTT/pGU0/4ZS + Kv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xILBv+jZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP/Qvq7/+Pn5//n5+f/5+fn/+fr6//r6+v/u8/f/JnnP/yZ5z/8mec//JnnP/yV5 + z/8lec//ELvl/wXV7f8F1e3/BdXt/wXV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf + 9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ia7H/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh3wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEok0Ag6HjAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/BYqn/zDB2P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ftrx/wDU + 7f8A1O3/ANTt/yLa7/8+3vH/QN/y/0Hf8v9D3/L/ReDy/0fg8v9I4PL/SuDy/0zh8v9O4fP/UOHz/1Li + 8/9U4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl9P9p5vT/aub0/2zm + 9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3vp9v996fb/f+n2/4Dq9v+C6vb/g+r2/4Xq + 9v+H6/f/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Hs9/+T7ff/k+33/5Xt+P+V7fj/lu34/5ft + +P+Y7vj/mO74/5nu+P+Z7vj/me74/5ru+P+Z7vj/me74/5nu+P+Y7vj/mO74/5ju+P+W7fj/le34/5Xt + +P+U7ff/k+33/5Hs9/+Q7Pf/j+z3/47s9/+M7Pf/i+v3/4rr9/+J6/f/iOv3/4bq9v+F6vb/Tbj1/z6v + /P8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/06v8P+LkJL/jo6O/2JkZP+Dh4f/xMrK/8bM + zP/Izs7/ys/P/8zR0f/O09P/z9TU/9HV1f/S19f/1NjY/9XZ2f+xg1//pGU0/6RlNP+kZTT/pGU0/6Rl + NP9hPB//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8CAgL/BQUF/wYGBv8FBQX/kFku/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/yK2W//v7+//7/Pz/+/z8//z8/P/8/Pz/+/z9/yR4zv8keM7/JHjO/yR4 + zv8keM7/JHjO/w+14v8C1O3/AtTt/wLU7f8C1O3/AtTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU + 7f9F5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef7/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oKcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWmFwCDoKcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/GaO+/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tm + +f8C1O3/ANTt/wDU7f8M1u7/Pt7x/0Df8v9C3/L/RN/y/0Xg8v9I4PL/SeDy/0vh8v9N4fL/T+Hz/1Hi + 8/9S4vP/VOLz/1bj8/9Y4/P/WePz/1vj8/9d5PT/X+T0/2Dk9P9i5fT/ZOX0/2bl9P9n5fT/aub0/2vm + 9f9t5vX/b+f1/3Hn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+C6vb/g+r2/4Tq + 9v+G6/b/iOv3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/lO33/5Xt+P+W7fj/mO74/5ju + +P+Z7vj/mu74/5ru+P+b7vj/m+74/5zu+P+c7vj/nO74/5vu+P+b7vj/mu74/5ru+P+Z7vj/mO74/5ju + +P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs9/+L6/f/iev3/4jr9/+H6/f/hOr2/0u1 + 9f8+r/z/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbL8/0iz/P9KtPz/cqC//4+QkP8lJib/HB0d/8TK + yv/Izs7/ys/P/8zR0f/O09P/0NXV/9HW1v/T2Nj/1dnZ/9ba2v/Y3Nz/r3xV/6RlNP+kZTT/pGU0/6Rl + NP+kZTT/VzYc/wAAAP8AAAD/AAAA/wwMDP8WFhb/GRkZ/xwcHP8gICD/IyMj/yYmJv8pKSn/LCws/5ds + S/+maTn/pGU0/6RlNP+kZTT/pGU0/8Ojiv/9/v7//v7+//7+/v/+//////////////8re8//InbN/yJ2 + zf8ids3/InbN/yJ2zf8NsOH/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8U2fH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfP5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6FvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIShXwCDoe0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ijar/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/J971/wDU7f8A1O3/ANTt/zTd8f9B3/L/Q9/y/0Tf8v9G4PL/SODy/0rg8v9M4fL/TeHy/0/h + 8/9S4vP/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2rm + 9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Xo9f936PX/eOj1/3rp9v986fb/fen2/3/p9v+B6vb/gur2/4Tq + 9v+G6/b/h+v3/4nr9/+L6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt+P+Y7vj/mO74/5ru + +P+a7vj/m+74/5zu+P+c7vj/ne74/57v+P+e7/j/nu/4/57v+P+d7vj/ne74/5zu+P+c7vj/m+74/5ru + +P+a7vj/mO74/5ju+P+W7fj/le34/5Pt9/+S7ff/kOz3/4/s9/+O7Pf/jOz3/4vr9/+J6/f/h+v3/4br + 9v9Mt/X/Pq/8/z6v/P8+r/z/Pq/8/z+v/P9CsPz/RbH8/0iz/P9KtPz/TbX8/1az8v94fX//AQEB/wAA + AP98gID/ys/P/8zR0f/O09P/0NXV/9LW1v/U2Nj/1tra/9fb2//Z3d3/2t7e/7SIZf+kZTT/pGU0/6Rl + NP+kZTT/pGU0/2Y/IP8AAAD/AAAA/yAgIP80NDT/Nzc3/zo6Ov89PT3/QUFB/0RERP9HR0f/SkpK/01N + Tf+3kHH/vpFu/6ZoOP+kZTT/pGU0/6RlNP/Mspz////////////////////////////+/v//IXXL/yF1 + y/8hdcv/IXXL/yF1y/8gdMv/C7Ph/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/MeH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8bpsD/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIShNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAISgugCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8fqsT/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0zo+/8K1+//ANTt/wDU7f8d2e//Qd/y/0Pf8v9F4PL/R+Dy/0jg8v9K4PL/TeHy/07h + 8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm + 9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p9v+A6vb/gur2/4Tq + 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Lt9/+U7ff/le34/5ft+P+Y7vj/me74/5ru + +P+b7vj/nO74/53u+P+e7/j/n+/4/5/v+P+g7/j/oO/4/6Dv+P+g7/j/oO/4/5/v+P+f7/j/nu/4/53u + +P+c7vj/m+74/5ru+P+Z7vj/mO74/5bt+P+V7fj/k+33/5Lt9/+Q7Pf/j+z3/43s9/+L6/f/iuv3/4nr + 9/+H6/f/Tbj2/z6v/P8+r/z/Pq/8/z6v/P9BsPz/RLH8/0ez/P9KtPz/TbX8/1C2/P9St/z/L0JQ/wAA + AP8AAAD/FRYW/8jOzv/O09P/0NXV/9LW1v/U2Nj/1tra/9jc3P/Z3d3/29/f/9zg4P+9mn7/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+RWS7/AwIB/xQUFP9SUlL/VVVV/1hYWP9bW1v/Xl5e/2FhYf9lZWX/aGho/2tr + a/9/eXT/zKmO/86skf+7jGf/pGU0/6RlNP+kZTT/2cm7////////////////////////////9vn7/x9z + yv8fc8r/H3PK/x9zyv8fc8r/H3PK/wm34/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/A9Xu/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5vn/Aoak/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh8gCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAhKFyAIOh9QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuSrv870OX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/N+P3/wDU7f8A1O3/BtXt/0Hf8v9D3/L/ReDy/0jg8v9J4PL/S+Hy/03h + 8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9l5fT/Z+X0/2jm + 9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo9f976fb/fOn2/37p9v9/6fb/ger2/4Pq + 9v+E6vb/h+v3/4jr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu + +P+c7vj/ne74/5/v+P+f7/j/oO/4/6Hv+P+h7/j/ou/4/6Lv+P+i7/j/ou/4/6Lv+P+h7/j/oe/4/6Dv + +P+f7/j/nu/4/53u+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jez3/4vr + 9/+J6/f/h+v3/1S/9v8+r/z/Pq/8/z6v/P9AsPz/Q7H8/0ay/P9KtPz/TbX8/1C2/P9Tt/z/RpnR/wED + BP8AAAD/BwcH/wAAAP9xdHT/z9TU/9HW1v/T2Nj/1tra/9jc3P/a3t7/3N/f/97h4f/f4uL/zLmp/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/0IoFf9AQED/cnJy/3Z2dv95eXn/fHx8/39/f/+CgoL/hoaG/4mJ + if+MjIz/wa6f/9i9qP/Zv6v/07We/6RlNP+kZTT/pGY1//Hu6////////////////////////////+Ts + 8/8dcsn/HXLJ/x1yyf8dcsn/HXLJ/x1yyf8Gweb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/x3c8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/L83k/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoS4Ag6HLAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AYWi/yWzzP9M5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8X2/H/ANTt/wDU7f8v3PD/RN/y/0bg8v9I4PL/SuDy/0zh + 8v9N4fL/T+Hz/1Hi8/9T4vP/VeLz/1bj8/9Z4/P/WuPz/1zk8/9e5PT/YOT0/2Ll9P9j5fT/ZeX0/2fl + 9P9p5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/duj1/3jo9f956Pb/e+n2/33p9v9/6fb/gOr2/4Lq + 9v+E6vb/huv2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+T7ff/le34/5ft+P+Y7vj/mu74/5vu + +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+k8Pn/pfD5/6Tw+f+k8Pn/pPD5/6Pv + +f+i7/j/oe/4/6Dv+P+f7/j/ne74/5zu+P+a7vj/me74/5ju+P+V7fj/lO33/5Pt9/+R7Pf/j+z3/47s + 9/+L6/f/iuv3/4jr9/9gy/n/Pq/8/z6v/P8/r/z/QrD8/0ay/P9Js/z/TLX8/0+2/P9St/z/Vbn8/xcy + Q/8AAAD/FyMr/xscHP8AAAD/EBER/8nNzf/T2Nj/1dra/9jc3P/a3t7/3N/f/97h4f/g4+P/4eTk/+De + 2/+laDn/pGU0/6RlNP+kZTT/pGU0/6RlNP+XXTD/dW1n/5OTk/+Wlpb/mpqa/52dnf+goKD/o6Oj/6am + pv+qqqr/wrq0/+PPwP/k0cL/5dPF/+TRwv+kZTT/pGU0/6+BXf/+/v7///////////////////////// + ///F1+r/HHDH/xxwx/8ccMf/HHDH/xxwx/8ccMf/As7r/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f854/j/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKjvf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqFsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICfCACCoYUAg6H6AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/D5ey/0DV6/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Reb6/wLU7v8A1O3/Fdju/0Xg8v9G4PL/SODy/0rg + 8v9M4fL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl + 9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq + 9v+D6vb/hOr2/4fr9/+I6/f/iuv3/4vr9/+O7Pf/j+z3/5Hs9/+T7ff/le34/5bt+P+Y7vj/mu74/5vu + +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+k8Pn/pfD5/6bw+f+m8Pn/p/D5/6fw+f+m8Pn/pvD5/6bw + +f+l8Pn/pPD5/6Pv+f+h7/j/oO/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt+P+T7ff/ku33/5Ds + 9/+O7Pf/jOz3/4vr9/+J6/f/atT5/z6v/P8+r/z/QbD8/0Wx/P9Is/z/S7T8/0+2/P9St/z/Vbj8/zt9 + qv8AAAD/AQID/0uRwv8TFxn/AAAA/wAAAP9gYmL/1NnZ/9fb2//Z3d3/29/f/97h4f/g4+P/4uXl/+Tn + 5//l6Oj/u5Jx/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/8Kcfv/Gv7r/t7e3/7u7u/++vr7/wcHB/8TE + xP/JyMj/39fR/+7h2P/v49r/8OXd//Hn3//k0sP/pGU0/6RlNP/EsJ7///////////////////////// + ////////kLbd/xpvxv8ab8b/Gm/G/xpvxv8ab8b/F3rK/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8J1+//Tun8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Tk+P8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISgPgCDofsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Boqo/ym40P8ntc7/JbPM/yWzzP8ls8z/JbPM/yWzzP8ir8n/IKzG/yCsxv8grMb/IKzG/yCs + xv8dqcP/GqW//xqlv/8apb//GqW//xqlv/8Yor3/FZ65/xWeuf8Vnrn/FZ65/xWeuf8TnLf/EJiz/xCY + s/8QmLP/EJiz/xCYs/8PlrH/C5Gu/wuRrv8Lka7/C5Gu/wuRrv8Fjqv/AIim/wCNq/8dr8j/RNzv/0ng + 8v9K4PL/TeHy/07h8/9Q4fP/UuLz/1Ti8/9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9h5PT/YuX0/2Xl + 9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo9f936PX/eOj1/3vp9v996fb/fun2/4Dq + 9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/lO33/5Xt+P+X7fj/me74/5ru + +P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw+f+l8Pn/pvD5/6fw+f+o8Pn/qPD5/6nw+f+p8Pn/qfD5/6jw + +f+o8Pn/pvD5/6bw+f+k8Pn/o+/5/6Hv+P+g7/j/n+/4/5zu+P+b7vj/mu74/5ju+P+W7fj/lO33/5Pt + 9/+Q7Pf/j+z3/43s9/+L6/f/iev3/3je+P8+r/z/P6/8/0Ox/P9Gsvz/SrT8/061/P9Rt/z/VLj8/02k + 3/8HDhP/AAAA/zNkhf9jvvz/BgsO/wAAAP8BAgL/CwwM/77Cwv/Y3Nz/2t7e/93h4f/f4+P/4eTk/+Tn + 5//m6Oj/6Orq/+Da0/+laTr/pGU0/6RlNP+kZTT/pGU0/6RlNP+xe1H/7+Tb/+7m3//p5N//6eXi/+vo + 5f/w7On/9vHt//jz7//59fL/+vf1//v59//9+/n/zKmO/6RlNP+selL/6+3s//////////////////// + /////////////0KHzf8ZbsX/GW3F/xltxf8ZbcX/GW3F/xGO0f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Jd70/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8myuL/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh4QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKiaACDof4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmj + vf9J4PL/S+Hy/03h8v9P4fP/UeLz/1Li8/9U4vP/VuPz/1jj8/9a4/P/W+Pz/17k9P9g5PT/YeT0/2Pl + 9P9l5fT/Z+X0/2nm9P9q5vT/bOb1/27n9f9w5/X/cuf1/3Pn9f926PX/eOj1/3no9v976fb/fen2/3/p + 9v+B6vb/gur2/4Tq9v+G6/b/iOv3/4nr9/+L6/f/juz3/4/s9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5ru + +P+c7vj/ne74/5/v+P+h7/j/ou/4/6Tw+f+m8Pn/p/D5/6jw+f+p8Pn/q/H5/6vx+f+r8fn/q/H5/6vx + +f+r8fn/qfD5/6jw+f+n8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v+P+d7vj/nO74/5ru+P+Y7vj/l+34/5Xt + +P+T7ff/kez3/4/s9/+O7Pf/i+v3/4rr9/+F6ff/Pq/8/0Gw/P9Fsfz/SLP8/0y1/P9Ptvz/U7j8/1a3 + +f8VLDv/AQEC/zJigv9jvvz/Wqnf/wAAAP8CAwP/IiMj/wAAAP9MTk7/19vb/9zf3//e4uL/4eTk/+Pm + 5v/l6Oj/6Orq/+rs7P/s7u7/0Lek/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/9Cvlv/9+/r//v39//// + ////////////////////////////////////////6tvQ/6ZoOP+mbD7/ysS8//39/f////////////// + //////////////L1+P8XbMP/F2zD/xdsw/8XbMP/F2zD/xdsw/8KqNz/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0Lm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Cp66/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIWfMACDofwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Qtjr/0vh8v9N4fL/T+Hz/1Hi8/9T4vP/VOLz/1bj8/9Y4/P/WuPz/1zk8/9e5PT/YOT0/2Ll + 9P9j5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9v5/X/cef1/3Ln9f906PX/duj1/3jo9f966fb/e+n2/33p + 9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/l+34/5nu + +P+a7vj/nO74/5/v+P+g7/j/oe/4/6Tw+f+l8Pn/p/D5/6jw+f+q8fn/q/H5/6zx+f+t8fn/rfH5/63x + +f+t8fn/rPH5/6vx+f+q8fn/qPD5/6fw+f+l8Pn/pPD5/6Hv+P+g7/j/nu/4/5zu+P+a7vj/me74/5ft + +P+V7fj/k+33/5Lt9/+Q7Pf/juz3/4zs9/+K6/f/iev3/0m2+v9Dsfz/RrL8/0q0/P9Otfz/Ubf8/1W4 + /P8lT2v/ESIt/0mRwf9jvvz/Zb/8/0mHsP8AAAD/FCIs/0FKUP8AAAD/BgYG/4GDg//d4OD/3+Pj/+Ll + 5f/k5+f/5+np/+nr6//r7e3/7e/v//Dx8f/Go4f/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/zKmO//z6 + +f//////////////////////////////////////4Mq5/6ltPv+lZzb/xbep/+fq6v////////////// + //////////////////+0zOX/FWrC/xVqwv8VasL/FWrC/xVqwv8VasL/A8Tm/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xDY8P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O+H2/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/z3P4/9L4fL/TeHy/0/h8/9R4vP/U+Lz/1Xi8/9W4/P/WePz/1vj8/9c5PP/XuT0/2Dk + 9P9i5fT/ZOX0/2Xl9P9n5fT/aub0/2vm9f9t5vX/b+f1/3Hn9f9z5/X/dOj1/3bo9f946PX/eun2/3zp + 9v9+6fb/f+n2/4Lq9v+D6vb/her2/4fr9/+J6/f/i+v3/43s9/+O7Pf/kOz3/5Lt9/+U7ff/lu34/5ju + +P+a7vj/m+74/53u+P+f7/j/oe/4/6Pv+f+k8Pn/pvD5/6jw+f+q8fn/q/H5/63x+f+u8fn/sPL5/7Dy + +f+w8vn/r/L5/67x+f+t8fn/q/H5/6nw+f+o8Pn/pvD5/6Tw+f+i7/j/oe/4/5/v+P+d7vj/m+74/5nu + +P+Y7vj/le34/5Tt9/+S7ff/kOz3/47s9/+M7Pf/i+v3/4nr9/9eyfr/RLH8/0iz/P9Mtfz/T7b8/1O4 + /P8ya5H/NW2T/125+P9hvvz/Zb/8/2jA/P8sUGj/AAAA/zJXcP9Qfp3/AAAA/wAAAP8KCgr/srW1/+Dk + 5P/j5ub/5ejo/+jq6v/q7e3/7e/v/+/x8f/y8/P/8/Pz/8SdgP+kZTT/pGU0/6RlNP+kZTT/pGU0/6Rl + NP+udkr/0rOb/+vd0v/28Ov/+PPv/+/k3P/awa3/t4Zf/6RlNP+maTn/xK+e/9fb2//8/Pz///////// + ////////////////////////RojM/xRpwf8UacH/FGnB/xRpwf8UacH/EnTG/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8u4PX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x7E + 3f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HrAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wGNqv9H3/H/TOHy/03h8v9P4fP/UuLz/1Pi8/9V4vP/V+Pz/1nj8/9b4/P/XOTz/17k + 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3Xo9f926PX/eOj1/3vp + 9v986fb/fun2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Ds9/+T7ff/le34/5bt + +P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx+f+u8fn/sPL5/7Hy + +f+y8vr/svL6/7Hy+f+w8vn/rfH5/6zx+f+q8fn/qPD5/6bw+f+k8Pn/o+/5/6Hv+P+f7/j/ne74/5vu + +P+a7vj/mO74/5Xt+P+U7ff/ku33/5Ds9/+O7Pf/jOz3/4vr9/+J6/f/dtz4/0ay/P9KtPz/TrX8/1G3 + /P9Rr+//U67s/1y7/P9gvfz/ZL/8/2fA/P9ovvj/DBUb/wAAAP9ZmcT/Xp7J/wAAAP8AAAD/AAAA/x8f + H//S1dX/4+bm/+bp6f/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+Pj/2MCt/6xyRv+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+vflf/zsS6/9bb2//v8fH///////// + ////////////////////////2+bw/xJowP8SZ7//Eme//xJnv/8SZ7//Eme//wqY1P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo + +/8ElLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICkDgCCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCLqf8Awt3/LNvw/0zh8v9O4fP/T+Hz/1Li8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/13k + 9P9e5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/23m9f9v5/X/cef1/3Pn9f916PX/d+j1/3jo + 9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s9/+R7Pf/k+33/5Xt + +P+X7fj/mO74/5ru+P+c7vj/nu/4/6Dv+P+h7/j/pPD5/6bw+f+n8Pn/qfD5/6vx+f+t8fn/r/L5/7Dy + +f+y8vr/tfP6/7Ty+v+y8vr/sPL5/67x+f+s8fn/qvH5/6jw+f+m8Pn/pPD5/6Pv+f+h7/j/n+/4/53u + +P+b7vj/mu74/5ju+P+V7fj/lO33/5Lt9/+Q7Pf/juz3/4zs9/+L6/f/iev3/4bq9/9KtPv/S7T8/0+2 + /P9Tt/z/V7n8/1q7/P9evPz/Yr78/2a//P9pwfz/SIGn/wAAAP8ZKzj/dsb9/2Sk0P8AAAD/AAAA/wAA + AP8AAAD/Ozw8/+Ll5f/n6en/6evr/+zu7v/v8PD/8fPz//T19f/39/f/+fr6//v8/P/59vP/07af/6x0 + SP+kZTT/pGU0/6RlNP+kZTT/pGU0/6RlNP+kZTT/pGU0/7F/WP/Muar/3N/f/9nd3f/k5+f///////// + /////////////////////////////1mSz/8RZr7/EWa+/xFmvv8RZr7/EWa+/xFmvv8DweX/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdry/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8z2/H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFoUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKEbAISimQCDof8Ag6H/AIOh/wCE + ot0Ag6HTAIOhzACDocwAg6HMAIOhzACDocwAg6HEAIOhuwCDobsAg6G7AIOhuwCDobsAgqK0AIOhqgCD + oaoAg6GqAIOhqgCDoaoAg6KkAISimQCEopkAhKKZAISimQCDob0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/D67I/yq50f8qudH/KrnR/yq50f8rutL/L8DX/y/A1/8ivNX/ALPO/wCzzv8AtM//ALnU/wC5 + 1P8AudT/ALnU/wC92P8A0uv/ANTt/wfV7f9J4PL/TuHz/0/h8/9S4vP/VOLz/1Xi8/9X4/P/WePz/1vj + 8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3fo + 9f946PX/e+n2/33p9v9+6fb/gOr2/4Lq9v+E6vb/huv2/4jr9/+J6/f/i+v3/43s9/+P7Pf/kez3/5Pt + 9/+V7fj/l+34/5ju+P+a7vj/nO74/57v+P+g7/j/oe/4/6Tw+f+m8Pn/p/D5/6nw+f+r8fn/rfH5/6/y + +f+w8vn/svL6/7Py+v+y8vr/sfL5/7Dy+f+t8fn/rPH5/6rx+f+o8Pn/pvD5/6Tw+f+j7/n/oe/4/5/v + +P+d7vj/m+74/5nu+P+Y7vj/le34/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iuv3/4nr9/+H6/f/Ysr5/0y1 + /P9Qtvz/VLj8/1i6/P9cu/z/YL38/2S//P9nwPz/ab/4/xIgKv8AAQH/V5S9/3jH/f9mpc//AAAA/wAA + AP8AAAD/AAAA/wAAAP9eYGD/5+np/+nr6//s7u7/7/Dw//Hz8//09fX/9/f3//n6+v/8/Pz//f39//z8 + /P/4+fj/5NbL/86ymv/Foob/vpRz/76Xd//DoYf/y7Ke/9zVzv/h5OT/3+Li/9zg4P/g4+P//f39//// + /////////////////////////////9Df7P8PZb3/D2W9/w9lvf8PZb3/D2W9/w9lvf8Ofcf/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zbj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/FrfQ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoewAgKoGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJkKAIOh8gCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wKnwv9I5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w3X8P8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/J9vw/07h8/9P4fP/UuLz/1Ti8/9V4vP/V+Pz/1nj + 8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl9P9m5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3Xo + 9f936PX/eOj1/3vp9v996fb/fun2/4Dq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+N7Pf/j+z3/5Hs + 9/+T7ff/le34/5bt+P+Y7vj/mu74/5zu+P+e7/j/n+/4/6Hv+P+k8Pn/pfD5/6fw+f+o8Pn/q/H5/6zx + +f+t8fn/r/L5/7Dy+f+x8vn/sPL5/7Dy+f+u8fn/rfH5/6vx+f+p8Pn/p/D5/6bw+f+k8Pn/ou/4/6Dv + +P+f7/j/nO74/5ru+P+Z7vj/l+34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr9/+I6/f/h+v3/3rg + 9/9Ntfz/Ubf8/1W5/P9auvz/Xrz8/2G+/P9lv/z/acH8/zxri/8AAAD/M1dw/3jH/f97yP3/XZW7/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AgIC/4CCgv/p6+v/6+3t/+7w8P/x8vL/8/T0//X29v/4+fn/+vr6//r7 + +//6+vr/+Pn5//X29v/z9PT/8fLy/+7w8P/r7e3/6evr/+bp6f/k5+f/4eTk/97i4v/i5eX/+/z8//// + //////////////////////////////r7/P8sdsP/DmO8/w5jvP8OY7z/DWO7/w1ju/8NY7v/BbPe/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v9N6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/R+b6/wGIp/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GSAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoaAAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AiKX/Kd3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8y4fb/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7f9I4PL/T+Hz/1Li8/9U4vP/VeLz/1fj + 8/9Z4/P/W+Pz/13k9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2jm9P9q5vT/bOb1/27n9f9v5/X/cef1/3Pn + 9f916PX/d+j1/3jo9f976fb/fen2/37p9v+A6vb/gur2/4Tq9v+G6/b/h+v3/4nr9/+L6/f/jez3/4/s + 9/+Q7Pf/k+33/5Xt+P+W7fj/mO74/5ru+P+c7vj/ne74/5/v+P+h7/j/o+/5/6Tw+f+m8Pn/qPD5/6nw + +f+r8fn/rPH5/63x+f+u8fn/rvH5/67x+f+t8fn/rfH5/6vx+f+q8fn/qPD5/6bw+f+l8Pn/o+/5/6Hv + +P+f7/j/nu/4/5zu+P+a7vj/mO74/5ft+P+V7fj/k+33/5Hs9/+P7Pf/juz3/4vr9/+J6/f/iOv3/4br + 9v+E6vb/XMT6/1O3/P9Xufz/Wrv8/1+8/P9jvvz/Z8D8/1mi0v8EBwn/IzxN/3XE+v96yP3/fcn9/1B+ + nf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/nZ+f/+rt7f/t7+//7/Hx//Lz8//09fX/9fb2//f4 + +P/3+Pj/9/j4//b39//09fX/8vPz//Dx8f/t7+//6+3t/+jq6v/l6Oj/4+bm/+Dk5P/n6en//f39//// + //////////////////////////////////92pNL/DGK7/wxiuv8MYrr/DGK6/wxiuv8MYrr/DXvF/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8h3fT/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/yrQ5v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhMQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wm41P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tuj8/wrW + 7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/JNrw/0/h8/9S4vP/VOLz/1Xi + 8/9X4/P/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/ZOX0/2bl9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Hn + 9f9z5/X/dej1/3fo9f946PX/e+n2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+J6/f/i+v3/43s + 9/+O7Pf/kOz3/5Lt9/+U7ff/le34/5ju+P+a7vj/m+74/5zu+P+f7/j/oO/4/6Lv+P+k8Pn/pfD5/6bw + +f+o8Pn/qfD5/6vx+f+r8fn/rPH5/6zx+f+s8fn/q/H5/6vx+f+p8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv + +P+g7/j/n+/4/53u+P+b7vj/mu74/5ju+P+W7fj/lO33/5Lt9/+Q7Pf/juz3/43s9/+L6/f/iev3/4fr + 9/+F6vb/hOr2/3be9/9TuPz/V7n8/1y7/P9gvfz/ZL/8/2S48f8SISr/KUhd/3HA9/94x/3/fMn9/3/K + /f82VWn/AAAA/wsRFf8AAAD/AAAA/wAAAP8AAAD/AAAA/wsLC/+lqKj/6+3t/+7w8P/w8fH/8vPz//P0 + 9P/09fX/9Pb2//T19f/z9PT/8vPz//Dx8f/u8PD/7O7u/+ns7P/n6ur/5Ofn/+Pm5v/x8vL///////// + //////////////////////////////////+1zOL/CmC5/wpguf8KYLn/CmC5/wpguf8KYLn/CmG6/w69 + 4/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Tuj8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8Nnrv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh0QAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCC + odcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AkK3/NuL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wPV7f9G4PL/UuLz/1Ti + 8/9V4vP/V+Pz/1nj8/9b4/P/XeT0/17k9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2rm9P9s5vX/beb1/2/n + 9f9x5/X/c+f1/3Xo9f926PX/eOj1/3rp9v986fb/fun2/3/p9v+C6vb/g+r2/4Xq9v+H6/f/iev3/4vr + 9/+M7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/me74/5ru+P+c7vj/nu/4/5/v+P+h7/j/ou/4/6Tw + +f+l8Pn/pvD5/6jw+f+o8Pn/qfD5/6rx+f+q8fn/qvH5/6nw+f+o8Pn/qPD5/6bw+f+l8Pn/pPD5/6Lv + +P+h7/j/n+/4/53u+P+c7vj/mu74/5ju+P+X7fj/le34/5Pt9/+R7Pf/kOz3/47s9/+M7Pf/iuv3/4nr + 9/+H6/f/her2/4Pq9v+B6vb/Ysj6/1i6/P9cu/z/Yb38/2W++/8pTGP/RHmc/3HE/f91xv3/ecj9/33J + /f+By/3/FSAo/wAAAP8oOkf/AAAA/wAAAP8OFBj/Exgb/wAAAP8AAAD/DQ0N/6utrf/s7u7/7e/v/+/x + 8f/w8vL/8fPz//Hz8//x8/P/8PLy/+/x8f/u8PD/7O7u/+rs7P/o6ur/5ejo/+vt7f/6+/v///////// + ///////////////////////////////////R3ej/C2C5/wlfuP8JX7j/CV+4/wlfuP8JX7j/CV+4/xCS + z/8J1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8/4PX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoHEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAe5eHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xDG3v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/TOj7/wjW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gdjv/1Li + 8/9T4vP/VeLz/1fj8/9Z4/P/W+Pz/1zk8/9e5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl9P9q5vT/a+b1/23m + 9f9v5/X/cef1/3Pn9f906PX/duj1/3jo9f966fb/e+n2/33p9v9/6fb/ger2/4Pq9v+E6vb/h+v3/4jr + 9/+K6/f/i+v3/47s9/+P7Pf/kez3/5Pt9/+V7fj/lu34/5ju+P+a7vj/m+74/5zu+P+e7/j/oO/4/6Hv + +P+i7/j/pPD5/6Xw+f+m8Pn/pvD5/6fw+f+o8Pn/qPD5/6jw+f+n8Pn/pvD5/6bw+f+k8Pn/pPD5/6Lv + +P+h7/j/n+/4/57v+P+c7vj/mu74/5nu+P+Y7vj/le34/5Tt9/+T7ff/kOz3/4/s9/+N7Pf/i+v3/4nr + 9/+I6/f/huv2/4Tq9v+C6vb/ger2/3nj9/9au/z/Xbz8/2G+/P9Un9L/Xqvf/27D/P9yxf3/dsb9/3rI + /f9+yv3/barS/wAAAP8JDhH/P11x/wAAAP8AAAD/GSQr/0NecP8AAAD/AAAA/wAAAP8PDw//paam/+vt + 7f/s7u7/7e/v/+7w8P/v8PD/7vDw/+3v7//s7u7/6+3t/+rs7P/p6+v/7vDw//j5+f////////////// + ///////////////////////////////////a5Oz/EGK4/wddt/8HXbf/B123/wddt/8HXbb/B122/wpw + vv8Y0uz/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zvk+P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Ir3V/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofMAgJ8QAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAXHEkAF1y6QCBnv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Al7P/OuT4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8u4Pb/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f853vH/U+Lz/1Xi8/9W4/P/WePz/1rj8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aeb0/2vm + 9f9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo9f946PX/eej2/3vp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4br + 9v+H6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+S7ff/k+33/5Xt+P+X7fj/mO74/5ru+P+c7vj/ne74/5/v + +P+f7/j/oe/4/6Lv+P+j7/n/pPD5/6Tw+f+l8Pn/pfD5/6bw+f+l8Pn/pfD5/6Tw+f+k8Pn/o+/5/6Hv + +P+h7/j/n+/4/57v+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/kez3/5Ds9/+O7Pf/jOz3/4rr + 9/+J6/f/h+v3/4Xq9v+D6vb/gur2/3/p9v9+6fb/bNP5/168/P9ivvz/ZsD8/2rB/P9vw/z/c8X9/3fH + /f97yP3/gMr9/zRRZP8AAAD/Vn+b/zxXaf8AAAD/AAAA/yo6Rf91o8D/AAAA/wAAAP8AAAD/AAAA/wwM + DP+RkZH/7/Hx/+7w8P/t7+//7O7u/+3v7//u8PD/8PLy//T19f/4+fn//v7+//////////////////// + ///////////////////////////////////M2uj/DmG3/wZctf8GXLX/Bly1/wVctf8FXLX/BVy1/wZi + t/8Xw+b/FNfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xPZ8P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Tej7/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6CUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW20cAFpv4QBab/8Acoz/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xTL4/9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Tej8/wvX7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Ctbu/0/h8/9U4vP/VuPz/1jj8/9a4/P/XOTz/17k9P9g5PT/YeT0/2Pl9P9l5fT/Z+X0/2nm + 9P9q5vT/bOb1/27n9f9w5/X/cef1/3Pn9f926PX/d+j1/3no9v976fb/fen2/37p9v+A6vb/gur2/4Tq + 9v+F6vb/h+v3/4nr9/+K6/f/jOz3/47s9/+P7Pf/kez3/5Pt9/+U7ff/le34/5ju+P+Z7vj/mu74/5zu + +P+c7vj/nu/4/5/v+P+g7/j/oe/4/6Lv+P+i7/j/o+/5/6Pv+f+j7/n/o+/5/6Pv+f+i7/j/oe/4/6Hv + +P+f7/j/n+/4/53u+P+c7vj/m+74/5ru+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+O7Pf/jez3/4vr + 9/+J6/f/iOv3/4br9v+E6vb/gur2/4Hq9v9/6fb/fen2/3vp9v9myPv/Yr78/2fA/P9rwvz/b8P8/3PF + /f93x/3/fMn9/3e97P8FBwn/KT5M/43Q/f8wRVP/AAAA/wAAAP9HYXP/ndX7/wwRFP8AAAD/AAAA/wAA + AP8AAAD/BAQE/11dXf/29vb///////////////////////////////////////////////////////// + //////////////////////////////3+/v+nwtz/BVy0/wRbtP8EWrT/BFq0/wRatP8EWrT/BFq0/wRc + tf8Us93/Gtjv/wvW7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f885Pn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y7K4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIakKgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVW0VAFpv2gBab/8AWm//AGF3/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Amrf/PuX5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P804vf/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8m2vD/VOLz/1bj8/9Y4/P/WuPz/1vj8/9e5PT/X+T0/2Hk9P9i5fT/ZeX0/2bl + 9P9o5vT/aub0/2zm9f9u5/X/b+f1/3Hn9f9z5/X/dej1/3bo9f946PX/eun2/3zp9v996fb/f+n2/4Hq + 9v+C6vb/hOr2/4br9v+I6/f/iev3/4vr9/+N7Pf/juz3/5Ds9/+R7Pf/k+33/5Xt+P+W7fj/mO74/5nu + +P+a7vj/m+74/5zu+P+d7vj/nu/4/5/v+P+g7/j/oO/4/6Hv+P+h7/j/oe/4/6Hv+P+h7/j/oO/4/5/v + +P+f7/j/nu/4/5zu+P+c7vj/mu74/5nu+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/jez3/4vr + 9/+K6/f/iOv3/4fr9/+F6vb/g+r2/4Lq9v9/6fb/fun2/33p9v976fb/duP3/2TB+/9nwPz/a8L8/2/D + /P9zxf3/eMf9/3zJ/f86XHP/DxYc/3275v+N0P3/Hyw2/wAAAP8AAAD/a5Ks/6HY/f8yRFD/AAAA/wAA + AP8KDQ//AAAA/wAAAP8AAAD/Dg4O/4KCgv/n5+f///////////////////////////////////////// + //////////////////////////////Dz9v9Wjcf/A1mz/wJZs/8CWbP/Almz/wJZs/8CWbP/Almz/wJa + s/8Tpdb/HNnv/xrY7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Y2vH/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p+/8KkK3/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIShuAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVXcPAFpv0gBab/8AWm//AFpv/wBab/8AdpH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xXO + 5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w/Y8P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AtTt/0Xg8v9W4/P/V+Pz/1nj8/9b4/P/XeT0/1/k9P9g5PT/YuX0/2Tl + 9P9m5fT/Z+X0/2rm9P9r5vX/beb1/2/n9f9x5/X/cuf1/3To9f926PX/eOj1/3no9v976fb/fen2/3/p + 9v+A6vb/gur2/4Tq9v+F6vb/h+v3/4nr9/+K6/f/i+v3/43s9/+P7Pf/kOz3/5Lt9/+T7ff/le34/5bt + +P+X7fj/mO74/5ru+P+a7vj/m+74/5zu+P+d7vj/nu/4/57v+P+f7/j/n+/4/5/v+P+f7/j/nu/4/57v + +P+d7vj/nO74/5zu+P+a7vj/mu74/5ju+P+Y7vj/lu34/5Xt+P+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs + 9/+K6/f/iev3/4fr9/+F6vb/hOr2/4Lq9v+A6vb/f+n2/33p9v976fb/eun2/3jo9f9y4Pb/aMP7/2vC + /P9vw/z/c8X9/3fH/f9np9L/DRUa/3Gu2P+Jzv3/jdD9/wsQE/8AAAD/BwkL/5fP8/+e1/3/VnWL/wAA + AP8AAAD/P1tu/wAAAP8AAAD/AAAA/wAAAP8AAAD/AgIC/zY2Nv97e3v/vLy8//b29v////////////// + ////////////////////////+fr6/6jD3P8NX7T/AVix/wFYsf8BWLH/AVix/wFYsf8BWLH/AVix/wFY + sf8RndL/Hdnv/xvZ7/8W2O//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/Qeb6/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8xzeT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + okIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAXXQLAFtvyABab/8AWm//AFpv/wBab/8AWm//AGJ5/wCCoP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ambb/OuP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P885Pn/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTs/wCuyf8DkK3/DJGt/yqxyf9X4fD/W+Pz/1zk8/9e5PT/YOT0/2Ll + 9P9k5fT/ZeX0/2fl9P9p5vT/a+b1/2zm9f9u5/X/cOf1/3Hn9f9z5/X/dej1/3fo9f946PX/e+n2/3zp + 9v996fb/f+n2/4Hq9v+C6vb/hOr2/4br9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/ku33/5Pt + 9/+U7ff/le34/5ft+P+Y7vj/mO74/5ru+P+a7vj/m+74/5zu+P+c7vj/nO74/5zu+P+c7vj/nO74/5zu + +P+c7vj/m+74/5ru+P+a7vj/me74/5ju+P+X7fj/le34/5Tt9/+T7ff/ku33/5Ds9/+P7Pf/juz3/4zs + 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Lq9v+B6vb/f+n2/33p9v986fb/e+n2/3jo9f936PX/dej1/2/d + 9/9rwfz/b8P8/3PF/f90wvf/JDpJ/2ahyf+EzP3/iM79/4C95/8AAAD/AAAA/z5WZ/+a1f3/m9b9/3ak + wv8AAAD/AAAA/2eWtv8OFRr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ICAj/Pj4+/2xs + bP+YmJj/tLS0/8rKyv/Fxsb/mKq7/xpRi/8APHv/AFSr/wBXsf8AV7H/AFex/wBXsf8AV7H/AFex/wBY + sf8RndL/H9nv/xzZ7/8b2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/INzz/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N6Pv/CpGu/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ocQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAASW0HAFpvvQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8GgZz/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/xDH4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x3c8/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wClwP8Ag6H/AIOh/wCDof8Ag6H/I6nB/1rj8/9c5PP/XuT0/2Dk + 9P9h5PT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n9f9x5/X/c+f1/3To9f926PX/eOj1/3no + 9v976fb/fen2/37p9v+A6vb/gur2/4Pq9v+E6vb/huv2/4jr9/+J6/f/i+v3/4zs9/+O7Pf/j+z3/5Ds + 9/+R7Pf/k+33/5Tt9/+V7fj/le34/5ft+P+Y7vj/mO74/5nu+P+Z7vj/mu74/5ru+P+a7vj/mu74/5ru + +P+a7vj/me74/5nu+P+Y7vj/mO74/5ft+P+V7fj/le34/5Pt9/+T7ff/kez3/5Ds9/+P7Pf/juz3/4zs + 9/+L6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/f+n2/37p9v996fb/e+n2/3no9v946PX/duj1/3To + 9f9z5/X/btv3/27E/P9yxf3/Qm6N/2iq1/9/yv3/g8z9/4fN/f9klbf/AAAA/wYICv+Dut//l9T9/5fU + /f+Hv+T/AAAA/wAAAP9Ygp7/RGd+/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAIEf8AL1//AFKn/wBXsf8AV7H/AFex/wFb + s/8Wrdr/INnv/x7Z7/8c2e//Gtjv/wXV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0no + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/LMje/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAZmYFAFlvsQBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Qd4v/Ocrh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ak6//NeL3/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/BNXu/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wC81v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/9W3+//W+Pz/13k + 9P9f5PT/YOT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Ln9f9z5/X/dej1/3fo + 9f946PX/eun2/3zp9v996fb/f+n2/4Dq9v+C6vb/hOr2/4Xq9v+H6/f/iOv3/4nr9/+L6/f/jOz3/43s + 9/+O7Pf/kOz3/5Hs9/+S7ff/k+33/5Tt9/+V7fj/le34/5bt+P+X7fj/l+34/5ju+P+Y7vj/mO74/5ju + +P+Y7vj/mO74/5ft+P+X7fj/lu34/5Xt+P+V7fj/lO33/5Pt9/+S7ff/kOz3/5Ds9/+O7Pf/jez3/4vr + 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/e+n2/3rp9v946PX/duj1/3Xo + 9f9z5/X/cef1/3Dn9f9u3/b/XqbT/2y36v95yP3/fcn9/4HL/f+Fzf3/RmqC/wAAAP9Ue5X/kdL9/5PS + /f+U0/3/kdD6/wIDBP8AAAD/UnqV/3m34P8BAgL/AAAA/wAAAP8BAgL/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAGDP8AMmf/AFew/wVq + uv8cwOL/Idrv/x/Z7/8d2e//HNnv/xbY7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y/h + 9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Sub6/waLqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVVUDAFpupABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Ncob/SN3w/0/p/P8Xobv/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wq92P9N6fv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yzg + 9f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDO5v8Aiqf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Utvt/1vj + 8/9c5PP/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/buf1/2/n9f9x5/X/c+f1/3To + 9f926PX/eOj1/3no9v976fb/fOn2/33p9v9/6fb/ger2/4Lq9v+E6vb/her2/4fr9/+I6/f/iev3/4rr + 9/+L6/f/jez3/47s9/+P7Pf/kOz3/5Hs9/+S7ff/k+33/5Pt9/+U7ff/le34/5Xt+P+V7fj/le34/5Xt + +P+V7fj/le34/5Xt+P+V7fj/lO33/5Tt9/+T7ff/k+33/5Lt9/+Q7Pf/kOz3/4/s9/+O7Pf/jOz3/4vr + 9/+K6/f/iev3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3rp9v946PX/d+j1/3Xo + 9f9z5/X/cuf1/3Hn9f9v5/X/beb1/2vg9/9yyvv/eMf9/3zJ/f+Ayv3/g8z9/yAwPP8qP03/jND9/47Q + /f+Q0f3/kNH9/5DR/f8IDA//AAAA/1B4lP+Fzf3/Gyk0/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wQi + Mv8duM3/Itrv/yHa7/8f2e//Hdnv/xvZ7/8L1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xLZ + 8P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yW81f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABAFtvmABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8LboP/Rtns/0/p/P9P6fz/Qdfs/wGE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ajar/KN3z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9O6Pz/Edjw/wDU7f8A1O3/ANTt/wDU7f8AnLj/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/BpOv/1jj + 8/9a4/P/W+Pz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9q5vT/a+b1/2zm9f9v5/X/cOf1/3Hn + 9f9z5/X/dej1/3bo9f946PX/eej2/3vp9v996fb/fun2/3/p9v+B6vb/gur2/4Tq9v+F6vb/h+v3/4fr + 9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+P7Pf/kOz3/5Ds9/+R7Pf/ku33/5Pt9/+T7ff/k+33/5Pt + 9/+T7ff/k+33/5Pt9/+T7ff/k+33/5Lt9/+S7ff/kez3/5Ds9/+Q7Pf/juz3/47s9/+N7Pf/i+v3/4vr + 9/+J6/f/iOv3/4fr9/+G6/b/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v946PX/d+j1/3bo + 9f906PX/cuf1/3Hn9f9v5/X/buf1/2zm9f9q5vT/aeX0/3DS+v96yP3/fsn9/3e86v8THST/fb7q/4nO + /f+Lz/3/jND9/4zQ/f+Mz/3/AwQF/wAAAP9KcYz/g8z9/z5hev8AAAD/AAAA/wAAAP8RHCX/AAAA/wEC + Av8dNkf/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AgkK/xaQnf8g2e//Htnv/xzZ7/8Z2O//AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU + 7f9C5vr/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Ph9f8ChaT/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpwiwBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8Jan//RNXo/0/p/P9P6fz/T+n8/0/p + /P8eqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOrx/9F5vr/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0Lm+v8C1O3/ANTt/wDU7f8AtM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/zbQ + 5v833/L/WePz/1vj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9n5fT/aOb0/2rm9P9s5vX/beb1/2/n + 9f9x5/X/cuf1/3Pn9f916PX/d+j1/3jo9f966fb/e+n2/33p9v9+6fb/f+n2/4Hq9v+C6vb/hOr2/4Tq + 9v+G6/b/h+v3/4jr9/+J6/f/iuv3/4vr9/+M7Pf/jez3/47s9/+O7Pf/j+z3/5Ds9/+Q7Pf/kOz3/5Ds + 9/+R7Pf/kez3/5Hs9/+R7Pf/kOz3/5Ds9/+Q7Pf/j+z3/4/s9/+O7Pf/juz3/43s9/+L6/f/i+v3/4rr + 9/+J6/f/h+v3/4fr9/+F6vb/hOr2/4Pq9v+C6vb/gOr2/3/p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo + 9f906PX/c+f1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aeb0/2fl9P9m5fT/atv3/3nL/P9OfJz/aqfQ/4TM + /f+Gzf3/iM79/4nO/f+Jzv3/hMbz/wAAAP8AAAD/W46w/4HL/f9Wiq7/AAAA/wAAAP8AAAD/L1Ns/wAA + AP8AAAD/JEZc/zhvlP8BAwT/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAQH/EXWA/x3Z7/8c2e//ENfu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8p3/T/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8WpsD/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/gCEojQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvfABab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8HZnv/QtHl/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Q9js/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaP/GdLp/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/KN/0/wDU7f8AyOP/AIek/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ya0 + zv9P6fz/Ten7/0bj9v9a4/P/XOTz/17k9P9f5PT/YeT0/2Ll9P9k5fT/ZuX0/2fl9P9p5vT/aub0/2zm + 9f9u5/X/b+f1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3rp9v976fb/fen2/37p9v9/6fb/ger2/4Lq + 9v+D6vb/hOr2/4Xq9v+H6/f/h+v3/4nr9/+J6/f/iuv3/4vr9/+L6/f/jOz3/43s9/+O7Pf/juz3/47s + 9/+O7Pf/juz3/4/s9/+O7Pf/juz3/47s9/+O7Pf/juz3/43s9/+N7Pf/jOz3/4vr9/+L6/f/iev3/4nr + 9/+I6/f/h+v3/4br9v+E6vb/hOr2/4Lq9v+B6vb/f+n2/37p9v996fb/fOn2/3vp9v956Pb/d+j1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/2zm9f9r5vX/aub0/2jm9P9m5fT/ZeX0/2Pl9P9i4/T/XbXT/37L + /f+By/3/g8z9/4TM/f+Fzf3/hc39/2Wbv/8AAAD/AAAA/2uq1P9+yv3/aqzZ/wAAAP8AAAD/AAAA/1KS + vP8EBQb/AQEB/wEBAv9PnNH/P4Kv/wMGCP8AAAD/AAAA/wAAAP8BAQL/Dig6/wABAv8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8MW2T/G9ju/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8Q2PD/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P800un/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoakAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvbgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8FY3f/P8zg/0/p/P9P6fz/SuDz/ze/ + 0v8knLD/EnqO/wJdcv8Ab4n/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCYtP824vf/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/03o/P8P1+7/AJWy/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xCY + tP9N5/r/T+n8/0/p/P9P6fz/Tub4/1rj8/9d5PT/XuT0/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/aOb0/2rm + 9P9r5vX/bOb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3fo9f946PX/eun2/3vp9v996fb/fun2/3/p + 9v+A6vb/gur2/4Lq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+J6/f/iuv3/4rr9/+L6/f/i+v3/4vr + 9/+M7Pf/jOz3/4zs9/+M7Pf/jOz3/4zs9/+M7Pf/i+v3/4vr9/+L6/f/iuv3/4rr9/+J6/f/iev3/4fr + 9/+H6/f/huv2/4Xq9v+E6vb/g+r2/4Lq9v+A6vb/f+n2/37p9v996fb/e+n2/3rp9v946PX/d+j1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Pl9P9i5fT/YOT0/17k + 9P9h4fX/btX5/37L/P+By/3/gcv9/4LL/f9DaoT/AAAA/w4WHP98x/v/e8j9/3O+8v8AAAD/AAAA/wAA + AP9jsuf/KEBQ/w8PD/8NDQ3/Kk1l/1q6/P9Ci73/CQ0Q/wQEBP8DAwP/AQEB/wouM/8mtcb/E15n/wIJ + Cv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wheaP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8E1e7/ROb5/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J5/r/B46r/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDofUAgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpwYABab/4AWm//AFpv/wBab/8AWm//AFpv/wBab/8DX3X/NbrO/zW6zv8imKz/D3WJ/wFb + cP8AWm//AFpv/wBab/8AWm//AFtx/wB+mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CLzW/0rn + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/IrXO/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wOH + pf9D2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6Pv/WuT1/17k9P9f5PT/YOT0/2Ll9P9k5fT/ZeX0/2fl + 9P9o5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/c+f1/3To9f926PX/d+j1/3jo9f966fb/e+n2/3zp + 9v996fb/f+n2/3/p9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4br9v+H6/f/h+v3/4jr9/+I6/f/iev3/4nr + 9/+J6/f/iuv3/4rr9/+K6/f/iuv3/4rr9/+K6/f/iuv3/4nr9/+J6/f/iev3/4jr9/+I6/f/h+v3/4fr + 9/+F6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v9/6fb/f+n2/33p9v986fb/e+n2/3rp9v946PX/d+j1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/1/k + 9P9e5PT/W+Pz/1rj8/9b4vT/aNj3/3XP+v9+yv3/FyQu/wAAAP84W3L/esj9/3jH/f91xv3/AQIC/wAA + AP8HDRH/asH8/0l9oP8eHh7/HBwc/x4kKP9MsO//PbLs/yyNqv8TFxj/ERER/w8PD/8NDQ3/FktR/yrY + 7f8ozeD/FWNs/wUHCP8CAgL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AHB9/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/MuH2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HbPM/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtwVABab/wAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AZ37/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ + p/8e1+7/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tp/wKFo/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8uvtb/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Do+/9Y5fb/XuT0/2Dk9P9h5PT/YuX0/2Tl + 9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/cOf1/3Hn9f9z5/X/dOj1/3bo9f936PX/eOj1/3no + 9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Pq9v+E6vb/hOr2/4Xq9v+G6/b/huv2/4fr + 9/+H6/f/h+v3/4fr9/+I6/f/iOv3/4jr9/+I6/f/iOv3/4fr9/+H6/f/h+v3/4fr9/+G6/b/her2/4Xq + 9v+E6vb/hOr2/4Lq9v+C6vb/ger2/3/p9v9/6fb/fun2/33p9v976fb/e+n2/3no9v946PX/duj1/3bo + 9f906PX/c+f1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/1/k + 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/RLLC/wAAAP8FCgz/Ybjg/3HL+/9yyPz/cMf8/wAA + AP8AAAD/HTpK/13I+f9Rtdv/LCws/yoqKv8oKCj/M6e0/zfd8f813fH/LJai/x8fH/8eHh7/HBwc/xoa + Gv8heIP/Kdvw/yfb8P8iucv/FUlP/w8PD/8NDQ3/CwsL/wkJCf8ICAj/BgYG/wQFBf8BhZX/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/HNvy/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/N9jt/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HaAJKSBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtwSQBab/kAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wB2kv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AJu3/zjj+P9P6fz/T+n8/0/p/P9P6fz/TOX4/wyTr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8XoLv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1bm+P9e5PT/YOT0/2Ll + 9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/beb1/2/n9f9w5/X/cef1/3Pn9f906PX/duj1/3bo + 9f946PX/eej2/3rp9v976fb/fOn2/33p9v9+6fb/f+n2/4Dq9v+B6vb/gur2/4Lq9v+D6vb/hOr2/4Tq + 9v+E6vb/hOr2/4Xq9v+F6vb/huv2/4br9v+G6/b/huv2/4Xq9v+F6vb/her2/4Tq9v+E6vb/hOr2/4Pq + 9v+C6vb/gur2/4Lq9v+A6vb/f+n2/3/p9v9+6fb/fen2/3zp9v976fb/eun2/3jo9f936PX/duj1/3Xo + 9f9z5/X/cuf1/3Hn9f9w5/X/b+f1/23m9f9s5vX/aub0/2nm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XOTz/1vj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/xxOVP8AAAD/K3uE/0zh8v9K4PL/SeDy/0XZ + 6v8AAAD/AAAA/yV+if9B3/L/Pt7x/ztPUf84ODj/Nzc3/zaDjP823fH/NN3x/zLc8f8vdX3/LCws/yoq + Kv8oKCj/Jikp/yequf8m2vD/JNrw/yPa7/8fnKr/Gykr/xkZGf8XFxf/FhYW/xQUFP8SEhL/DxcY/wKz + yP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Dtjv/0zo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SOb5/wePq/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOfSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AX3X/AIGe/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8IutX/Sej7/0/p/P9P6fz/T+n8/yGux/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Giqj/R9/z/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/U+j6/17k + 9f9g5PT/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2zm9f9t5vX/b+f1/3Dn9f9x5/X/cuf1/3Pn + 9f916PX/duj1/3fo9f946PX/eej2/3vp9v976fb/fOn2/33p9v9+6fb/f+n2/3/p9v+A6vb/ger2/4Lq + 9v+C6vb/gur2/4Lq9v+D6vb/g+r2/4Pq9v+D6vb/hOr2/4Pq9v+D6vb/g+r2/4Pq9v+C6vb/gur2/4Lq + 9v+B6vb/gOr2/3/p9v9/6fb/fun2/33p9v996fb/fOn2/3vp9v966fb/eej2/3jo9f926PX/duj1/3To + 9f9z5/X/cuf1/3Hn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9U4vP/U+Lz/0S8yv8BAgL/G09V/03g8f9L4fL/SeDy/0jg + 8v86uMf/AAAA/wIFBv88zd7/P9/x/z7e8f9Eg4v/R0dH/0VFRf8/cnn/Nd3x/zPd8f8y3PH/Mdjr/zhU + WP84ODj/Nzc3/zU1Nf8yRUf/JtHm/yTa8P8h2u//INnv/x/H2/8kV13/JiYm/yQkJP8jIyP/ISEh/x8f + H/8YOz//Ac3l/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/B9bu/0Tm+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xatxv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOhowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFttKgBab/AAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/oAFluqwBzjqkAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIel/xfT6v9P6fz/T+n8/zjM4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Nsjf/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9R6Pv/XOX1/2Dk9P9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2nm9P9q5vT/bOb1/23m9f9v5/X/b+f1/3Hn + 9f9y5/X/c+f1/3To9f926PX/duj1/3jo9f946PX/eej2/3vp9v976fb/fOn2/33p9v996fb/fun2/3/p + 9v9/6fb/f+n2/4Dq9v+A6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+B6vb/ger2/4Hq9v+A6vb/gOr2/3/p + 9v9/6fb/f+n2/37p9v996fb/fen2/3zp9v976fb/e+n2/3rp9v956Pb/eOj1/3fo9f926PX/dej1/3Pn + 9f9z5/X/cef1/3Dn9f9v5/X/buf1/2zm9f9r5vX/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/8aSE7/GERJ/0va6v9L4fL/SuDy/0jg + 8v9G4PL/LpWh/wAAAP8WSlH/QN/y/z7e8f883vH/Rqm0/1VVVf9TU1P/TWhr/zTd8f8y3PH/MNzw/y/c + 8P80u8r/RkhI/0VFRf9DQ0P/QUFB/zSDjP8j2u//Idrv/x/Z7/8d2e//Gtfu/xqHlP8yMzP/MTEx/y8v + L/8tLS3/Kysr/xhvev8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/A9Xu/z7l+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yvN4/8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh5wCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABbb7cAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnDgAFpwogBbb2UAWWwoAAAAAAAAAAAA//8BAISgugCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AlLH/LN/0/0ni9f8Hjar/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/HqrD/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/o/P9Y5/j/YOT0/2Ll9P9j5fT/ZeX0/2bl9P9n5fT/aeb0/2rm9P9s5vX/bOb1/27n + 9f9v5/X/cOf1/3Hn9f9z5/X/c+f1/3Xo9f926PX/duj1/3jo9f946PX/eej2/3rp9v976fb/e+n2/3zp + 9v996fb/fen2/33p9v9+6fb/fun2/37p9v9/6fb/f+n2/3/p9v9/6fb/f+n2/3/p9v9/6fb/fun2/37p + 9v996fb/fen2/33p9v986fb/e+n2/3vp9v966fb/eej2/3jo9f946PX/d+j1/3bo9f916PX/dOj1/3Pn + 9f9y5/X/cef1/2/n9f9v5/X/beb1/2zm9f9q5vT/aub0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/88qbf/HVJZ/0vd7f9M4fL/SuDy/0jg + 8v9H4PL/ReDy/yJye/8AAAD/L6Ox/z7e8f893vH/PN7x/0HG1v9kZGT/YmJi/1tvcf8z3fH/Mdzw/zDc + 8P8t3PD/LNvw/0eEi/9TU1P/UVFR/1BQUP9NUFD/J8bY/yDZ7/8e2e//HNnv/wzW7v8A1O3/FaO0/z1E + RP89PT3/PDw8/zo6Ov84OTn/DLDD/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AdXt/zfj9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zzf9P8ChaT/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEoVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm/vAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/XAFpumwBabl0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + nh0Ag6LwAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKoxP8Uor3/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/CpCt/0vk9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Xo+f9f5PX/YuX0/2Pl9P9l5fT/ZuX0/2fl9P9p5vT/aub0/2vm + 9f9s5vX/beb1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3Xo9f926PX/duj1/3fo9f946PX/eOj1/3no + 9v966fb/e+n2/3vp9v976fb/fOn2/3zp9v986fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3zp + 9v986fb/e+n2/3vp9v976fb/eun2/3no9v946PX/eOj1/3fo9f926PX/duj1/3Xo9f906PX/c+f1/3Ln + 9f9x5/X/cOf1/2/n9f9u5/X/bOb1/2vm9f9q5vT/aeb0/2fl9P9m5fT/ZeX0/2Tl9P9i5fT/YeT0/2Dk + 9P9e5PT/XeT0/1vj8/9a4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4fL/OaOw/03h8v9M4fL/SuDy/0ng + 8v9H4PL/ReDy/0Tf8v8RNzz/EDY7/z/f8f8+3vH/PN7x/zre8f863e//cHZ3/3BwcP9ne33/Mtzx/zDc + 8P8u3PD/Ldzw/yvb8P8yydv/YWNj/2BgYP9eXl7/XFxc/0SMlP8f2e//Hdnv/xXY7v8A1O3/ANTt/wDU + 7f8Rtsn/RldZ/0pKSv9ISEj/RkZG/zhdYf8B0en/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fm+v8IlbH/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoaYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFpvywBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/0AWm/RAFtwkgBbcFQAWW8XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKgXgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AYSi/zzR5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Uun6/13l9v9i5fT/Y+X0/2Xl9P9m5fT/Z+X0/2jm + 9P9q5vT/aub0/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/duj1/3bo + 9f936PX/eOj1/3jo9f946PX/eej2/3no9v966fb/eun2/3rp9v976fb/e+n2/3vp9v976fb/eun2/3rp + 9v966fb/eej2/3no9v946PX/eOj1/3jo9f936PX/duj1/3bo9f916PX/dOj1/3Pn9f9z5/X/cuf1/3Hn + 9f9w5/X/b+f1/27n9f9t5vX/bOb1/2rm9P9q5vT/aOb0/2fl9P9m5fT/ZeX0/2Pl9P9i5fT/YOT0/1/k + 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9M4fL/SuDy/0ng + 8v9I4PL/RuDy/0Tf8v89zd7/AQME/zOxwP8+3vH/PN7x/zve8f853vH/N93x/3GTl/9+fn7/cI2R/zHc + 8P8v3PD/Ldzw/yvb8P8q2/D/Kdvw/1mQlv9ubm7/bGxs/2pqav9obG3/ItLn/xvZ7/8D1e3/ANTt/wDU + 7f8A1O3/ANTt/xG90f9SYWP/VlZW/1VVVf9TU1P/JZmm/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p/P8Srcf/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoeAAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm/8AFpv/wBab/8AWm//AFpv/wBab/0AWm/HAFtvigBb + b0wAYHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/yWzzP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9Q6fv/Wub3/2Ll9P9j5fT/ZOX0/2Xl + 9P9n5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9w5/X/cef1/3Hn9f9y5/X/c+f1/3Pn + 9f906PX/dej1/3bo9f926PX/duj1/3fo9f936PX/eOj1/3jo9f946PX/eOj1/3jo9f946PX/eOj1/3jo + 9f946PX/eOj1/3fo9f936PX/duj1/3bo9f926PX/dej1/3To9f9z5/X/c+f1/3Ln9f9x5/X/cef1/3Dn + 9f9v5/X/buf1/23m9f9s5vX/a+b1/2rm9P9p5vT/Z+X0/2fl9P9l5fT/ZOX0/2Ll9P9i5fT/YOT0/1/k + 9P9e5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/KIiU/xhVXP8+3vH/PN7x/zve8f853vH/ON3x/zbd8f9uq7L/jY2N/3Ki + qP8w3PD/Ltzw/yzb8P8r2/D/Kdvw/yfb8P9Bv87/fX19/3t7e/95eXn/d3d3/z21w/8K1u7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Fb/T/19rbf9jY2P/YWFh/1hnaf8GzeT/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/x3c8/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8hxt3/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAhKM6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFttOABab58AWm+qAFtvgQBZb0UAXXQLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICqDACCodcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/w+Xsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9X5/j/YeX0/2Ll + 9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n9f9v5/X/cef1/3Hn + 9f9x5/X/cuf1/3Pn9f9z5/X/dOj1/3To9f916PX/dej1/3bo9f926PX/duj1/3bo9f926PX/duj1/3bo + 9f926PX/duj1/3Xo9f916PX/dej1/3To9f9z5/X/c+f1/3Pn9f9y5/X/cef1/3Hn9f9w5/X/b+f1/2/n + 9f9u5/X/beb1/2zm9f9r5vX/aub0/2nm9P9o5vT/Z+X0/2bl9P9l5fT/Y+X0/2Ll9P9h5PT/YOT0/17k + 9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt7x/xNBRv870uT/Pd7x/zze8f853vH/ON3x/zfd8f813fH/Zb7J/5ub + m/9vtLz/Ltzw/y3c8P8r2/D/Kdvw/yjb8P8m2vD/J9jt/4WPkP+JiYn/h4eH/4WFhf9Zoar/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8avtH/bnN0/29vb/9ubm7/OZ2p/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/x/c8/9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8u1uz/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6J7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaEuAIOh9gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wOGpP9C2e3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/1Po + +v9f5fX/YuX0/2Pl9P9k5fT/ZeX0/2bl9P9n5fT/aOb0/2rm9P9q5vT/a+b1/2zm9f9t5vX/buf1/2/n + 9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Ln9f9y5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Pn + 9f9z5/X/c+f1/3Pn9f9z5/X/c+f1/3Ln9f9y5/X/cef1/3Hn9f9x5/X/cOf1/2/n9f9v5/X/buf1/23m + 9f9s5vX/bOb1/2vm9f9q5vT/aeb0/2jm9P9n5fT/ZuX0/2Xl9P9k5fT/YuX0/2Ll9P9g5PT/X+T0/17k + 9P9d5PT/W+Pz/1rj8/9Z4/P/WOPz/1bj8/9V4vP/VOLz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0rg + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/zCksv8wq7n/Pd7x/zze8f863vH/Od7x/zfd8f813fH/NN3x/1HE + 0/+UlJT/XL3J/y3c8P8r2/D/Ktvw/ynb8P8m2vD/Jdrw/yTa8P9zrbT/l5eX/5aWlv+UlJT/eZ2h/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y62xf9+fn7/fHx8/3V9fv8KzOP/ANTt/wDU + 7f8A1O3/ANTt/yHd9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P833fP/AYel/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKC6AKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoWQAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8tvdT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Uej7/1zl9v9h5PT/XeDx/0rM3/9Dxdn/WNjp/2fl9P9n5fT/aOb0/2nm9P9q5vT/a+b1/2zm + 9f9s5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/cOf1/3Dn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn + 9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/cOf1/2/n9f9v5/X/b+f1/27n9f9t5vX/bOb1/2zm + 9f9r5vX/aub0/2rm9P9p5vT/aOb0/2fl9P9m5fT/ZeX0/2Tl9P9j5fT/YuX0/2Hk9P9g5PT/XuT0/17k + 9P9c5PP/W+Pz/1rj8/9Z4/P/V+Pz/1bj8/9U4vP/U+Lz/1Li8/9R4vP/T+Hz/07h8/9N4fL/S+Hy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Dc7v8yssD/Pt7x/zze8f863vH/Od7x/zfd8f823fH/NN3x/zLc + 8f9By9v/g4OD/0XE0/8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//WrnE/5mZmf+bm5v/nZ2d/5ei + o/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/S625/4qKiv+IiIj/Ray4/wDU + 7f8A1O3/ANTt/yTe9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P894vf/A4+s/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HgAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISinwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Wn7r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9A1ev/JazF/waIpv8Ag6H/AIOh/wKEo/89vtP/ZeX0/2bl9P9n5fT/aOb0/2nm + 9P9q5vT/aub0/2vm9f9s5vX/bOb1/2zm9f9t5vX/beb1/27n9f9u5/X/b+f1/2/n9f9v5/X/b+f1/2/n + 9f9v5/X/b+f1/2/n9f9v5/X/b+f1/2/n9f9u5/X/buf1/27n9f9t5vX/bOb1/2zm9f9s5vX/a+b1/2rm + 9P9q5vT/aeb0/2jm9P9n5fT/Z+X0/2bl9P9l5fT/ZOX0/2Pl9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k + 9P9b4/P/WuPz/1nj8/9Y4/P/VuPz/1bj8/9U4vP/U+Lz/1Li8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8+2+z/Pt7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zLc + 8f8x3PD/NNXo/3Jycv8z0eT/K9vw/ynb8P8o2/D/Jtrw/yTa8P8j2u//Idrv/zzB0f+IiIj/ioqK/4yM + jP+Ojo7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7P92p63/l5eX/4qa + nP8H0en/ANTt/ybe9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9B5fn/BZa0/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H1AIKhMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBopQAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Giqj/R97y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/S+T4/zDB2P8QmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/1rb7P9l5fT/ZeX0/2bl + 9P9n5fT/Z+X0/2jm9P9p5vT/aub0/2rm9P9q5vT/a+b1/2vm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm + 9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9s5vX/bOb1/2zm9f9r5vX/a+b1/2rm9P9q5vT/aub0/2nm + 9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9k5fT/YuX0/2Ll9P9h5PT/YOT0/1/k9P9e5PT/XeT0/1zk + 8/9b4/P/WePz/1nj8/9X4/P/VuPz/1Xi8/9U4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h8v9M4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y7c8P9adHf/K9vw/ynb8P8o2/D/Jtrw/yXa8P8j2u//Idrv/yDZ7/8j0OT/d3d3/3l5 + ef97e3v/e35//wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Eszi/4+c + nv+bm5v/QrzL/yrf9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G5vr/CaC8/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKhXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoEYAg6H+AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Ncfe/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8/1Or/IKzG/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof9Ex9r/YuX0/2Pl + 9P9k5fT/ZeX0/2Xl9P9m5fT/Z+X0/2fl9P9n5fT/aOb0/2nm9P9p5vT/aub0/2rm9P9q5vT/aub0/2rm + 9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aub0/2rm9P9q5vT/aeb0/2nm9P9o5vT/Z+X0/2fl + 9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Pl9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9d5PT/XOTz/1vj + 8/9a4/P/WePz/1jj8/9W4/P/VuPz/1Ti8/9T4vP/UuLz/1Hi8/9P4fP/TuHz/03h8v9L4fL/SuDy/0ng + 8v9I4PL/RuDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y7c8P8t3PD/QI2W/yrb8P8p2/D/Jtrw/yXa8P8k2vD/Itrv/yDZ7/8b2e//BNXt/15u + cP9oaGj/ampq/2F3ef8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8yucn/ioqK/3ybnv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9K5/v/DqjC/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAnBIAg6HkAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/HajC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/y/A + 1/8QmLP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T9Pl/2Dk + 9P9h5PT/YuX0/2Ll9P9j5fT/ZOX0/2Xl9P9l5fT/ZeX0/2bl9P9m5fT/Z+X0/2fl9P9n5fT/Z+X0/2jm + 9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2jm9P9o5vT/aOb0/2fl9P9n5fT/Z+X0/2fl9P9m5fT/ZuX0/2Xl + 9P9l5fT/ZeX0/2Tl9P9j5fT/YuX0/2Ll9P9h5PT/YOT0/2Dk9P9f5PT/XuT0/13k9P9c5PP/W+Pz/1rj + 8/9Z4/P/WOPz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UuLz/1Dh8/9P4fP/TeHy/03h8v9L4fL/SuDy/0jg + 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f873vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/K9vw/zGruv8p2/D/J9vw/yXa8P8k2vD/Itrv/yHa7/8a2O//A9Xt/wDU + 7f9HaW3/V1dX/1paWv9CfYT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/B9bv/2mWm/97e3v/WtLh/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/D6zF/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhvQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GqAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/Co+s/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/H6vF/wSI + pf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HqK8/17k + 9P9e5PT/X+T0/2Dk9P9g5PT/YeT0/2Ll9P9i5fT/Y+X0/2Pl9P9k5fT/ZOX0/2Xl9P9Q0OL/Pb3S/03N + 4f9l5fT/ZuX0/2bl9P9m5fT/ZuX0/2bl9P9m5fT/ZuX0/2Xl9P9l5fT/ZeX0/2Xl9P9l5fT/ZOX0/2Tl + 9P9j5fT/YuX0/2Ll9P9i5fT/YeT0/2Dk9P9g5PT/X+T0/17k9P9e5PT/XeT0/1zk8/9b4/P/WuPz/1nj + 8/9Y4/P/V+Pz/1bj8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/0/h8/9O4fP/TeHy/0zh8v9K4PL/SeDy/0jg + 8v9H4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p0OT/J9vw/yba8P8k2vD/Itrv/yHa7/8W2O//AtTt/wDU + 7f8A1O3/Mmpw/0ZGRv9JSUn/J4yX/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Dtjv/0Xn+v9T1+f/aW5v/2Gdpf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/DavG/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKJdAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AYSi/zvP5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+P3/y+/1/8Pl7L/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/D5Ov/1jg + 8P9b4/P/XOTz/13k9P9e5PT/XuT0/1/k9P9g5PT/YOT0/2Dk9P9h5PT/YuX0/2Ll9P8prMP/AIOh/wCD + of8Ag6H/IaO8/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/Y+X0/2Pl9P9j5fT/Y+X0/2Ll9P9i5fT/YuX0/2Ll + 9P9i5fT/YeT0/2Dk9P9g5PT/YOT0/1/k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1jj + 8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9N4fL/TeHy/0vh8v9K4PL/SeDy/0jg + 8v9G4PL/ReDy/0Tf8v9D3/L/Qd/y/0Df8v8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yrb8P8p2/D/J9vw/yba8P8k2vD/I9rv/yHa7/8P1+7/ANTt/wDU + 7f8A1O3/ANTt/yFsdf81NTX/ODg4/xOgsf8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/GNry/0vo+/9P6fz/T+n8/1WbpP9bZWb/UOb4/0/p/P9P6fz/T+n8/0/p/P9G5/r/DKnE/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh4wCApBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKUfAIOi8ACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/ySyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+1On/H6rE/wSIpf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCat/8AutX/BdTr/zfd + 8f9Z4/P/WePz/1rj8/9b4/P/W+Pz/1zk8/9d5PT/XuT0/17k9P9e5PT/X+T0/2Dk9P9Gyd3/AIOh/wCD + of8Ag6H/AIOh/wCDof89wNX/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9h5PT/YeT0/2Hk9P9g5PT/YOT0/2Dk + 9P9g5PT/X+T0/1/k9P9e5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9b4/P/WuPz/1nj8/9Z4/P/WOPz/1bj + 8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9K4PL/SODy/0fg + 8v9F4PL/ReDy/0Pf8v9C3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f863vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/I9rv/xzZ7/8I1e7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Ub3n/JCQk/ycpKv8DyN//ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Jd70/07p/P9P6fz/T+n8/0/p/P9O3u//S1ZX/0+5xv9P6fz/T+n8/0/p/P9F5vn/CqXB/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAIOhwACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6Vsf9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuP2/y6+1v8PlrL/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKrF/wDJ4/8A1O3/ANTt/wDU + 7f8A1O3/Ftjv/0jg8v9Z4/P/WePz/1nj8/9a4/P/W+Pz/1vj8/9c5PP/XOTz/13k9P9e5PT/LbHJ/wCD + of8Ag6H/AIOh/wCDof8Ag6H/JanC/1/k9P9f5PT/X+T0/1/k9P9f5PT/X+T0/1/k9P9e5PT/XuT0/17k + 9P9e5PT/XuT0/13k9P9d5PT/XOTz/1vj8/9b4/P/W+Pz/1rj8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1bj + 8/9V4vP/VOLz/1Pi8/9S4vP/UeLz/1Dh8/9P4fP/TuHz/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0bg + 8v9F4PL/RN/y/0Pf8v9B3/L/Qd/y/z/f8f8+3vH/PN7x/zve8f853vH/Od7x/zfd8f823fH/Nd3x/zPd + 8f8y3PH/MNzw/y/c8P8t3PD/LNvw/yvb8P8p2/D/J9vw/yba8P8k2vD/Itrv/xPX7v8B1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Cm56/xQUFP8RQ0n/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8H1u//OeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0WUnf9DeoH/T+n8/0/p/P9D5fn/CaK+/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCCoz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhdQCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKGpP9B2O3/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+j/HqrE/wOHpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi/wCbt/8Autb/ANPs/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/JNrw/1Dh8/9Y4/P/WOPz/1nj8/9Z4/P/WuPz/1rj8/9b4/P/W+Pz/yWq + w/8Ag6H/AIOh/wCDof8Ag6H/AIOh/yGlvv9d5PT/XeT0/13k9P9d5PT/XeT0/13k9P9c5PP/XOTz/1zk + 8/9c5PP/W+Pz/1vj8/9b4/P/WuPz/1rj8/9Z4/P/WePz/1nj8/9Y4/P/V+Pz/1bj8/9W4/P/VeLz/1Ti + 8/9U4vP/U+Lz/1Li8/9R4vP/UOHz/0/h8/9O4fP/TeHy/0zh8v9L4fL/SuDy/0ng8v9I4PL/R+Dy/0Xg + 8v9F4PL/Q9/y/0Lf8v9B3/L/QN/y/z7e8f893vH/PN7x/zve8f853vH/ON3x/zfd8f813fH/NN3x/zLc + 8f8x3PD/MNzw/y/c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/yba8P8f2e//ENfu/wPV7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wBue/8DAwP/AnSB/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8X2/H/SOf6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N3e//MENG/0/n+v864vf/Bpq3/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCBo0UAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD + ofkAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8svNP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/SuP2/y691f8OlbH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AjKn/AKvG/wDK5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1e3/L9zw/1Ti8/9W4/P/V+Pz/1jj8/9Y4/P/WePz/1nj + 8/8lqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8dp8H/WOT2/1vj8/9b4/P/W+Pz/1vj8/9a4/P/WuPz/1rj + 8/9a4/P/WePz/1nj8/9Z4/P/WePz/1jj8/9Y4/P/V+Pz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Pi + 8/9S4vP/UuLz/1Hi8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0vh8v9K4PL/SeDy/0jg8v9H4PL/RuDy/0Xg + 8v9E3/L/Q9/y/0Hf8v9B3/L/P9/x/z7e8f893vH/PN7x/zre8f853vH/N93x/zfd8f813fH/NN3x/zLc + 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/J9vw/x/Z7/8M1u7/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8AdYP/AAAA/wCvw/8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU + 7v8u4PX/T+n8/0Xc8P81x97/PNDm/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zJ4gf8qscL/AZCt/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDoE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICfCACD + odMAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P890uj/HqrD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AYSi/xehvP8nxt3/B9Xu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8M1u7/NN3x/1Pi8/9W4/P/VuPz/1bj + 8/9W4/P/JKrD/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9R6Pr/VuX1/1nj8/9Y4/P/WOPz/1jj + 8/9Y4/P/V+Pz/1fj8/9X4/P/VuPz/1bj8/9W4/P/VuPz/1Xi8/9U4vP/VOLz/1Ti8/9T4vP/UuLz/1Li + 8/9R4vP/UOHz/0/h8/9P4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9J4PL/SODy/0fg8v9G4PL/ReDy/0Tf + 8v9D3/L/Qt/y/0Hf8v9A3/L/Pt7x/z3e8f883vH/O97x/zne8f853vH/N93x/zbd8f813fH/M93x/zLc + 8f8x3PD/MNzw/y7c8P8t3PD/K9vw/yrb8P8p2/D/Itrv/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AH2L/wAZHP8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y + 7/9B5vr/T+n8/yy80/8BhaL/AIOh/wCDof8Vn7n/Tef6/0/p/P9P6fz/T+n8/07o/P8dtMj/BkpZ/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+QCDolIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oY0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Fiqf/Rt7x/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/SuL2/y291f8OlLD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Jj6v/J7bP/0bd8f9P6fz/T+n8/0no+/8Z2/L/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u7/MNzw/1Hi + 8/9U4vP/VOLz/yOqw/8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p/P9R5/r/VOX1/1bj + 8/9W4/P/VuPz/1Xi8/9V4vP/VeLz/1Ti8/9U4vP/VOLz/1Pi8/9T4vP/UuLz/1Li8/9R4vP/UeLz/1Dh + 8/9P4fP/T+Hz/07h8/9N4fL/TeHy/0zh8v9K4PL/SuDy/0ng8v9I4PL/R+Dy/0bg8v9F4PL/RN/y/0Pf + 8v9C3/L/Qd/y/0Df8v8/3/H/Pt7x/z3e8f883vH/Ot7x/zne8f843fH/N93x/zXd8f813fH/M93x/zLc + 8f8w3PD/L9zw/y3c8P8t3PD/K9vw/yrb8P8n2/D/E9fu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCImP8AV2L/ANTt/wDU7f8A1O3/AtTt/yfe + 9f9N6fv/T+n8/0Ta7v8BhKL/AIOh/wCDof8Ag6H/AIOh/yq50f9P6fz/T+n8/0rn+/8Vw9z/AIaj/wA8 + Sf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCFoEsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + okIAg6H+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8bc/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P890ef/HanD/wOGpP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AYSi/xihvP83y+H/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHh9v8E1e7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8H1e3/K9vw/03h8v8iqsP/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p + /P9Q5/r/U+P1/1Pi8/9T4vP/U+Lz/1Li8/9S4vP/UuLz/1Li8/9R4vP/UeLz/1Dh8/9P4fP/T+Hz/0/h + 8/9O4fP/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Lf + 8v9B3/L/Qd/y/z/f8f8+3vH/Pd7x/zze8f873vH/Od7x/zne8f833fH/Nt3x/zXd8f803fH/Mtzx/zHc + 8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8a2O//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Aj6D/AIyc/wDU7f8A1O3/Fdnx/0Pm + +f9P6fz/T+n8/0/p/P82yd//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/T+n8/0Lm+v8MsMv/AIOh/wCD + of8AVWn/AHqW/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh9QCDoUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH + pREAg6HiAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKbB/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/y29 + 1P8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKH/FqrE/0bd8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Ruf6/xja + 8v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8E1e3/DabB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P5/n/UOT0/1Hi8/9Q4fP/UOHz/0/h8/9P4fP/T+Hz/07h8/9O4fP/TeHy/03h + 8v9M4fL/TOHy/0vh8v9K4PL/SuDy/0ng8v9I4PL/SODy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf + 8v9B3/L/P9/x/z7e8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc + 8P8w3PD/Ltzw/y3c8P8r2/D/K9vw/yHa7/8K1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AJao/wCzyP8I1u//M+L3/0/p + /P9P6fz/T+n8/0/p/P9P6fz/MsTb/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqn/y7e8/8Dm7f/AIOh/wCD + of8Ag6H/AH6b/wBkev8Ag6H/AIOh/wCDof8Ag6H/AIOh7QCCoDsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6GmAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/CY6s/0ri9v9P6fz/T+n8/0/p/P880Of/HajC/wKG + pP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ats7/S+f7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/N+P3/wnW7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xynwf9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P5vn/TuL0/03h8v9N4fL/TeHy/03h8v9M4fL/TOHy/0vh + 8v9K4PL/SuDy/0ng8v9J4PL/SODy/0jg8v9H4PL/RuDy/0Xg8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Df + 8v8/3/H/Pt7x/z3e8f883vH/PN7x/zre8f853vH/ON3x/zfd8f823fH/Nd3x/zTd8f8y3PH/Mdzw/zDc + 8P8v3PD/Ldzw/y3c8P8r2/D/JNrw/w7W7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wGqv/8h2vH/Suf7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3S6P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ai6j/AIOh/wCD + of8Ag6H/AIOh/wCDof8AaYH/AIOh/wCDof8Ag6H/AISh3wCDoikAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKBZAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zvP5f9P6fz/SeL1/yy80/8Mk7D/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wybt/9B4vf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9L6Pv/JN30/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8ApMH/AIOh/wCDof8Ag6H/AIOh/wCDof8cp8H/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vj/S+L0/0rg8v9K4PL/SuDy/0ng + 8v9J4PL/SODy/0jg8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9D3/L/Q9/y/0Hf8v9B3/L/QN/y/z/f + 8f8+3vH/Pd7x/zze8f883vH/Ot7x/zne8f843fH/N93x/zbd8f813fH/NN3x/zPd8f8y3PH/MNzw/zDc + 8P8u3PD/Ldzw/yzb8P8l2vD/ENfu/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xja8v8/3vL/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AHSP/wCDof8Ag6H/AIOgzQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yOwyv87z+b/HKfC/wKFpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoMEAg6DtAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4in/y7Q5v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vn/Gtvy/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKTB/wCDof8Ag6H/AIOh/wCDof8Ag6H/HKfB/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5fj/SeH0/0jg + 8v9H4PL/R+Dy/0bg8v9F4PL/ReDy/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v8/3/H/Pt7x/z7e + 8f893vH/PN7x/zve8f863vH/Od7x/zjd8f833fH/N93x/zXd8f813fH/M93x/zLc8f8x3PD/MNzw/y/c + 8P8t3PD/LNvw/x7Z7/8N1u7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Fdnx/z7l+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/waKqP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wB/nP8Ag6H/AIKgsgCAqgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA//8BAIOhvQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8KkK3/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKHfAIOiewCAnRoAAAAAAICkHACD + ocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Fq3G/0fm+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8+5fn/Etnw/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wCkwf8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn + wf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9K5fj/RuHz/0Tf8v9E3/L/Q9/y/0Pf8v9C3/L/Qd/y/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pd7x/zze + 8f883vH/O97x/zre8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/yzb + 8P8g2e//Etfu/wTV7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8R2PD/OuT4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8SmrX/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIKigwCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIShcgCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoa0AgaJHAICAAgAAAAAAAAAAAAAAAAAA + AAAAjqoJAIOhqACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8GjKr/Ndbs/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P854/j/Etnw/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Ap8T/AIOh/wCDof8Ag6H/AIOh/wCD + of8cp8H/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07p/P9J5ff/Q+Dz/0Hf8v9B3/L/QN/y/z/f8f8+3vH/Pt7x/z7e8f893vH/PN7x/zve + 8f863vH/Od7x/zne8f843fH/N93x/zbd8f813fH/Nd3x/zPd8f8y3PH/Mdzw/yjb8P8g2e//Fdju/wvW + 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xfa8f864/j/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HanD/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HzAIOhVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIWiLACDofcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKLdAIOgeQCAnxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqFsAIOh+ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Yr8j/RuX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P865Pj/F9rx/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AKnF/wCDof8Ag6H/AIOh/wCD + of8Ag6H/GKG8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zo+/844fX/I9rw/yHa7/8j2u//JNrw/yTV6v8bvtb/HcTb/yja + 7/8q2/D/Kdvw/yjb8P8l2vD/Itrv/yDZ7/8c2e//F9jv/xLX7v8N1u7/B9Xt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8D1e7/IN3z/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yq40P8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqHXAIWiLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICqBgCDotIAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh+gCDoasAgaNFAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqgYQAfZr/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wSJpv8szOL/Tuj8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9E5vn/It30/wTV7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wCpxf8Ag6H/AIOh/wCD + of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/8n3vX/Bdbu/wC40v8Ah6T/AIOh/wCD + of8AkrD/AM/o/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrW7/8q3/X/Sej7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P81x97/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKCfAIuiCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoYoAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HcAIOhdwCFphcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/KAFxy/wB1kP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/w+duP873fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/L+H2/xLZ8P8A1O3/ANTt/wDU7f8AqcX/AIOh/wCD + of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/z3Y7f8BhqT/AIOh/wCD + of8Ag6H/AIOh/wCivv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8G1e7/INzz/zzk+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Qdfs/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCEofQAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACGpCoAg6H9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh+QCDoakAg6FEAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXG0vAFpv/wBab/8AWm//AGiB/wCAnv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xmwyf9E4/f/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/ROb5/yfe9f8K1u//AKnF/wCD + of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8QmLP/AIOh/wCD + of8Ag6H/AIOh/wCDof8Aj6v/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/AtTt/xnb8v824vf/Ten8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0zl + +P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgaJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKJ+AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HaAISidgCAohYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvlQBab/8AWm//AFpv/wBab/8AXnT/AHeT/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AoWj/yG81f9I5vn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x6w + yv8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8ots7/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AJe0/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV + 7v8b2/L/MuH2/0rn+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/CY6s/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKiaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhmACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh+ACD + oKcAg6JCAP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBab+8AWm//AFpv/wBab/8AWm//AFpv/xaB + lv8xwdn/BYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4im/yPB + 2v9H5vn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AoWj/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wC30f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHU7f8Q2PD/J971/z3k + +f9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoI8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HYAISgdACG + nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbbl8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/81us7/T+n8/0jh9P8dqcP/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/Aoel/yG/2P9G5fn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5vn/DZSw/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCduP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wTV7v8U2fH/Jd70/zXi9/9I5/v/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/SeH1/0/p + /P9P6fz/T+n8/0nn+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8grcb/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6G1AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISieACDofUAg6H/AIOh+ACDoaYAg59AAP//AQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/EAFpv/wBab/8AWm//AFpv/wBa + b/8HZnv/Teb5/0/p/P9P6fz/T+n8/zvP5v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AoWj/x+40f9C4vX/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/JLHK/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJqP8Azef/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/B9bv/xLZ8P8h3fT/MuH2/0Lm+f9O6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0LY7f8QmLP/AIOh/wGF + ov8eq8T/Od/1/0Pl+f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/LLzT/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh2wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8QAICeIgCApA4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW20qAFpv/wBab/8AWm//AFpv/wBa + b/8AWm//JZ2x/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/yu70/8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xaowv842e7/Tun8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8ls8z/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/PNDn/wGE + ov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AutX/ANTt/wDU7f8D1e7/B9bv/wzX8P8Q2fD/Fdnx/xrb + 8v8i3fT/LOD1/zfj9/9B5vr/TOj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/07n+/8Lkq7/AIOh/wCD + of8Ag6H/AIOh/xehvP9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zfL4f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDofsAmZkFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlvjwBab/8AWm//AFpv/wBa + b/8AWm//AFtw/0PT5/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/SuL2/ySxyv8ChqT/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuUsP8syeD/SOX5/0/p + /P9P6fz/T+n8/0/p/P9P6fz/JbPM/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+T4/wuR + rv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8dqcP/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1Or/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D2e7/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOjJwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEltBwBab+wAWm//AFpv/wBa + b/8AWm//AFpv/xR+k/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Rt3x/x2o + wv8BhKL/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKF + o/8YqML/NdTq/03o+/9P6fz/T+n8/yWzzP8Ag6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/x+r + xf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0Lm+f8m3fP/CLbR/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tef6/wGFov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb1oAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8zt8r/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/RNru/x2pw/8BhKH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Giqj/HbHK/zbV6/8kssv/AIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/znM + 4v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Os3k/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5fn/K97z/xDG3f8Aor7/AIim/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yy91P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8Lkq//AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKFyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm++AFpv/wBa + b/8AWm//AFpv/wBab/8GZHr/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0zj9v8glKn/AGB3/wBzjf8AgqD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0ni + 9f8Ijqv/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9L6Pv/N+P3/yDX7/8KutT/AJu4/wCGpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/M8Xb/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AISglwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV3ImAFpv/gBa + b/8AWm//AFpv/wBab/8AWm//I5it/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/z3J3f8LboP/AFpv/wBab/8AWm//AF90/wBxjP8AgZ7/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xeh + vP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8cpsH/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/C5Ku/0zl+P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9L6Pv/O+T4/yrb8f8UxNz/AqTB/wCPrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wKFo/87z+X/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/yOwyv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpviABa + b/8AWm//AFpv/wBab/8AWm//AFpv/0LS5P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Teb5/yWesv8BXHH/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AF1x/wBthf8Afpv+AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P81x97/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYSi/z3S6P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/03p + /P8+5fn/Lt7z/x3P5/8NuNP/AZ+8/wCOrP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/z7T + 6f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8vv9f/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFWABgBa + b+kAWm//AFpv/wBab/8AWm//AFpv/xJ7j/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/QdDk/w90if8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXDQAFpwMACE + oU8AhKC6AIOh/QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9I3/P/Boqo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/yWyy/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rn+/9A5fr/NeL3/yvc8f8g0ej/FsLa/wuw + y/8CnLj/AI6r/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Hi6n/RNrv/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCAmQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZ + b1MAWm//AFpv/wBab/8AWm//AFpv/wBab/8xs8f/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9O6Pv/Kqa6/wJec/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/wAWm+IAFWABgAA + AAAAAAAAAAAAAACAoh4Ag6F9AIOh3ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wSPrP8cyN//Gsnh/x/L5P8gzub/IM/n/yTS + 6f8l0en/JM/m/x/M5P8ayOD/FcLa/xG61P8Nsc3/CKrF/wOfu/8AlbL/AIup/wCEov8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDocEAhKKkAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wuSrv9G3fL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0be + 8f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWm+4AFpv/wBab/8AWm//AFpv/wBab/8FY3f/TOT2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9E1ej/E3yQ/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab9wAWG46AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//wEAhKA+AIShmwCDoesAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/McHZ/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HQAIOhdwCAnyAAAAAAAAAAAACCoYcAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/DpWx/0rj9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/A4el/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIKgVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWHAgAFpv/QBab/8AWm//AFpv/wBab/8AWm//IZaq/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o + +/8vsMP/BGF2/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/gBbb5gAXXQLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiQgCDoJQAg6HlAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/Rd3x/wSIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqD/AIOhywCDoX8Ag6IpAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8BAIOipACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Unbj/Tef6/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDonsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvgwBab/8AWm//AFpv/wBab/8AWm//AFpv/0DO4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0fb + 7v8XhZj/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv5gBZbkgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAgCD + oaAAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/xSduP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIGf/wB2kf8AaoL/AGB4lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq + qgMAg6GxAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xiivf9O6Pv/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GiAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAECABABZb+QAWm//AFpv/wBab/8AWm//AFpv/xB3jP9P6fz/T+n8/0/p/P9P6fz/T+n8/zW5 + zf8GZHr/AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpvqABVcRIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/y291P8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/A4el/wB3 + kv8AbYb/AGN6/wBbcP8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICZCgCDocgAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/Ia3H/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/JbTN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhxgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABZcU0AWm//AFpv/wBab/8AWm//AFpv/wBab/8vsMP/T+n8/0/p/P9P6fz/St/y/xyM + oP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/tAFpuWAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0PZ7v8DhqT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8DhqT/EZm1/yGvyP8xwtr/Qdfs/y+x + xf8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhp4VAIOh1ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8ot8//T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoe0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+zAFpv/wBab/8AWm//AFpv/wBab/8EYXb/S+L1/0/p/P9P6fz/OcLV/whp + fv8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AW2+3AF5xGwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07n+/8RmLT/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8ChaP/Co+s/xOctv8fq8X/K7rS/zjL4f9E2u//Tuj7/0/p/P9P6fz/T+n8/0/p + /P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AW2/IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6HjAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/y6/ + 1v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890ef/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AICcEgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAW20cAFpv+wBab/8AWm//AFpv/wBab/8AWm//H5Km/0/p/P9M5Pb/IZWp/wBb + cP8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/QAWnBpAAAAAQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8puM//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISh9gCCon4Ag6F/AIKikQCD + oaMAgqG2AIOhuwCDobsAg6HLAIOhzACDocwAg6HMAIOhzACDosoAg6G7AIOhuwB3lPIAdI7/AHGK/wBu + h/8Aa4T/AGd//wt3jP9H3/P/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFlwiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKfLQCDovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8BhKL/N8rg/0/p/P9P6fz/T+n8/0/p/P9P6fz/SeL1/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCC + oTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFpufQBab/8AWm//AFpv/wBab/8AWm//AFpv/z/M3/89yd3/C2+E/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab8YAXHEkAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9A1ev/AoWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoGEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW252AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//P8zg/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqI3AIOh9QCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wKGpP87z+X/T+n8/0/p/P9P6fz/T+n8/0/p/P8Giqj/AIOh/wCDof8Ag6H/AIOh/wCD + of8AhKJdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAICAAgBacOAAWm//AFpv/wBab/8AWm//AFpv/wxxhv8mnrP/AV1x/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv+QBbcHkAVVUDAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9N5/n/DpSw/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoLIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFxwGQBa + b/sAWm//AFpv/wBab/8AWm//AFpv/yGWqv9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoE4Ag6H8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/BYmn/0HX7P9P6fz/T+n8/0/p/P9P6fz/Epq1/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZbkgAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFlv0wBcbS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/JbLL/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDoesAgJ8YAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW2+0AFpv/wBab/8AWm//AFpv/wBab/8GZHn/TeX4/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8djqL/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiYwCD + of4Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Jj6v/RNvv/0/p/P9P6fz/T+n8/x2owv8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+tAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/9AFlwiQBJbQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/PdHn/wGEov8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhKJVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtwVABab/8AWm//AFpv/wBab/8AWm//AFpv/zS4zP9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFpviAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJ4AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wuSrv9I4fT/T+n8/0/p/P8puND/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWW8XAFpv+gBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/dAFlvPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/xeh + vP9P6fz/T+n8/0/p/P9P6fz/TOX4/wuSrv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6GlAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABVgAYAWm/sAFpv/wBab/8AWm//AFpv/wBab/8Xg5f/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBbcEkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/EZm0/0zl+P9P6fz/NMfd/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlveABab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWnCZAGJ2DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD + of8Xobz/T+n8/0/p/P9P6fz/T+n8/yGtx/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HlAIelEQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFtwkgBab/8AWm//AFpv/wBab/8AWm//AVxx/0fa7f9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgIACAIOipACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Vnrn/Tef6/0DW + 6/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AhJ4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBab9sAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab+YAWHBLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD + of8Ag6H/F6G8/0/p/P9P6fz/T+n8/znN4/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H+AIOfSAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYbTEAWm//AFpv/wBab/8AWm//AFpv/wBab/8qpbn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjaH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAqgYAg6G9AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xyn + wv9L5Pf/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZcEIAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab6oAXmsTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCD + of8Ag6H/AIOh/xehvP9P6fz/T+n8/0ri9v8Jj6v/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhmAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlw0ABab/8AWm//AFpv/wBab/8AWm//DHCF/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/CWp//wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDocwAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/JLHK/wiOq/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2+mAFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv7gBbb1oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCD + of8Ag6H/AIOh/wCDof8Xobz/T+n8/0/p/P8dqML/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AISi3QCA + qgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabm8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/89yNz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBb + cEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhp4VAISh2wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AgqGNAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYGAIAFpv+ABab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpvugBecRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCD + of8Ag6H/AIOh/wCDof8Ag6H/F6G8/0/p/P82yN//AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/ACC + oz0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVW0VAFpv+QBab/8AWm//AFpv/wBa + b/8AWm//H5Kn/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAoyQAg6HqAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhswAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdyJgBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/1AFlvagAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oYcAg6H/AIOh/wCDof8Ag6H/AIOh/xehvP9I4PT/B4up/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + oIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb64AWm//AFpv/wBa + b/8AWm//AFpv/wRid/9M5Pb/T+n8/0/p/P9P6fz/T+n8/0/p/P8cjKH/AFpv/wBab/8AWm//AFpv/wBa + b/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWhLgCD + ovAAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDodgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWm/wAFpv/wBa + b/8AWm//AFpv/wBab/8AW2/IAFpuJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Xobz/GaO9/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + odQAgJ8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXFNAFpv/wBa + b/8AWm//AFpv/wBab/8AWm//MrTI/0/p/P9P6fz/T+n8/0/p/P9P6fz/CGl+/wBab/8AWm//AFpv/wBa + b/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6JCAIOh+QCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H6AIC/BAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpuXQBa + b/4AWm//AFpv/wBab/8AWm96AECABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofoAhaMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABa + b+gAWm//AFpv/wBab/8AWm//AFpv/xR9kv9P6fz/T+n8/0/p/P9P6fz/RNXo/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBZbkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACEoVcAg6H8AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCA + oyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAW3EtAFtuhABabn0AWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6F/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnCLAFpv/wBab/8AWm//AFpv/wBab/8AW3D/Rdfp/0/p/P9P6fz/T+n8/zCxxP8AWm//AFpv/wBa + b/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6JKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6LKAIC/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFdwKQBab/8AWm//AFpv/wBab/8AWm//AFpv/yehtf9P6fz/T+n8/0/p/P8cjKH/AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H3AIOiKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm/HAFpv/wBab/8AWm//AFpv/wBab/8KbIH/Tuj7/0/p/P9P6fz/CGl+/wBa + b/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCEoJcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCEopkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOgcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFlvZwBab/8AWm//AFpv/wBab/8AWm//AFpv/zvE2P9P6fz/RNXo/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOhsQCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6G7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOhwACAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8AWm/2AFpv/wBab/8AWm//AFpv/wBab/8djqL/T+n8/zCx + xP8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAYnYNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAmQoAg6HDAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh8gCDoiEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvpQBab/8AWm//AFpv/wBab/8AWm//A191/0vh + 9P8cjKH/AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCD + oe8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDockAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAg6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCCoWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZb0UAWm//AFpv/wBab/8AWm//AFpv/wBa + b/8wscT/CGl+/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgKQcAIOh4wCDof8Ag6H/AIOh/wCDof8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqGHAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDobUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIACAFpv4QBab/8AWm//AFpv/wBa + b/8AWm//B2Z7/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACFphcAhKGdAIOh/QCDoesAg6F1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoe0AhaMZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABab4MAWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/sAXXQLAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDonsAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8AgqBWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV20jAFpv/gBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaFBAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6GpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b8EAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oZ4Ag6H/AIOh/wCDof8Ag6HQAICfEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnBgAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab0cAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOgRgCDoXcAhKBbAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFVqDABab/MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/oAXXQLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+fAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/HAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFtvhwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP/////////////A///////gB////////////////////////////////8B//////8AH + ////////////////////////////////wD//////wAP///////w////////////////////////AH/// + ///AA///////+D///////////////////////8AP/////4AD///////gH/////////////////////// + wA//////gAP//////8Af///////////////////////AB/////8AA///////gD////////////////// + /////+AD/////wAB//////8AP///////////////////////4AH/////AAH//////gA///////////// + ///////////gAP////4AAf/////8AD///////////////////////+AAf////gAB//////AAf/////// + ////////////////4AA////8AAH/////4AB////////////////////////gAB////wAAP/////AAH// + //////////////////////AAD////AAA/////4AAf///////////////////////8AAH///gAAAA//// + AAD////////////////////////wAAP/+AAAAAAD//4AAP////////////////////////AAAf8AAAAA + AAAf/AAA////////////////////////8AAA8AAAAAAAAAHwAAH////////////////////4f//wAAAA + AAAAAAAAACAAAf////////////////////Af//gAAAAAAAAAAAAAAAAB////////////////////8Af/ + +AAAAAAAAAAAAAAAAAH////////////////////wA//4AAAAAAAAAAAAAAAAA/////////////////// + //AA//gAAAAAAAAAAAAAAAAD////////////////////+AB/+AAAAAAAAAAAAAAAAAP///////////// + ///////4AB/4AAAAAAAAAAAAAAAAB/////////////////////wAB/wAAAAAAAAAAAAAAAAH//////// + /////////////AAD+AAAAAAAAAAAAAAAAAf////////////////////+AADgAAAAAAAAAAAAAAAAA/// + //////////////////4AAAAAAAAAAAAAAAAAAAAA/////////////////////wAAAAAAAAAAAAAAAAAA + AAA/////////////////////AAAAAAAAAAAAAAAAAAAAAB////////////////////+AAAAAAAAAAAAA + AAAAAAAAB////////////////////4AAAAAAAAAAAAAAAAAAAAAD////////////////////wAAAAAAA + AAAAAAAAAAAAAAD/////////////h//////AAAAAAAAAAAAAAAAAAAAAAH////////////8B/////8AA + AAAAAAAAAAAAAAAAAAAAH////////////wB/////4AAAAAAAAAAAAAAAAAAAAAAP////////////AB// + ///gAAAAAAAAAAAAAAAAAAAAAAf///////////8AD////8AAAAAAAAAAAAAAAAAAAAAAAf////////// + /4AD////gAAAAAAAAAAAAAAAAAAAAAAA////////////gAD///8AAAAAAAAAAAAAAAAAAAAAAAB///// + ///////AAD///gAAAAAAAAAAAAAAAAAAAAAAAD///////////+AAD//8AAAAAAAAAAAAAAAAAAAAAAAA + D///////////4AAD//gAAAAAAAAAAAAAAAAAAAAAAAAH///////////wAAD/8AAAAAAAAAAAAAAAAAAA + AAAAAAP///////////AAAD/gAAAAAAAAAAAAAAAAAAAAAAAAAf//////////+AAAD8AAAAAAAAAAAAAA + AAAAAAAAAAAA///////////4AAADgAAAAAAAAAAAAAAAAAAAAAAAAAB///////////wAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAD///////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////////+AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAP//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////// + //+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH// + ////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAf//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAB//////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////gAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAH//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////8AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAf/////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////gH///gAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAD////+AB///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////wAB//+A + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////AAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///8A + AD//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////wAAH/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD + ////gAAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///+AAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP///4AAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gAAD+AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAB///+AAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4AAAfAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAH///gAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//+AAADgAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAA///4AAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///gAAA4AAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAD//+AAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//4AAAMAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//gAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8A + AACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//wAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAD/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAD+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAHAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAB/gAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAB/4AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAP/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAB + /+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AD8AB//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAP/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA/AB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8Af//gAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAfwD//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/A///gAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP8H//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/x///AAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAH/f//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///+AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///8AAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///4 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + A///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/4AAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/+AAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAA//4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//wAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////4AA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///// + ///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH///////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + f///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAH///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////+AAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAD///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAB///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////+AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAA///////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////// + /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/ + //////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA///////AAAHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////8AAH/gAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAf//////wAH//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////gH//8AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAf///////H///4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD////////////wAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAP////////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////////////+AAAAA + AAAAAAAAAAAAAAAAAAAAAAAAP////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAB///////////// + gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + /////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/// + //////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAH////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAA + AA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAAAAAAA + AAAAAAA/////////////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////AAAAAAAAAAAAAAAAA + AAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////gAAAAAAAAAAA + AAAAAAAAAAAAAAAAA/////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wAAAAAA + AAAAAAAAAAAAAAAAAAAAAA/////////////+AAAAAAAAAAAAAAAAAAAAAAAAAAAAH/////////////wA + AAPAAAAAAAAAAAAAAAAAAAAAAAA//////////////AAAD+AAAAAAAAAAAAAAAAAAAAAAAP////////// + ///4AAB/+AAAAAAAAAAAAAAAAAAAAAAB//////////////AAAf/4AAAAAAAAAAAAAAAAAAAAAAP///// + ////////4AAP//gAAAAAAAAAAAAAAAAAAAAAD//////////////gAD//+AAAAAAAAAAAAAAAAAAAAAAf + /////////////+AB///wAAAAAAAAAAAAAAAAAAAAAB//////////////wAf///AAAAAAAAAAAAAAAAAA + AAAAD//////////////gP///8AAAAAAAAAAAAAAAAAAAAAAP//////////////D////gAAAAAAAAAAAA + AAAAAAAAAA///////////////////+AAAAAAAAAAAAAAAAAAAAAAD///////////////////wAAAAAAA + AAAAAAAAAAAAAAAP///////////////////AAAAAAAAAAAAAAAAAAAAAAA///////////////////8AA + AAAAAAAAAAAAAAAAAAAAD///////////////////gAAAAAAAAAAAAAAAAAAAAAAH//////////////// + //+AAAAAAAAAAAAAAAAAAAAAAAf//////////////////wAAAAAAAAAAAAAAAAAAAAAAB/////////// + ////////AAAABgAAAAAAAAAAAAAAAAAH//////////////////8AAAAPwAAAAAAAAAAAAAAAAAf///// + /////////////gAAAD/wAAAAAAAAAAAB4AAAB//////////////////+AAAAf/4AAAAAAAAAAA/wAAAH + //////////////////wAAAH//4AAAAAAAAAAH/gAAAP//////////////////AAAA///gAAAAAAAAAA/ + /AAAA//////////////////8AAAP//+AAAAAAAAAAD/+AAAD//////////////////gAAB///4AAAAAA + AAAAP/8AAAP/////////////////+AAAf///gAAAwAAAAAA//4AAA//////////////////4AAD///+A + AAH//wAAAH//wAAD//////////////////AAA////4AAAf//AAAAf//gAAH/////////////////8AAH + ////gAAD//8AAAB///AAAf/////////////////gAA////+AAAf//4AAAH//+AAB//////////////// + /+AAP////4AAB///gAAA///4AAH/////////////////4AB/////gAAP//+AAAD///wAAf////////// + ///////AAf////+AAB///8AAAP///gAB/////////////////8AD/////4AAH///wAAA////AAH///// + ////////////gA//////gAA////gAAH///+AAP////////////////+AH/////+AAH///+AAAf///8AA + /////////////////4B//////4AAf///4AAB////4AD/////////////////gP//////gAD////wAAH/ + ///wAP/////////////////D//////+AAf////AAA/////gA/////////////////+///////4AD//// + 8AAD/////AD/////////////////////////gAP////4AAP////8AP////////////////////////+A + B/////gAA/////4Af////////////////////////4AP/////AAH/////wB///////////////////// + ////gA/////8AAf/////gH////////////////////////+AH/////wAB//////Af/////////////// + /////////4A//////gAH/////+D/////////////////////////gD/////+AA//////8f////////// + //////////////+Af/////4AD////////////////////////////////8D//////wAP//////////// + ////////////////////wP//////AA/////////////////////////////////B//////+AH/////// + /////////////////////////////////4Af////////////////////////////////////////gB// + ///////////////////////////////////////AH///////////////KAAAAIAAAAAAAQAAAQAgAAAA + AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgJwSAIOh+gCDof8Ag6H/AIKjLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvrwBab/8AWm//AFpv/wBa + b/8AWm+eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuiCwCZmQUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACApBwAg6H/AIOh/wCDof8Ag6H4AIOfJQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABecRsAWm/7AFpv/wBab/8AWm//AFpv/wBab9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDoK8Ag6H/AIOh2gCAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACDof8Ag6H/AIOh/wCDof8Ag6HjAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpufwBab/8AWm//AFpv/wBa + b/8AWm//AFpv/QBidg0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqgMAg6GYAIOh/wCDof8Ag6H/AIGgQwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCD + of8Ag6HaAIaeFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABAgAQAWm/iAFpv/wBab/8qprr/BmR6/wBab/8AWm//AFlwQgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgKoMAIOhtQCDof8Ag6H/AIOh/wCDof8Ahp8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKGbAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HPAIiZDwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBab/8AWm//Al1y/0jd + 8P8Xg5f/AFpv/wBab/8AWW94AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCDoM0Ag6H/AIOh/wCDof8Ag6H/AIOh8wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoHEAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6HDAICZCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm+1AFpv/wBab/8bi6D/T+n8/yehtv8AWm//AFpv/wBZb64AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF + oiwAg6HhAIOh/wCDof8Ag6H/AIOh/wCDof8AhKKZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIGiRwCDof8Ag6H/CY+s/yWzzP8Ag6H/AIOh/wCDof8Ag6K3AICqBgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFprHwBab/0AW3H/AF1z/zO9 + 0v9D2e7/LLPJ/wBief8AYXf/AF926QCAoh4AkpIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOi8ACDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + olIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKQcAIOh/wCD + of8Ag6H/SuP2/yGtx/8Ag6H/AIOh/wCDof8Ag6GpAKqqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAICZCgCC + oDsAg6JrAIOhkACDoK8AfpvnAICd/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6HqAIOi0gCCoLIAgqCJAIKiWgCFoiwAgL8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIShXwCD + ofkAg6H/AIOh/wGEov8grMb/AIOh/wCDof8Ag6H8AIiZDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZmYFAFxwGQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6LwAIOh/wCDof8+0+j/Tuj7/xynwv8Ag6H/AIOh/wCD + of8Ag6GaAP//AQCAnxgAgqJgAIOhngCDotIAg6H7AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofUAhKC6AIOhdwCCnzUAgIACAP//AQCDoX8Ag6H+AIOh/wCDof8EiKX/OMvh/yq50f8Ag6H/AIOh/wCD + ocMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABZb78AWm//AFpv/QBabz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + ocYAg6H/AIOh/zHB2f9P6fz/Tef6/xihvP8Ag6H/AIOh/wCDof8Ag6LoAIOh/gCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCCoeUAg6HWAIOh/wCD + of8Ag6H/CI2q/z/U6v9P6fz/FJ24/wCDof8Ag6H/AIOhfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASW0HAFpv/wBab/8AWm//AFpv/wBZ + cKAAWXMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgnACDof8Ag6H/JLHK/0/p/P9P6fz/TOX4/xSc + uP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIWj/wCOrP8AmLT/AKK9/wCq + xf8Cr8r/BbXQ/wW30f8Du9X/ALnU/wC10P8As87/ALHN/wCrxv8Aob3/AJm2/wCPq/8AhKL/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w2UsP9G3fH/T+n8/0vk+P8ChaT/AIOh/wCD + of8AhKE2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWm/4AFpv/wBab/8AWm//AFpv/wBab+wAWm9jAAAAAQAAAAAAAAAAAAAAAAAA + AAAAhKFyAIOh/wCDof8WoLv/T+n8/0/p/P9P6fz/SuP2/xCYtP8Ag6H/AIOh/wCDof8Ag6H/AIWj/wGX + s/8Nr8z/Gsff/yfY7/804vf/PeT5/0Lm+f9F5fj/OMzh/yayyv9M5fj/T+n8/0/p/P9P6fz/TOj7/0rn + +/9F5/r/PuX5/zPi9/8o3/T/Htzz/xPW7v8GxN7/ALLO/wCeuv8Aiaf/AIOh/wCDof8Ag6H/AIOh/wCD + of8Unbj/SuL2/0/p/P9P6fz/OMvh/wCDof8Ag6H/AIOh7QCAgAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabz4AWm//AFpv/wBa + b/8AWm//AFpv/wBab/8AWm/DAF1uLAAAAAAAAAAAAAAAAACDn0gAg6H/AIOh/wmPrP9P6fz/T+n8/0/p + /P9P6fz/SOH0/w2TsP8Ag6H/AIOh/wSLqf8+5Pn/Ten8/0/p/P9P6fz/T+n8/03m+v82x93/G6O8/wSF + oP8Af5v/AH+b/zC/1f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9B5vr/L+D2/xfK4v8Biqj/AIOh/wCDof8Ag6H/HanD/03n+v9P6fz/T+n8/0/p/P8hr8j/AIOh/wCD + of8Ag6CnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbb8UAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AFpviABm + ZgoAgqExAIOhxgCDof8Ag6H/AIOh/0vk9/9P6fz/T+n8/0/p/P9P6fz/Rt3y/wCDof8Ag6H/FZ+5/0/p + /P9P6fz/T+n8/0ri9v8otcz/CIql/wB/m/8Af5v/AH+b/wB/m/8Af5v/DpKs/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P880Ob/BIil/wCDof8Ag6H/AIOh/ye1 + zv9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCEobgAgJ8QAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlxTQBa + b/8AWm//AFpv/w92iv8AWm//AFpv/wBab/8AXnT/AHSP8wCDof4Ag6H/AIOh/wCDof8Ag6H/PtPp/0/p + /P9P6fz/T+n8/0/p/P9P6fz/S+T4/zrO5P9N5/n/T+n8/0zl+P8qt87/BYei/wB/m/8Af5v/AH+b/wB/ + m/8djqb/NZqw/wB/m/8Af5v/Hr7U/ynT6P8s1ej/L9bq/zPY7P883fD/R+P1/07m+f9P6fz/T+n8/0/p + /P9P6fz/T+n8/xiivf8Ag6H/AIOh/wKFo/8xwtr/T+n8/0/p/P9P6fz/T+n8/0/p/P9F3PD/AIOh/wCD + of8Ag6H/AIOh/wCDoesAg6BpAICAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAFlv0wBab/8AWm//H5Gm/zrC1v8Lb4P/AGuE/wCB + nv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8xwtn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P890uf/Co+q/wB/m/8Af5v/AH+b/wGAm/85nrb/j87f/7ro9/+Lydn/EYii/wB/m/8Ak67/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/BcLY/w7F2f8Yy+D/J9Pn/zrc8P9L5fj/Lb3U/wCDof8Fiqf/Os7k/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HFAIOiKQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWW9cAFpv/wBab/8CXXL/Ps/j/xOct/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weVsf8Ag6H/AIOh/ySx + yv9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+/8y3/T/EK/I/wCAnP8Af5v/AH+b/wB/m/9ApLv/pdzs/8fw + /v/I8P7/yPD+/7Xk8/86nLL/AH+b/wCAnP8At8//AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wLA1/8Rx9z/H8ng/zXY7f9O5/v/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/GaO9/wCD + of8Ag6H/AYqn/wCDof8Ag6H/AIOh/wCDof8Ag6H4AIShegCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6B5AIOh5QCDor8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgAQAWnDgAFpv/wBqgv8ChKL/AIOh/wCD + of8Ag6H/AIOh/wqfvP823fL/LLzT/wCDof8Ag6H/F6C7/0/p/P9P6fz/S+j7/zHh9v8S0+n/Acbd/wCf + uP8Af5v/AH+b/wB/m/8RiaT/jtDj/8fw/v/I8P7/yPD+/8jw/v/I8P7/yPD+/3rA0f8EgZ3/AH+b/wCb + tf8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wbE + 2f8e0Ob/OuDz/07o/P9P6fz/T+n8/0/p/P8Fiqf/AIOh/wCDof8zy+L/HMXd/wCJp/8Ag6H/AIOh/wCD + of8Ag6H/AIOhxgCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDofYAg6H/AIOh/wCD + of8Ag6GoAIahJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABecnIAdI//AIOh/wCDof8Ag6H/AIOh/wGMqP8nz+f/TOj7/0/p/P83yuD/AIOh/wCD + of8Lkq7/ROb5/yLc8/8Ezeb/AMTb/wDB2P8Apb//AH+b/wB/m/8Af5v/NJ63/7bn9//H8P7/yPD+/8jw + /v/G8P7/uuz+/8jw/v/I8P7/ptzs/yeTqv8Af5v/AIKe/wC81P8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA1/8AwNf/BsXc/yHT6f9E5fj/T+n8/xKa + tf8Ag6H/CI6r/03n+f9P6fz/PuP4/xGvyf8Ag6H/AIOh/wCDof8Ag6H/AIOi8ACColgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOh8gCDof8Ag6H/AIOh/wCDof8Ag6H7AIShmwCEnh0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6IhAIOh0QCDof8Ag6H/AIOh/wCD + of8NqMT/P+P4/0/p/P9P6fz/T+n8/0fe8v8Ag6H/AIOh/wicuf8Bzub/AMTb/wDC2f8Awtn/ALTM/wCA + nP8Af5v/AH+b/0Glvv+96/z/xO/+/8bw/v/H8P7/vO3+/6bn/v+i5v7/uOz+/8Lw/v+77vz/W6/D/wB/ + m/8Af5v/AKO9/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDA1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wDA + 1/8AwNf/AMDX/wDA1/8AwNf/AMDX/wHC2P8a0Ob/Ot3x/zXH3v9I3/P/T+n8/0/p/P9P6fz/T+n8/zPZ + 7/8HkrD/AIOh/wCDof8Ag6H/AIOh/wCCoI8AqqoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhqQqAIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh9wCDoo4AgKYUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOhRACDoewAg6H/AIOh/wCDof8Ah6T/Hsrh/03p+/9P6fz/T+n8/0/p/P9P6fz/Suf7/xzP + 5v8Bs83/AMPa/wDD2f8Aw9n/AMPZ/wDB1/8AiqX/AH+b/wB/m/8znrj/u+v8/8Lv/v/D7/7/w+/+/6/q + /v+i5v7/oub+/6Hl/f+g5/3/vO/9/7bu/f+Q0+P/E4mi/wB/m/8AiKP/AMHY/wDC2f8Awtn/AMLZ/wDC + 2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/wDB2P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Awtn/Es3j/zjg9f9P6fz/T+n8/0/p/P9P6fz/T+n8/0jn+v8YsMr/AIOh/wCDof8Ag6H/AIOh/wCD + orcAiJkPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KWAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + ofMAg6GAAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoFYAg6H5AIOh/wCDof8Ag6H/ApGt/y/b + 8P9P6fz/T+n8/0/p/P9P6fz/T+n8/zLh9/8G1e7/AM3l/wDE2/8AxNv/AMTb/wDE2/8AxNv/AKS+/wB/ + m/8Af5v/EYql/6/m+f+/7v7/we7+/8Du/v+p6P7/oub+/6Lm/v+g5f3/l+T9/43k/P+s7P3/sO79/6bq + +f9Bobf/AH+b/wB/m/8Arsb/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD + 2f8Aw9n/AMPZ/wDD2f8Aw9n/AMPZ/wDD2f8Awtn/AMLZ/wDC2f8Awtn/AMLZ/xXP5f9B5Pf/T+n8/0/p + /P9P6fz/T+n8/0/p/P8uz+b/A4ak/wCDof8Ag6H/AIOh/wCCoNUAg6AjAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG + nhUAg6HxAIOh/wCDof8Fiqf/CY+s/wCDof8Ag6H/AIOh/wCDof8Ag6HrAIOicwCOqgkAAAAAAAAAAAAA + AAAAgqBmAIOh/ACDof8Ag6H/AIOh/wSfu/864vf/T+n8/0/p/P9P6fz/T+n8/0jn+/8Z2/L/ANLq/wDI + 4P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDC2v8AhKD/AH+b/wB/m/93x97/ve3+/77u/v+/7v7/qej+/6Lm + /v+i5v7/n+X9/5bk/f+M4/z/g+P8/4/n/P+q7f3/pO38/3rJ2v8Fgp3/AH+b/wCQq/8AxNv/AMTb/wDE + 2/8AxNv/AMTb/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD2v8Aw9r/AMPa/wDD + 2v8Aw9r/AMPa/wDD2v8Aw9r/AMPZ/wHF2/8g1er/Sej7/0/p/P9P6fz/T+n8/0/p/P9C4vX/D5m0/wCD + of8Ag6H/AIOh/wCDoewAg59AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoXcAg6H/AIOh/wKFpP9C2e3/LL3U/weL + qf8Ag6H/AIOh/wCDof8Ag6H/AIOh5ACCoGYAgL8EAIShcgCDof4Ag6H/AIOh/wCDof8IqMP/QOX5/0/p + /P9P6fz/T+n8/0/p/P895Pn/CNfv/wDQ6P8AyN//AMfe/wDH3v8Ax97/AMbd/wDG3f8Axt3/AK3F/wB/ + m/8Af5v/FIyo/7Xq/f+87f7/ve3+/7Lq/v+i5v7/oub+/57l/f+V5P3/i+P8/4Li/P954vv/ceH7/6Ds + /P+e7Pz/keT0/ymUrP8Af5v/AH+b/wC30P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF + 3P8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE2/8AxNv/AMTb/wDE + 2/8Fyd//M97y/0/p/P9P6fz/T+n8/0/p/P9N6Pv/ILHK/wCDof8Ag6H/AIOh/wCDofoAgqJYAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICfCACDoeAAg6H/AIOh/xumwP9P6fz/S+T4/yi3z/8Fiaf/AIOh/wCDof8Ag6H/AIOh/wCD + oOoAhKH+AIOh/wCDof8Ag6H/Ca3J/0Tm+v9P6fz/T+n8/0/p/P9P6fz/L+H2/wLV7f8Azeb/AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8Al7H/AH+b/wB/m/9UtM3/uez+/7rt/v+87f7/pef+/6Lm + /v+d5f3/lOT9/4rj/P+B4vz/eOH7/27g+/9k4Pr/guf7/5jr+/+T6/v/YLzP/wB/m/8Af5v/AJmz/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axt3/AMbd/wDG3f8Axdz/AMXc/wDF + 3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/AMXc/wDF3P8Axdz/HNPp/0vn+v9P6fz/T+n8/0/p + /P9P6fz/KsLZ/wCDof8Ag6H/AIOh/wCDofwAgqFcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKiWACDof8Ag6H/AIOh/z3S + 6P9P6fz/T+n8/0nh9f8kssv/A4ak/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmyzf9E5vr/T+n8/0/p + /P9P6fz/T+n8/yTe9P8A1O3/AM3l/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wCF + oP8Af5v/AH+b/4nS6f+47P7/uez+/7br/v+i5v7/nOX9/5Pk/f+J4/z/gOL8/3fh+/9t4Pv/ZN/6/1rf + +f9d4Pn/kev7/4zq+/9+2+3/FYqj/wB/m/8AgZz/AMDY/wDI3/8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH + 3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG + 3f8Axt3/AMbd/wDG3f8Axt3/Ccrh/z3h9f9P6fz/T+n8/0/p/P9P6fz/M8/l/wKFpP8Ag6H/AIOh/wCD + ofwAg6BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAA//8BAIOhyACDof8Ag6H/EZm0/0/p/P9P6fz/T+n8/0/p/P9H3vL/IKzG/wGF + ov8Ag6H/AIOh/wCDof8Ag6H/EJq2/zzQ5v9P6fz/T+n8/03p/P8c2/L/ANTt/wDN5v8AyuL/AMri/wDK + 4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AxNz/AH+b/wB/m/8CgZz/qef8/7fs/v+47P7/r+n+/5vl + /f+S5P3/ieP8/3/i/P924fv/bOD6/2Pf+v9Z3vn/UN75/0bd+P955/r/her6/4Do+v9Dqb//AH+b/wB/ + m/8Aor3/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI + 3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMjf/wDI3/8AyN//AMfe/wDH3v8Ax97/A8jf/zPa + 7v9P6fz/T+n8/0/p/P9P6fz/PNft/wWKp/8Ag6H/AIOh/wCDof4AgqBmAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKM6AIOh/gCD + of8Ag6H/NMfd/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/xynwv8BhKL/AIOh/wCDof8Ag6H/AIOh/xih + vP9K5fj/GNrx/wDU7f8Azeb/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wC+ + 1/8Af5v/AH+b/xOLp/+u6f7/tuv+/7fs/v+l6P3/keT9/4jj/P9+4vv/deH7/2vg+v9i3/r/WN75/0/d + +f9F3fj/PNz4/1Hg+P9/6fr/fun6/3HS4/8Ggp3/AH+b/wCGof8Ax+D/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI + 4P8AyOD/AMjg/wDI4P8AyOD/AMjg/wDI4P8AyOD/Acjg/yrV6f9O5/r/T+n8/0/p/P9P6fz/QNvw/wWJ + p/8Ag6H/AIOh/wCDof4Ag6JrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GrAIOh/wCDof8KkK3/Teb5/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0HX7P8Yobz/AIOh/wCDof8Ag6H/AIOh/w+91v8A1O3/AM7m/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AL7X/wB/m/8Af5v/HZKt/63p/v+16/7/r+v9/5rm + /P+H4/z/feL7/3Ph+/9q4Pr/Yd/6/1je+f9O3fn/RNz4/zvc+P8y2/f/K9v3/3jo+v9+6fr/fuf4/y2a + sf8Af5v/AH+b/wCvyP8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK + 4v8AyuL/AMri/wDK4v8AyuL/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ + 4f8AyeH/AMnh/yDT6f9N5Pj/T+n8/0/p/P9P6fz/Ptrv/wWJp/8Ag6H/AIOh/wCDof4AhKBbAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA + niIAg6H4AIOh/wCDof8qutL/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P890uj/FJ23/wCD + of8Bh6T/AMfh/wDP5/8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AxN3/AH+b/wB/m/8akKv/q+j+/6vq/f+m6v3/kub8/3zi+/9y4fv/aeD6/2Df+v9X3vn/Td35/0Pc + +P862/j/Mdr3/yfa9/8n2vf/WOL5/37p+v9+6fr/Ycfa/wB/m/8Af5v/AI6p/wDM5P8AzOT/AMzk/wDM + 5P8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL4/8Ay+P/AMvj/wDL + 4/8Ay+P/AMvj/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/wDK4v8AyuL/AMri/xvR5/9M4/b/T+n8/1vr + /P9h6/z/R9ru/wKFo/8Ag6H/AIOh/wCDofkAg6JCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYsAg6H/AIOh/wSJpv9I4fT/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/HM7m/wDM5v8A0en/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDK5P8Af5v/AH+b/weEn/+d5fz/oun9/53o + /f+N5vz/ceH7/2jg+v9f3/r/Vd75/0zd+f9C3Pj/Odv4/zDa9/8n2vf/J9r3/yfa9/813Pj/fun6/37p + +v985PX/GIym/wB/m/8Af5v/ALrT/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AMzk/wDM + 5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM5P8AzOT/AMzk/wDM + 5P8AzOT/AMvj/wDL4/8EzOP/Qdjq/47p9P/J9/z/0fn+/9H5/v/R+f7/t+72/2m2yP8Vjan/AIOh/wCD + ovAAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICfEACDoesAg6H/AIOh/yGux/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/y/h + 9v8A1O3/ANLr/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wCHo/8Af5v/AH+b/3vU7f+Z6P3/k+f8/43n/P9r4fr/Xt/6/1Te+f9L3fn/Qdz4/zjb + 9/8v2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/9p5fn/fun6/37p+v9KuM7/AH+b/wB/m/8AmLL/AM3m/wDN + 5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3m/wDN5v8Azeb/AM3l/wDN5f8AzeX/AM3l/wDN + 5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8AzeX/AM3l/wDN5f8EzuX/VN3u/6vv9v+98vj/vfL4/8b0 + +v/R9/z/0fn+/9H5/v/R+f7/xu70/7nd5f9escT/AoSi/wCDoeoAgKMkAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWjGQCDopYAg6GYAISgdACB + o0UAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCDof8Ag6H/AYWi/0LZ + 7f9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9D5vr/AdXt/wDT7P8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/AJiy/wB/m/8Af5v/QrHL/43m + /P+J5vz/g+b7/33l+/9l4fr/S935/0Dc+P832/f/Ltr3/yfa9/8n2vf/J9r3/yfa9/8n2vf/J9r3/0Xf + +P9+6fr/fun6/3bd7/8Ggp3/AH+b/wCAnP8AxNz/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO + 5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM7m/wDO5v8Azub/AM3m/wDN5v8Azeb/AM3m/wDN + 5v8Bzeb/Rtvt/6fu9v+98vn/oOz1/3fd6/9Sytz/RcDT/0u80P9jx9f/fNXk/6vs9f/Q+f7/wunv/73f + 5/+Qydb/D4qn/wCEodsAgJ8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKG+AIOh/wCDof8Ag6H/AIOh/wCDofsAg6G1AIGjRQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAmZkFAIOh4gCDof8Ag6H/F6G8/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj8/wfW + 7v8A1O3/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR + 6f8A0en/ANHp/wDR6f8Ascv/AH+b/wB/m/8Af5v/euD4/37l+/955fv/c+T6/3Dk+f9l4/n/Rt74/y3a + 9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa9/8n2vf/Kdr3/3fo+v9+6fr/fun6/zOmvf8Af5v/AH+b/wCi + vP8A0Oj/ANDo/wDQ6P8A0Oj/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8Az+f/AM/n/wDP5/8Az+f/L9jr/5nr9f+i7PX/W9Dg/xunwP8Ah6P/AIGe/wCB + nv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/xeQrP9zz97/td/o/73f5/+e0Nv/E4yo/wCCoLIA//8BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoeYAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOhowCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6HeAIOh/wCD + of8Ag6H/Os7k/0/p/P9P6fz/T+n8/0/p/P8c3PL/ANTt/wDT7P8A0+z/ANLr/wDS6/8A0uv/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDN5v8Agp3/AH+b/wB/ + m/8MiqX/Rb/Y/2vi+f9q4/n/ZeP5/2Hj+f9d4/n/TeH4/zDc9/8n2vf/J9r3/yfa9/8n2vf/J9r3/yfa + 9/8n2vf/VuL5/37p+v9+6fr/Z9Ll/wB/m/8Af5v/AIOf/wDN5f8A0en/ANHp/wDR6f8A0en/ANHp/wDR + 6f8A0en/ANHp/wDR6f8A0en/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/ANDo/wDQ6P8A0Oj/G9Xq/4Ho + 9P+J5O//NbbM/wGHo/8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCBnv8AgZ7/AIGe/wCB + nv8Xkaz/fsDQ/73f5/+f0Nz/BYWj/wCDoHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIOhxQCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOhzACJ + nQ0AAAAAAAAAAAAAAAAAAAAAAIShXwCDof8Ag6H/AIOh/wCDof8OlbH/Tuj7/0/p/P9P6fz/O+T4/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wCeuf8Af5v/AH+b/wB/m/8Af5v/EZKu/0TL5P9a4vn/VuL5/1bi + +f9b4/n/XeP5/0ng+P8r2/f/J9r3/yfa9/8n2vf/J9r3/yfa9/8z3Pf/fun6/37p+v9+6Pn/HZWu/wB/ + m/8Af5v/AK7I/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS + 6v8A0ur/ANLq/wDR6f8A0en/FNXr/2rk8v972uj/Jqa9/wCBnv8AgZ7/AIyp/wCatf8Apb//AKrF/wCr + xv8AqsT/AKO+/wCYtP8AhqP/AIGe/wCBnv8AgZ7/AIGe/wCBnv8BgZ7/XK7C/7ze5v9XrcH/AIOh/ACB + oUEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6GjAIOh/wCD + of8Ag6H/ALbR/wCbuP8Ag6L/AIOh/wCDof8Ag6H/AIOhmAAAAAAAAAAAAAAAAACAnwgAg6HiAIOh/wCD + of8Ag6H/AIOh/wCDof8xwdn/T+n8/0zo/P8D1u3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wC0 + zv8AiKP/AH+b/wB/m/8Af5v/AH+b/xafu/9B1O3/VOL5/1ji+f9c4/n/X+T5/17j+f9B3vj/KNr3/yfa + 9/8n2vf/J9r3/yfa9/9m5fn/fun6/37p+v9Sxdv/AH+b/wB/m/8Ai6b/ANLr/wDT7P8A0+z/ANLr/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8A0uv/Fdbt/2ff7v9uz9//Hpix/wCJ + pv8Aor3/ALnS/wDM5f8A0ur/ANLq/wDS6v8A0ur/ANLq/wDS6v8A0ur/ANLq/wDR6f8Aw9v/EqrE/waI + pf8AgZ7/AIGe/wCBnv8AgZ7/QKG3/7DZ4v8Qi6f/AIOg6ACAnRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDoZAAg6H/AIOh/wCEov8A1O3/ANTt/wDF3v8Aiqf/AIOh/wCD + of8Ag6H7AIOiIQAAAAAAAAAAAIOgcQCDof8Ag6H/AIOh/wOTsP8Ag6H/AIOh/wiNqv9L5Pj/Htzy/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDQ6f8AqML/AIKe/wB/m/8Af5v/AH+b/wGB + nf8jrsj/T9z0/1nj+f9c4/n/YOT5/2Tk+f9c4/n/ON34/yfa9/8n2vf/J9r3/0Lf+P996fr/fun6/3rl + 9/8Kh6P/AH+b/wB/m/8AudT/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDT7P8A0+z/ANPs/wDT + 7P8A0+z/JNnv/1nU5v9SvM//D5ex/wCow/8Awdv/ANPs/wDT7P8A0+z/ANPs/wDT7P8A0+z/ANPs/wDS + 6/8A0uv/ANLr/wDS6/8A0uv/ANLr/wDS6/8C0+v/O+D0/y691P8Giab/AIGe/wCBnv8AgZ7/Tqi8/3G6 + y/8Ag6H/AIOhwwCZmQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKigwCD + of8Ag6H/AIOh/wDS6/8A1O3/ANTt/wC30v8Ag6H/AIOh/wCDof8AgqGNAAAAAACAvwQAg6HhAIOh/wCD + of8Aiaf/KdTq/wOHpf8Ag6H/AIOh/yCzzP8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8C1O3/AtTt/wLU7f8Dy+T/AqC6/wCAnP8Af5v/AH+b/wB/m/8Fh6P/MLzV/1ji9/9e4/n/YeT5/2Xl + +f9o5fn/VuL5/y/b9/8n2vf/Ot34/3ro+v9+6fr/fun6/zy2zv8Af5v/AH+b/wCUr/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8b2e//PtTo/0zE1v84tcv/Aq/J/wDJ4v8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANPs/wDT + 7P8I1u7/SOb5/0rh9f8hq8T/AYKf/wCBnv8AgZ7/Wq7B/yGTrf8Ag6H/AIOihgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KGAIOh/wCDof8Ag6H/ANLq/wDU7f8A1O3/ANPs/wCP + rP8Ag6H/AIOh/wCDodEAAAAAAIKgVgCDof8Ag6H/AIOh/wqyzf9N6fz/J7bP/wCDof8Ag6H/AIek/wDS + 6v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wPV7f8E1e3/BdXt/wbV7f8H1e3/B9Xt/wjV7v8I1e7/B8Xe/wKV + sP8Af5v/AH+b/wB/m/8Af5v/DZGs/0DK4v9f5Pn/YuT5/2bl+f9q5fn/beb6/3Hn+v905/r/d+j6/3zp + +v9+6fr/Z9zw/wB/m/8Af5v/AH+b/wDD3f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BMri/wa9 + 1v8Av9r/ANDp/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8T2fD/Ten8/0/p/P85y+L/Boil/wCB + nv8AgZ7/TKe8/wCDof8Ag6H9AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oZAAg6H/AIOh/wCDov8A1O3/ANTt/wDU7f8A1O3/AKfD/wCDof8Ag6H/AIOh/QCOqgkAg6HHAIOh/wCD + of8AhaP/Md70/0/p/P9M5fj/CY+r/wCDof8Aiqf/ANPs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8D1e3/BNXt/wXV7f8H1e3/B9Xt/wnW + 7v8J1u7/Ctbu/wvW7v8M1u7/DNbu/w3W7v8N1u7/Dtbu/wq60/8Ci6f/AH+b/wB/m/8Af5v/AH+b/xqf + uv9Q1u3/ZOT5/2fl+f9r5vr/bub6/3Ln+v915/r/eOj6/1je9P8XqMT/AH+b/wB/m/8Af5v/AZ65/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8h3fP/T+n8/0/p/P9F3fD/EJex/wCBnv8OiaP/JpWv/wCDof8Ag6HmAIaeFQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhqACDof8Ag6H/AIyq/wDU7f8A1O3/ANTt/wDU + 7f8AqcX/AIOh/wCDof8Ag6H/AIOiQgCDof8Ag6H/AIOh/weow/9N6Pz/T+n8/0/p/P9C3PD/A6/L/wDH + 4f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wPV + 7f8E1e3/BtXt/wfV7f8J1u7/Ctbu/wvW7v8M1u7/Ddbu/w7W7v8P1+7/ENfu/xDX7v8Q1+7/Edfu/xLX + 7v8S1+7/Etfu/xPV7P8Kr8n/AYSg/wB/m/8Af5v/AH+b/wKAnf8pr8j/Xt/0/2jl+f9s5vr/cOb6/2Lk + +P8oudP/AoWg/wB/m/8Af5v/AH+b/wCAnf8DpsD/BNXt/wLU7f8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8x4fb/T+n8/0/p + /P9M5fj/FJ23/wCBnv8Yjqn/A4Si/wCDof8AgqK0AP//AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg6HQAIOh/wCDof8AmbX/ANTt/wDU7f8A1O3/ANTt/wCfu/8Ag6H/AIOh/wCDoe0Ag6GQAIOh/wCD + of8Ag6H/JtXt/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8B1O3/AtTt/wTV7f8G1e3/B9Xt/wnW7v8K1u7/DNbu/w3W7v8O1u7/ENfu/xDX + 7v8R1+7/E9fu/xPX7v8U1+7/Fdju/xXY7v8W2O//Ftjv/xfY7/8X2O//F9jv/xfY7/8V0ej/CqS+/wCA + nf8Af5v/AH+b/wB/m/8Gh6H/O77W/2Lj+P85yOH/B46q/wB/m/8Af5v/AH+b/wB/m/8Dl7L/Ccjh/wnW + 7v8I1e7/B9Xt/wXV7f8D1e3/AtTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7f8/5fn/T+n8/0/p/P9M5vn/EZ23/wCCoP8EhaL/AIOh/wCD + of8AgqJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCCoPwAgqD/AIOh/wCpxf8A1O3/ANTt/wDU + 7f8A1O3/AJCt/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wGRrv9F5/r/T+n8/0/p/P9M6Pv/Bdbu/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/A9Xt/wTV7f8H1e3/CNXu/wrW + 7v8M1u7/Ddbu/w7W7v8Q1+7/Edfu/xPX7v8U1+7/Fdju/xbY7/8X2O//GNjv/xjY7/8Z2O//Gtjv/xrY + 7/8b2e//G9nv/xvZ7/8c2e//HNnv/xzZ7/8b2e//F8ri/wiYsv8Af5v/AH+b/wB/m/8Af5v/AYCb/wB/ + m/8Af5v/AH+b/wB/m/8DjKj/Db7X/xDX7v8P1+7/Dtbu/wzW7v8L1u7/Cdbu/wfV7f8F1e3/BNXt/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wrX + 7/9L6Pv/T+n8/0/p/P9N5/r/Cpm1/wCCof8Ag6H/AIOh/wCDoewAhp4VAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKBZAIKg/wCBnv8AgJ7/ALvU/wDU7f8A1O3/ANTt/wDU7f8AhqT/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/F77X/0/p/P9P6fz/T+n8/zHh9v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8B1O3/A9Xt/wXV7f8H1e3/Cdbu/wvW7v8M1u7/Dtbu/xDX7v8R1+7/E9fu/xTX7v8V2O7/F9jv/xjY + 7/8a2O//Gtjv/xvZ7/8c2e//Hdnv/x7Z7/8f2e//H9nv/x/Z7/8f2e//INnv/yDZ7/8g2e//INnv/yDZ + 7/8g2e//H9nv/xbA2P8Fjqj/AH+b/wB/m/8Af5v/AH+b/wB/m/8ChaD/DbDJ/xfW7f8W2O//Fdju/xPX + 7v8S1+7/ENfu/w7W7v8N1u7/C9bu/wnW7v8H1e3/BdXt/wTV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/x7d8/9P6fz/T+n8/0/p/P9I5Pf/Ao+q/wCD + of8Ag6H/AIOh/wCEoZsAAAAAAAAAAAAAAAAAAAAAAAAAAACCorQAg6H/AIKg/wCDof8AyuP/AMzk/wDO + 5v8A0uv/ANTt/wCcuP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof813vT/T+n8/0/p/P9P6fz/Edjw/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xDX + 7v8T1+7/FNfu/xXY7v8X2O//Gdjv/xrY7/8b2e//HNnv/x7Z7/8f2e//INnv/yHa7/8h2u//Itrv/yPa + 7/8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yTa8P8k2vD/JNrw/yLZ7f8UtMz/A4ai/wB/ + m/8BgJ3/DaS+/xzS6P8c2e//HNnv/xrY7/8Z2O//GNjv/xbY7/8U1+7/E9fu/xDX7v8P1+7/Ddbu/wvW + 7v8J1u7/B9Xt/wXV7f8D1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/zjj+P9P6fz/T+n8/0/p/P8r1uv/AIil/wCDof8Ag6H/AIOh/gCEnzgAAAAAAAAAAAAA + AAAAhKUfAIOh+wCDof8Ag6H/AJy5/wDU7f8A0uv/AM/n/wDL4/8Ay+P/AMfg/wCcuf8AhKL/AIOh/wCD + of8Ag6H/BZy4/03p/P9P6fz/T+n8/0Hl+v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wTV + 7f8H1e3/Cdbu/wvW7v8N1u7/D9fu/xHX7v8T1+7/Fdju/xbY7/8Y2O//Gtjv/xvZ7/8d2e//H9nv/yDZ + 7/8h2u//Itrv/yTa8P8k2vD/Jdrw/yba8P8m2vD/J9vw/yjb8P8p2/D/Kdvw/ynb8P8p2/D/Kdvw/ynb + 8P8p2/D/Kdvw/ynb8P8o2/D/J9vw/yba8P8k1Ov/E6/H/x7L4v8k2vD/Itrv/yHa7/8f2e//H9nv/x3Z + 7/8b2e//Gtjv/xjY7/8W2O//Fdju/xPX7v8R1+7/D9fu/w3W7v8L1u7/Cdbu/wfV7f8E1e3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/C9fv/03p+/9P6fz/T+n8/03p + /P8IuNP/AIOh/wCDof8Ag6H/AIShuAAAAAAAAAAAAAAAAACDoZIAg6H/AIOh/wCDof8Au9X/ANTt/wDU + 7f8A1O3/ANTt/wDT7P8Azeb/AMvj/wDJ4P8Av9n/AK3J/wCsx/8a1+7/T+n8/0/p/P9P6fz/Jd70/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AdTt/wPV7f8F1e3/CNXu/wrW7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY + 7/8Z2O//Gtjv/xzZ7/8e2e//H9nv/yHa7/8j2u//JNrw/yXa8P8m2vD/KNvw/ynb8P8q2/D/K9vw/yvb + 8P8s2/D/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/y3c8P8t3PD/Ldzw/yzb8P8s2/D/K9vw/yvb + 8P8p2/D/Kdvw/yjb8P8m2vD/Jdrw/yTa8P8i2u//Idrv/x/Z7/8e2e//HNnv/xrY7/8Y2O//F9jv/xXY + 7v8T1+7/ENfu/w7W7v8M1u7/Ctbu/wfV7f8F1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Kd/0/0/p/P9P6fz/T+n8/yvg9f8Amrj/AIOh/wCDof8Ag6H+AIakKgAA + AAAAgKYUAIOh8wCDof8Ag6H/AI6q/wDS6v8A0uv/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDP + 5/8A0Oj/ANLr/zLh9v9L4/f/Os7k/z3S6P8LyOD/AMni/wDO5v8A0er/ANTt/wLU7f8E1e3/B9Xt/wnW + 7v8M1u7/Dtbu/xDX7v8T1+7/Fdju/xfY7/8Z2O//G9nv/xzZ7/8f2e//Idrv/yLa7/8k2vD/Jtrw/yfb + 8P8p2/D/Ktvw/yvb8P8s2/D/Ldzw/y7c8P8v3PD/MNzw/zDc8P8x3PD/Mtzx/zLc8f8y3PH/Mtzx/zLc + 8f8y3PH/Mtzx/zLc8f8x3PD/Mdzw/zDc8P8w3PD/L9zw/y3c8P8t3PD/LNvw/yvb8P8p2/D/KNvw/yba + 8P8l2vD/JNrw/yHa7/8g2e//Htnv/xzZ7/8a2O//GNjv/xbY7/8U1+7/Etfu/xDX7v8N1u7/C9bu/wnW + 7v8G1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8G1e7/S+j7/0/p + /P9P6fz/R+f6/wHG3/8Ag6H/AIOh/wCDof8Ag6CEAAAAAACEoHwAg6H/AIOh/wCDof8Ascv/ANLq/wDQ + 6P8Azub/ANDo/wDS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/RuX3/wqPrP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIim/wGMqf8Bj63/A5Sx/wSZtf8Fm7j/B5+6/wmlwf8LqcT/DazG/w+x + yv8Sts//FbrS/xe91f8aw9v/Hsbe/yLO5P8q2/D/K9vw/y3c8P8u3PD/MNzw/zDc8P8y3PH/Mtzx/zPd + 8f813fH/Nd3x/zXd8f823fH/Nt3x/zfd8f833fH/N93x/zfd8f833fH/Nt3x/zbd8f813fH/Nd3x/zTd + 8f8z3fH/Mtzx/zHc8P8w3PD/L9zw/y3c8P8s2/D/K9vw/ynb8P8n2/D/Jtrw/yTa8P8i2u//INnv/x7Z + 7/8c2e//Gtjv/xjY7/8V2O7/E9fu/xDX7v8O1u7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8s4PX/T+n8/0/p/P9P6fz/FNrw/wCZtf8Ag6H/AIOh/wCD + odYAgJ8QAIOh7ACDof8Ag6H/AIqn/wDS6/8A1O3/ANPs/wDS6v8Az+f/AM3l/wDM5P8Az+f/ANPs/wDU + 7f8A1O3/ANTt/xDZ8P9F3PD/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/w6f + uf8w3PD/Mdzw/zLc8f803fH/Nd3x/zbd8f833fH/ON3x/zne8f853vH/Ot7x/zre8f873vH/O97x/zve + 8f883vH/O97x/zve8f873vH/Ot7x/zne8f853vH/ON3x/zfd8f833fH/Nd3x/zXd8f8z3fH/Mtzx/zDc + 8P8v3PD/Ldzw/yvb8P8p2/D/KNvw/yba8P8k2vD/Idrv/x/Z7/8d2e//G9nv/xnY7/8W2O//FNfu/xHX + 7v8P1+7/DNbu/wnW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7Y + 7/9P6fz/T+n8/0/p/P8t4PX/ALLO/wCDof8Ag6H/AIOh/wCDoIwAg6H/AIOh/wCDof8Arcj/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANPs/wDQ6P8Azeb/AM7m/wDR6f8A1O3/It30/0/p/P8hr8j/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/zHY7f813fH/N93x/zjd8f853vH/Ot7x/zze + 8f883vH/Pd7x/z7e8f8+3vH/P9/x/z/f8f9A3/L/QN/y/0Df8v9A3/L/P9/x/z/f8f8+3vH/Pt7x/z7e + 8f883vH/PN7x/zve8f853vH/ON3x/zfd8f823fH/NN3x/zLc8f8x3PD/L9zw/y3c8P8r2/D/Kdvw/yfb + 8P8l2vD/I9rv/yHa7/8f2e//HNnv/xrY7/8X2O//Fdju/xLX7v8P1+7/Ddbu/wrW7v8H1e3/BNXt/wLU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/0rn+/9P6fz/T+n8/z/k+f8Awdv/AIOh/wCD + of8Ag6H/AIOh+QCDof8Ag6H/AIuo/wDP6f8A0+z/ANLr/wDS6/8A0+z/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDT7P8y4fb/T+n8/0/p/P8yw9r/AIak/wCDof8Ag6H/AIOh/xGZtP8/1On/QNbr/z3S + 6P86zuT/Ncfe/zHC2v8uvtb/KrnR/yazzf8jr8n/H6vF/xqlv/8Xobv/FJy4/xCYs/8Lkq//CI6r/wCD + of8arsf/ON3x/zne8f873vH/PN7x/z7e8f8+3vH/QN/y/0Hf8v9C3/L/Q9/y/0Pf8v9D3/L/RN/y/0Tf + 8v9E3/L/RN/y/0Tf8v9E3/L/Q9/y/0Pf8v9D3/L/Qt/y/0Hf8v9A3/L/P9/x/z7e8f883vH/O97x/zne + 8f843fH/N93x/zXd8f8z3fH/Mdzw/y/c8P8t3PD/K9vw/ynb8P8m2vD/JNrw/yHa7/8f2e//HNnv/xrY + 7/8Y2O//Fdju/xPX7v8Q1+7/Ddbu/wrW7v8H1e3/BNXt/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/O+T4/0/p/P9P6fz/Ten8/wLL5f8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8AtdD/ANTt/wDT + 7P8A0ur/ANHp/wDP5/8Az+f/AM/n/wDS6v8A0+z/ANTt/wDU7f8A1O3/ANTt/0Hm+v9P6fz/T+n8/0vo + +/8BxuD/AI+s/wCDof8Ag6H/AIOh/wqQrf8/1Or/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9O6fz/Ot/y/zre8f883vH/Pt7x/z/f8f9B3/L/Qt/y/0Pf + 8v9E3/L/ReDy/0bg8v9H4PL/SODy/0jg8v9I4PL/SeDy/0ng8v9J4PL/SeDy/0jg8v9I4PL/SODy/0fg + 8v9G4PL/ReDy/0Tf8v9D3/L/Qt/y/0Hf8v8/3/H/Pt7x/zze8f863vH/Od7x/zfd8f813fH/Mtzx/zDc + 8P8v3PD/ON3x/0Df8v9F4PL/SODy/0bg8v9C3/H/Od7x/y3b8P8f2fD/Fdju/xPX7v8Q1+7/Ddbu/wrW + 7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p/P9P6fz/B8/o/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AJWy/wDT7P8A1O3/ANTt/wDT7P8A0uv/ANLq/wDR6f8Az+f/AM3m/wDM + 5P8AzOT/AM3m/wDR6f8C1O3/Tuj8/0/p/P9P6fz/POT5/wDU7f8Az+n/AJ26/wCDof8Ag6H/AIOh/wKF + pP8vwNf/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0vn + +v883vH/Pt7x/0Df8v9C3/L/Q9/y/0Xg8v9G4PL/SODy/0ng8v9K4PL/SuDy/0vh8v9M4fL/TeHy/03h + 8v9N4fL/TeHy/03h8v9N4fL/TeHy/03h8v9M4fL/S+Hy/0rg8v9K4PL/SODy/0jg8v9G4PL/ReDy/0Pf + 8v9C3/L/QN/y/z7e8f883vH/Ot7x/zrd8f9M4fP/X+T0/23m9f9u5vT/bOb0/2rm9P9o5vT/Z+X0/2Xl + 9P9i5PT/YeT0/1/k9P9T4vP/O97x/yDZ7/8Q1+7/Ddbu/wnW7v8H1e3/BNXt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/y7g9v9P6fz/T+n8/0/p/P8Mzeb/AIOh/wCDof8Ag6H/AIOh/wCHpf8AyeL/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A0+z/ANLq/wvU6/9P6Pv/T+n8/0/p + /P8x4fb/ANTt/wDU7f8A1O3/AbDL/wCEo/8Ag6H/AIOh/wCDof8dqML/TOX4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/R+T3/0Hf8v9D3/L/RN/y/0bg8v9I4PL/SeDy/0rg + 8v9M4fL/TeHy/07h8/9P4fP/UOHz/1Hi8/9R4vP/UuLz/1Li8/9S4vP/UuLz/1Li8/9S4vP/UeLz/1Dh + 8/9P4fP/T+Hz/07h8/9N4fL/TOHy/0rg8v9J4PL/SODy/0Xg8v9E3/L/Qt/y/0Lf8v9b4/P/cuf1/3To + 9f9z5/X/cef1/3Dn9f9u5vT/bOb0/2rm9P9p5vT/Z+X0/2Xl9P9j5fT/YeT0/1/k9P9d5PT/W+Tz/0Xg + 8v8f2u//DNbu/wnW7v8G1e3/A9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MOH2/0/p/P9P6fz/T+n8/wvG + 3/8Ag6H/AIOh/wCDof8Ag6H/ALfS/wDT7P8A0ur/AM/n/wDN5v8AzOT/AMvj/wDK4v8AyuL/AMnh/wDJ + 4f8AyeH/AMnh/wDK4v8Ay+P/E9Tp/0/o+/9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU7f8C1O3/EMPc/wOM + qf8Ag6H/AIOh/wCDof8OlLD/Q9nu/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9G4vX/ReDy/0bg8v9I4PL/SuDy/0zh8v9N4fL/T+Hz/1Dh8/9S4vP/U+Lz/1Ti8/9U4vP/VeLz/1bj + 8/9W4/P/VuPz/1bj8/9W4/P/VuPz/1bj8/9W4/P/VeLz/1Ti8/9T4vP/UuLz/1Hi8/9Q4fP/T+Hz/03h + 8v9L4fL/SuDy/0jg8v9X4/P/def1/3rp9v946fX/d+j1/3Xo9f9z6PX/aeL0/1/X9P9Y0PT/Vsv0/1TI + 9P9SzfT/VNLz/1jb8/9f5PP/YOT0/17k9P9d5PT/W+Tz/1nj8/873vH/Etfu/wjV7v8F1e3/AtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/BrXQ/wCDof8Ag6H/AIOh/wCpxf8A1O3/ANPs/wDR + 6f8Azub/AM3l/wDL4/8AyuL/AMri/wDK4v8AyuL/AMzk/wDN5f8Azub/ANHp/wDT7P8c2/L/T+n8/0/p + /P9P6fz/Id3z/wDU7f8A1O3/ANTt/wLU7f8Z2O//GtHo/wiZtf8Ag6H/AIOh/wCDof8Eiab/Ncfe/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0fg8/9I4PL/SuDy/03h8v9O4fP/UOHz/1Li + 8/9T4vP/VOLz/1bj8/9X4/P/WOPz/1nj8/9Z4/P/WuPz/1vj8/9b4/P/W+Pz/1vj8/9b4/P/W+Pz/1rj + 8/9Z4/P/WePz/1fj8/9W4/P/VeLz/1Ti8/9S4vP/UeLz/0/h8/9Q4fL/bOf0/4Dp9v9/6fb/fOn2/3vp + 9v9x5PX/Xsv0/1Gz8v9SovP/UqLz/1Kh8/9SofP/UqHz/1Kh8/9SofP/UqHz/06n8v9MvfP/UdTz/1zj + 9P9c5PT/WuPz/1jj8/9O4fP/Htnv/wfV7f8E1e3/AdTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k+f9P6fz/T+n8/0zo + +/8Anbj/AIOh/wCDof8Ag6HpANTt/wDU7f8A0+z/ANPs/wDT7P8A1O3/ANTt/wDU7f8AutT/AIOh/wCq + xf8AtM//AL/a/wDK5P8A0+z/ANTt/xzb8v9P6fz/T+n8/0/p/P8d3PL/ANTt/wDU7f8A1O3/A9Xt/xvZ + 7/8f2e//Idnu/xGsx/8Ag6H/AIOh/wCDof8Ag6H/I7DK/03n+v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9N5/v/SuDy/03h8v9P4fP/UOHz/1Li8/9U4vP/VuPz/1fj8/9Z4/P/WuPz/1vj8/9c5PP/XuT0/17k + 9P9f5PT/X+T0/2Dk9P9g5PT/YOT0/1/k9P9f5PT/XuT0/17k9P9d5PT/XOTz/1vj8/9Z4/P/WOPz/1bj + 8/9V4vP/WOPz/3np9v+B6vb/gOr2/3/q9v925fX/Wsby/06j8f9Pn/H/T5/w/0+f8P9Pn/D/T5/w/0+f + 8P9PnvD/T57w/0+e8P9PnvD/Tp7w/06e8P9OnvD/Rq/w/0rU8v9Y4/T/V+Lz/1Ti8/9R4vP/Jtvw/wbV + 7f8D1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Suf7/0/p/P9P6fz/P+T5/wCHpP8Ag6H/AIOh/wCCoa4A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ALPN/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCFo/8Am7f/G9Tr/0/p + /P9P6fz/T+n8/yHd8/8A1O3/ANTt/wDU7f8C1O3/Htnv/yHa7/8k2vD/J9vw/x7C2P8Diab/AIOh/wCD + of8Ag6H/Epu2/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/07l9/9O4fP/UOHz/1Li8/9U4vP/VuPz/1nj + 8/9a4/P/W+Pz/17k9P9e5PT/YOT0/2Hk9P9i5fT/YuX0/2Pl9P9k5fT/ZOX0/2Tl9P9k5fT/ZOX0/2Pl + 9P9i5fT/YuX0/2Hk9P9g5PT/X+T0/17k9P9c5PP/W+Pz/13k8/986fb/gur2/4Hq9v9/6vb/atj0/02l + 7v9MnO7/TJzu/0yc7v9MnO7/Tp3t/2il4/97rd3/hrDZ/4uy1v9/rdr/c6ne/1ig6f9Lm+3/S5vt/0ub + 7f9Lm+3/S5vt/0O57v9P4PL/UeLz/0/i8/9N4fP/Jdrw/wXV7f8C1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wzY8P9P6fz/T+n8/0/p + /P8y2O7/AIOh/wCDof8Ag6H/AIKhZADS6v8A0ur/ANHp/wDR6f8A0en/ANHp/wC40v8Ag6L/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Xssz/T+n8/0/p/P9P6fz/It70/wDU7f8A1O3/ANTt/wDU + 7f8h2u//JNrw/yfb8P8q2/D/Ldzw/yrR5v8KlbH/AIOh/wCDof8Ag6H/B4up/zvP5f9P6fz/T+n8/0/p + /P9P6fz/T+T2/1Li8/9U4vP/VuPz/1nj8/9b4/P/XOTz/17k9P9g5PT/YuX0/2Pl9P9k5fT/ZeX0/2bl + 9P9n5fT/Z+X0/2jm9P9o5vT/aeb0/2nm9P9o5vT/Z+X0/2fl9P9m5fT/ZeX0/2Xl9P9j5fT/YuX0/2Dk + 9P9f5PT/fen2/4Pq9v+B6vb/f+r2/2PK8v9Kmuz/SZns/0mZ7P9Jmez/YaLk/5W10f+2wsf/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vMTE/6i8zP9+rNn/TJrq/0iY6/9HmOr/R5jq/0Op7P9J2vH/SuHy/0ng + 8v9G4PL/Gtju/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/H9zz/0/p/P9P6fz/T+n8/yK+1v8Ag6H/AIOh/wCDofwAhaMZAMzk/wDM + 5P8AzeX/AM3m/wDQ6P8Avtj/AIWj/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xWl + wP9P6fz/T+n8/0/p/P8l3vT/ANTt/wDU7f8A1O3/ANTt/yDZ7/8m2vD/Ktvw/y3c8P8w3PD/M93x/zTa + 7v8XqMH/AIOh/wCDof8Ag6H/AYSi/ym4z/9P6fz/T+n8/0/p/P9T4vT/VeLz/1jj8/9a4/P/XOTz/17k + 9P9g5PT/YuX0/2Tl9P9m5fT/Z+X0/2nm9P9q5vT/a+b1/2zm9f9s5vX/beb1/23m9f9t5vX/beb1/2zm + 9f9s5vX/bOb1/2vm9f9q5vT/aOb0/2fl9P9l5fT/ZOX0/3bo9f+E6vb/gur2/4Dq9v9iwvD/Rpbp/0aW + 6f9Glun/UJnm/5a00P+8xMT/vcTE/77Fxf+/xcX/v8bG/8DHx//Bx8f/wcjI/8LIyP/Dycn/w8rK/8TK + yv+4xs3/cafd/0SV6P9Elej/RJXo/0Sf6v9F2PH/ReDy/0Lf8v893/L/C9bt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8z4vf/T+n8/0/p + /P9P6fz/Dpez/wCDof8Ag6H/AIShuAAAAAAA1O3/ANTt/wDU7f8A1O3/AL7Y/wCIpf8Ag6H/AIOh/wCD + of8Ag6KkAImdDQCFoS4AhKFRAIOh/wCDof8Ag6H/Ep65/0/p/P9P6fz/T+n8/yjf9P8A1O3/ANTt/wDU + 7f8A1O3/Htnv/ynb8P8s2/D/MNzw/zLc8f823fH/Od7x/zze8f8ovdP/A4ek/wCDof8Ag6H/AIOh/xeh + vP9J4vX/UOj7/1bj8/9Z4/P/W+Pz/17k9P9g5PT/YuX0/2Xl9P9m5fT/aOb0/2rm9P9s5vX/beb1/27n + 9f9v5/X/cOf1/3Hn9f9x5/X/cef1/3Hn9f9x5/X/cef1/3Hn9f9w5/X/b+f1/27n9f9s5vX/a+b1/2rm + 9P9v5/X/her2/4Pq9v+B6vb/acvt/0OU5/9Dk+b/Q5Pm/1uc4P+xwcr/wcjI/8LIyP/Cycn/w8rK/8PK + yv/CyMj/wsjI/8HIyP/Dycn/xcvL/8jOzv/Jzs7/yc/P/8rQ0P/K0ND/krbW/0KS5P9BkuX/QZLl/0Gh + 6P9A3PH/Pt7x/zve8f8n2/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AtXt/0rn+/9P6fz/T+n8/0nl+P8BhKL/AIOh/wCDof8AhKBbAAAAAADU + 7f8A1O3/ANTt/wC0z/8AhKL/AIOh/wCDof8Ag6H/AIOhvQCAqgYAAAAAAAAAAAAAAAAAgqGNAIOh/wCD + of8Kj6z/T+n8/0/p/P8+1On/Dp25/wCTsP8AxN//ANTt/wDU7f8a2O//K9vw/y7c8P8y3PH/Nd3x/zjd + 8f883vH/Pt7x/0Lf8v85z+P/CpGt/wCDof8Ag6H/AIOh/wqQrf9B0ub/WePz/1zk8/9f5PT/YeT0/2Tl + 9P9m5fT/aOb0/2rm9P9s5vX/buf1/3Dn9f9x5/X/c+f1/3Pn9f916PX/duj1/3bo9f926PX/duj1/3bo + 9f926PX/dej1/3To9f9z5/X/cuf1/3Hn9f9v5/X/buf1/4Dp9v+F6/b/gur2/3XZ8/8xbar/QJHk/0CR + 5P9Vm+H/u8fO/8bMzP/Hzc3/x83N/8XLy//Ax8f/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/v8XF/8XL + y//O09P/z9TU/9DV1f/Q1dX/nb3Z/z6P4v8+j+L/Po/i/zyw6P863vH/ON3x/zLd8P8M1u7/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8U2fH/T+n8/0/p + /P9P6fz/M8je/wCDof8Ag6H/AIOh6wCOqgkAAAAAANTt/wDT7P8AqMT/AIOh/wCDof8Ag6H/AIOh/wCE + obwAgJkKAAAAAAAAAAAAAAAAAAAAAACDoX0Ag6H/AIOh/wGEov9K5Pj/I6/J/wGEov8Ag6H/AIOh/wCi + vv8A1O3/ANTt/xXY7v8t3PD/Mdzw/zTd8f833fH/O97x/z7e8f9B3/L/ReDy/0jg8v9G2+7/GaG8/wCD + of8Ag6H/AIOh/wKFpP9N1Ob/YOT0/2Ll9P9l5fT/Z+X0/2rm9P9s5vX/b+f1/3Dn9f9y5/X/dOj1/3bo + 9f936PX/eOj1/3no9v966fb/e+n2/3vp9v976fb/e+n2/3rp9v966fb/eOj1/3jo9f926PX/dej1/3Pn + 9f936PX/huv3/4Tq9/984vj/SKnr/yVViP86iNj/SZTf/7nI0f/K0ND/y9HR/8vR0f/Dysr/vcTE/73E + xP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/7/Gxv/P1NT/1dra/9ba2v/W29v/irTd/zqM + 4P86jOD/Oo3g/zXQ7v8z3PH/K9zw/x7Z7/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/y3g9f9P6fz/T+n8/0/p/P8apb//AIOh/wCDof8AgqKDAAAAAAAA + AAAAx+H/AJe0/wCDof8Ag6H/AIOh/wCDof8AhKGdAJmZBQAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbQCD + of8Ag6H/AIOh/wmQrP8Ag6H/AIOh/wCDof8Ag6H/ALvW/wDU7f8A1O3/Dtbu/zDc8P8z3fH/N93x/zne + 8f893vH/Qd/y/0Pf8v9H4PL/SuDy/03h8v9R4vP/L7jO/wGFov8Ag6H/AIOh/yyvx/9i5fT/ZeX0/2jm + 9P9r5vX/beb1/3Dn9f9y5/X/dOj1/3bo9f946PX/eun2/3vp9v996fb/fen2/37p9v9/6fb/f+n2/3/p + 9v9/6fb/f+n2/37p9v996fb/fOn2/3vp9v946PX/d+j1/4Hq9v+G6vb/hOr2/2HF+P89rfn/JFiM/y9y + uP+du9b/z9TU/9DV1f/Q1dX/w8rK/73ExP+9xMT/vcTE/73ExP+9xMT/vcTE/73ExP+5ta7/tKGO/7KZ + g/+0o5H/urm0/77Fxf/U2dn/29/f/9zg4P/a3+D/Vpnc/zeJ3f83id3/NaLi/y/c8P8n2+//I9rv/wXV + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/SOf6/0/p + /P9P6fz/Tef6/wWJp/8Ag6H/AIOh+QCFoxkAAAAAAAAAAACEov8Ag6H/AIOh/wCDof8Ag6H+AIOgeQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/DZOw/zvP + 5v8W2vH/ANTt/wDU7f8E1e3/Mtzx/zXd8f853vH/PN7x/z/f8f9D3/L/RuDy/0ng8v9N4fL/UOHz/1Pi + 8/9W4/P/QMjb/wCDof8MkKz/TtHj/2bl9P9p5vT/bOb1/2/n9f9x5/X/c+f1/3bo9f946PX/eun2/3zp + 9v9+6fb/f+n2/4Hq9v+C6vb/g+r2/4Tq9v+E6vb/hOr2/4Tq9v+D6vb/gur2/4Hq9v+A6vb/fun2/33p + 9v986fb/h+v2/4br9v+D6vb/R7L5/z6v/P8rdbH/PGiT/9PY2f/U2dn/1dra/8nPz/+9xMT/vcTE/73E + xP+9xMT/vcTE/73ExP+6ubP/q4Bd/6RlNP+kZTT/pGU0/6RlNP+kZjb/ropr/77Bvv/d4OD/4eTk/+Ll + 5f+8z+H/NIbb/zSG2/8zhtr/KdPu/yHa7/8d2e//Dtbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXa8f9P6fz/T+n8/0/p/P83yuD/AIOh/wCDof8Ag6KhAAAAAAAA + AAAAAAAAAIOh/wCDof8Ag6H/AIOg6ACDokoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACF + oHMAg6H/AIOh/wCDof8Ag6H/AoWj/ye1zv9N5/n/T+n8/0Tm+f8C1O3/ANTt/wDU7f8t3PD/N93x/zre + 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9S4vP/VuPz/1nj8/9c5PP/YOT0/2Ll9P9l5fT/aeb0/2zm + 9f9v5/X/cef1/3To9f936PX/eej2/3zp9v9+6fb/gOr2/4Lq9v+E6vb/her2/4fr9/+H6/f/iOv3/4jr + 9/+I6/f/iOv3/4fr9/+H6/f/her2/4Tq9v+C6vb/gOr2/4Lq9v+I6/f/huv3/3jd9/8/r/z/Pq/8/zea + 4v9jc4H/w8bG/9re3v/W2tr/vsTE/73ExP+9xMT/vsXF/8DGxv/ByMj/u7Sp/6dsP/+kZTT/pGU0/6Rl + NP+kZTT/pGU0/6RlNP+kZTT/p3JH/8bBuv/m6en/5+np/+fq6v9Sldn/MIPY/zCD2P8ptuX/Gtnv/xfY + 7/8S1+7/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p + /P9P6fz/T+n8/xulwP8Ag6H/AIOh/wCCnzUAAAAAAAAAAAAAAAAAg6H/AIOh/wCDoasAgKYUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEnh0AgqK0AIOh/wCDof8Ag6H/AIOh/xGZtf9B1+z/T+n8/0/p + /P9P6fz/T+n8/ybe9f8A1O3/ANTt/yDZ7/853vH/PN7x/0Df8v9D3/L/R+Dy/0rg8v9O4fP/UeLz/1Ti + 8/9Y4/P/W+Pz/17k9P9i5fT/ZeX0/2jm9P9s5vX/b+f1/3Hn9f916PX/eOj1/3vp9v996fb/f+n2/4Lq + 9v+E6vb/huv2/4jr9/+J6/f/i+v3/4vr9/+M7Pf/jez3/43s9/+M7Pf/i+v3/4vr9/+J6/f/iOv3/4br + 9v+E6vb/h+v2/4jr9/+G6/f/XcT2/z6v/P8+r/z/Pq/8/3OLnf+go6P/3+Li/8rQ0P+9xMT/vsXF/8HH + x//Dycn/xcvL/8XIxf+obkL/pGU0/6RlNP+jZTT/fU0o/2Y/IP+FUir/pGU0/6RlNP+kZTT/qHhQ/+Tn + 5v/s7u7/7e/v/52+3v8tgNX/LYDV/yqX3P8T1+7/Etfu/xLX7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wLU7v9L6Pv/T+n8/0/p/P9L5fj/A4el/wCDof8Ag6H/AAAAAAAA + AAAAAAAAAAAAAACDotIAg59IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgAIAg6JrAIOh8gCD + of8Ag6H/AIOh/wSIpf8tvdT/Tuj7/0/p/P9P6fz/T+n8/0/p/P9P6fz/TOj7/wrW7/8A1O3/Edfu/zre + 8f8+3vH/Qd/y/0Xg8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Tl9P9n5fT/a+b1/27n + 9f9x5/X/dOj1/3jo9f976fb/fen2/4Dq9v+D6vb/her2/4jr9/+K6/f/jOz3/47s9/+P7Pf/kOz3/5Hs + 9/+R7Pf/kez3/5Hs9/+Q7Pf/j+z3/47s9/+L6/f/iev3/4fr9/+J6/b/iOv2/4Xq9v9MtPT/Pq/8/z6v + /P8+r/z/WaHS/4mKiv/Y29v/wMbG/7/Gxv/Dycn/xszM/8nOzv/L0ND/vaaR/6RlNP+kZTT/nmEy/ykZ + Df8AAAD/AAAA/wAAAP8+JhT/o2U0/6RlNP+kZTT/zL+y//Lz8//y9PT/0t/q/yp90/8qfdP/KoDT/wzW + 7v8M1u7/DNbu/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Gtvy/0/p + /P9P6fz/T+n8/zLF3P8Ag6H/AIOh/wCDoaMAAAAAAAAAAAAAAAAAAAAAAP//AQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAg6IpAIOhxQCDof8Ag6H/AIOh/wCDof8WoLv/Rdzw/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/NuP3/wDU7f8D1e3/Ot7x/z/f8f9D3/L/R+Dy/0rg8v9O4fP/UuLz/1Xi + 8/9Z4/P/XOTz/2Dk9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3Pn9f936PX/e+n2/33p9v+A6vb/g+r2/4fr + 9/+J6/f/i+v3/47s9/+Q7Pf/ku33/5Pt9/+V7fj/le34/5bt+P+W7fj/le34/5Xt+P+T7ff/kez3/5Ds + 9/+N7Pf/i+v3/4vr9/+I6/b/hev2/0Wv9v8+r/z/Pq/8/z6v/P9Brvn/g5CZ/6qtrf/Axsb/xMrK/8fN + zf/L0ND/ztPT/9DV1f+xhGD/pGU0/6RlNP9SMxr/AAAA/wAAAP8AAAD/AAAA/wAAAP9qQSL/pGU0/6Rl + NP+5lnn/9/j4//j5+f/r8PT/J3rQ/yd60P8netD/Cs/s/wfV7f8H1e3/AtTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f814vf/T+n8/0/p/P9P6fz/FqC7/wCDof8Ag6H/AIOgcQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgKoGAISifgCDofkAg6H/AIOh/wCD + of8Gi6j/MsTb/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/F9vx/wDU + 7f8r2/D/Qd/y/0Tf8v9I4PL/TOHy/0/h8/9T4vP/VuPz/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n + 9f9z5/X/duj1/3no9v996fb/gOr2/4Pq9v+H6/f/iev3/4zs9/+P7Pf/ku33/5Tt9/+W7fj/mO74/5nu + +P+a7vj/mu74/5ru+P+a7vj/mO74/5ft+P+V7fj/k+33/5Ds9/+O7Pf/i+v3/4jr9/+F6vb/P6z3/z6v + /P8+r/z/P6/8/0Wy/P9lptL/aGlp/4KFhf/Izs7/zNHR/8/U1P/T19f/1tra/6pzSP+kZTT/pGU0/y0c + Dv8AAAD/AgIC/woKCv8RERH/FRUV/1E4Jf+kZjX/pGU0/7CCXv/8/Pz//f39//z9/f8jd83/I3fN/yN3 + zf8Hxej/AdTt/wHU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Bdbu/03p + /P9P6fz/T+n8/0jl+P8BhaL/AIOh/wCDof8AhKA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAISfOACDodMAg6H/AIOh/wCDof8Ag6H/HKfB/0jh9P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9E5vr/AtTu/xTX7v9C3/L/ReDy/0ng8v9N4fL/UeLz/1Ti + 8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9t5vX/cef1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr + 9/+M7Pf/j+z3/5Lt9/+V7fj/mO74/5ru+P+c7vj/ne74/5/v+P+f7/j/n+/4/57v+P+c7vj/m+74/5nu + +P+W7fj/k+33/5Ds9/+O7Pf/iuv3/4fr9/9Arff/Pq/8/z6v/P9Fsfz/SrT8/1C2+/8oLjL/HB0d/8rQ + 0P/Q1dX/1NjY/9fb2//b3t7/rnxU/6RlNP+kZTT/OyQT/xsbG/9GRkb/TExM/1NTU/9ZWVn/lIBx/7mJ + ZP+kZTT/toxr////////////+fv8/yB0y/8gdMv/IHTL/wTJ6f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8g3PP/T+n8/0/p/P9P6fz/Lsvi/wCDof8Ag6H/AIOh/ACJ + nQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqDACCopEAg6H9AIOh/wCDof8Ag6H/CY+s/zjM + 4v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8n3vX/AdTt/z7e8f9H4PL/SuDy/07h8/9S4vP/VeLz/1nj8/9d5PT/YOT0/2Tl9P9n5fT/a+b1/2/n + 9f9z5/X/duj1/3rp9v996fb/ger2/4Tq9v+I6/f/i+v3/47s9/+S7ff/le34/5ju+P+b7vj/nu/4/6Dv + +P+h7/j/o+/5/6Tw+f+k8Pn/ou/4/6Hv+P+f7/j/nO74/5nu+P+W7fj/k+33/5Ds9/+M7Pf/iev3/0Ov + 9v8+r/z/Q7H8/0mz/P9Ptvz/QpDE/wgOEf8HBwf/e39//9PY2P/Y3Nz/3N/f/9/j4//BoIX/pGU0/6Rl + NP+KVSz/aGZl/4iIiP+Ojo7/lZWV/6Kfnf/Yw7P/28Ov/6RlNP/Ks6D////////////s8fX/HXHI/x1x + yP8dcsj/AdLs/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/z3k + +f9P6fz/T+n8/0/p/P8Qn7r/AIOh/wCDof8Ag6HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + olUAg6H/AIOh/wCDof8Ag6H/AIOh/w2TsP82yeD/Os7k/zrO5P86zuT/Oczi/zXH3v81x97/Ncfe/zXH + 3v81x97/NMbd/y/A1/8vwNf/L8DX/y/A1/8vwNf/L7/X/ym50f8FsMr/Gb3W/0jg8v9L4fL/T+Hz/1Pi + 8/9W4/P/WuPz/17k9P9i5fT/ZeX0/2nm9P9s5vX/cOf1/3To9f946PX/e+n2/3/p9v+C6vb/huv2/4nr + 9/+N7Pf/kOz3/5Tt9/+Y7vj/m+74/57v+P+h7/j/pPD5/6bw+f+n8Pn/qPD5/6jw+f+m8Pn/pPD5/6Hv + +P+f7/j/m+74/5ju+P+V7fj/kez3/47s9/+K6/f/SrT0/0Cv/P9Hs/z/TrX8/1O09v8RIy//OXCV/wIC + A/8WFxf/0dbW/9vf3//g4+P/5Ofn/9vTyv+lZjb/pGU0/6RlNP/AppL/19LO/9jV0v/h3dr/8Onj//Xu + 6f/m1Mb/pms9/+ro5P///////////8jY5/8absb/Gm7G/xd7yv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8L1/D/T+n8/0/p/P9P6fz/QuP3/wCDof8Ag6H/AIOh/wCD + onMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6JlAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/KbnQ/0zh8v9P4fP/VOLz/1fj8/9b4/P/XuT0/2Ll9P9m5fT/aub0/23m + 9f9x5/X/dej1/3jo9f986fb/gOr2/4Tq9v+H6/f/i+v3/47s9/+S7ff/lu34/5ru+P+d7vj/oe/4/6Tw + +f+n8Pn/qvH5/6zx+f+t8fn/rPH5/6rx+f+n8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+T7ff/j+z3/4vr + 9/9gyfj/RLH8/0u0/P9St/z/KFVz/zlxlv9aqd7/ChAU/xYXF/9eYGD/3uHh/+Ll5f/n6ur/7O7u/8io + jv+kZTT/pGU0/610SP/w5d3/////////////////9vDr/7SBWf/Drpv//v7+////////////daTT/xZr + w/8Wa8P/DpfU/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/ynf + 9P9P6fz/T+n8/0/p/P8lyOD/AIOh/wCDof8Ag6H/AIafKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + oasAg6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8YsMn/TeHy/1Dh + 8/9U4vP/WOPz/1vj8/9f5PT/Y+X0/2fl9P9q5vT/buf1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr + 9/+L6/f/kOz3/5Pt9/+X7fj/mu74/5/v+P+i7/j/pvD5/6nw+f+t8fn/sPL5/7Hy+f+w8vn/rPH5/6nw + +f+l8Pn/oe/4/57v+P+a7vj/l+34/5Pt9/+P7Pf/i+v3/3ne+P9Hsvz/T7b8/0yj3f9UquP/ZL/8/z5x + kv8nQ1b/KjpF/xITE/+Tlpb/5Ofn/+rs7P/v8PD/8/X1/9O3of+maTr/pGU0/6VnN/+/knD/zKiM/8GX + df+rckf/x7Wl//Hy8v////////////f5+v8bbMH/E2jA/xNowP8Ft+H/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1O3/Ref6/0/p/P9P6fz/Tun8/wmcuf8Ag6H/AIOh/wCD + odwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISjOgCDof8Ag6H/AIOh/wCDoe4Ag6HhAISi3QCE + ot0AhKLdAISi3QCEot0Ag6HRAIOhzACDocwAg6HMAIOh7wCDof8Ag6H/AISi/w+cuP8Vnrn/FZ65/xWe + uf8Gnbn/AJ66/wCeuv8Anrr/AKnF/wzS6/9M4fL/UeLz/1Ti8/9Y4/P/W+Pz/2Dk9P9j5fT/Z+X0/2vm + 9f9v5/X/cuf1/3bo9f966fb/fen2/4Hq9v+E6vb/iev3/4zs9/+Q7Pf/lO33/5ju+P+b7vj/n+/4/6Pv + +f+m8Pn/qvH5/67x+f+y8vr/tPL6/7Dy+f+t8fn/qfD5/6bw+f+h7/j/nu/4/5ru+P+X7fj/k+33/4/s + 9/+L6/f/h+v3/0u0+P9Rt/z/Wbr8/2G9/P9nv/r/Ficz/12cx/8vTF//DQ8R/wsMDP++v7//6+3t//Dx + 8f/19vb/+/v7//Hp4//KqI3/t4di/7F9VP+2iGT/xKaO/9nW0v/o6ur/////////////////l7rb/xBl + vv8QZb7/EG/C/wDT7P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xTZ + 8f9P6fz/T+n8/0/p/P863/T/AIOh/wCDof8Ag6H/AIKhhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhKJNAIOh/wCDof8Ag6H/GMXe/0/p/P9P6fz/T+n8/zfj9/8A1O3/ANTt/wDU7f8A1O3/ANTt/zHc + 8P9R4vP/VOLz/1jj8/9c5PP/YOT0/2Pl9P9n5fT/a+b1/2/n9f9y5/X/duj1/3rp9v996fb/ger2/4Tq + 9v+J6/f/jOz3/5Ds9/+T7ff/mO74/5vu+P+f7/j/ou/4/6bw+f+p8Pn/rPH5/6/y+f+w8vn/rvH5/6vx + +f+o8Pn/pPD5/6Hv+P+d7vj/mu74/5bt+P+S7ff/juz3/4vr9/+H6/f/Zs34/1S4/P9cu/z/ZL/8/0By + lP81W3X/esj9/ypCUv8THCL/AAAA/xscHP/W2Nj/7/Dw//T19f/4+Pj/+fn5//b39//x8/P/7O7u/+fq + 6v/i5eX/6evr/////////////////+zx9f8SZrz/DWO7/wxiu/8Motj/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MeH2/0/p/P9P6fz/T+n8/x291v8Ag6H/AIOh/wCD + of0AgJ8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAvwQAg6HnAIOh/wCDof8BlrP/Q+X5/0/p + /P9P6fz/Tun8/wzY8P8A1O3/ANTt/wDU7f8A1O3/Ctbu/0/h8/9U4vP/WOPz/1vj8/9g5PT/Y+X0/2fl + 9P9q5vT/b+f1/3Ln9f926PX/eej2/33p9v+B6vb/hOr2/4jr9/+L6/f/j+z3/5Pt9/+W7fj/mu74/53u + +P+h7/j/pPD5/6bw+f+p8Pn/q/H5/6vx+f+q8fn/qPD5/6Xw+f+i7/j/n+/4/5zu+P+Y7vj/le34/5Hs + 9/+O7Pf/iev3/4br9v9/5vf/Vrj5/168/P9XotT/PWqJ/3fG/f9+yv3/EBge/xsoMP8FBgj/BAQE/yco + KP/b3d3/8PHx//Lz8//z9PT/8fPz/+7w8P/q7Oz/5+rq//L09P/////////////////2+Pr/MXnC/wlf + uP8JX7j/DHDA/wbS6/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV + 7v9P6fz/T+n8/0/p/P9L6Pv/A4uo/wCDof8Ag6H/AIKhtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAGZmCgByjeEAg6H/AIOh/wCDof8f0Oj/T+n8/0/p/P9P6fz/NOL3/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/Kdvw/1Ti8/9Y4/P/W+Pz/1/k9P9i5fT/ZuX0/2rm9P9u5/X/cef1/3Xo9f946PX/fOn2/4Dq + 9v+E6vb/h+v3/4vr9/+O7Pf/kez3/5Xt+P+Y7vj/m+74/5/v+P+h7/j/pPD5/6Xw+f+m8Pn/pvD5/6bw + +f+k8Pn/ou/4/5/v+P+c7vj/me74/5bt+P+T7ff/j+z3/4vr9/+I6/f/hOr2/4Hq9v9t1fj/X738/2Gy + 6v9wxP3/ecf9/2ijy/8YJCz/IC44/xgiKf8oNj//AAAA/yoqKv/P0dH/7vDw/+7w8P/v8fH/8PLy//b2 + 9v/+/v7/////////////////9ff4/0uJxv8GXbb/Bl22/wZet/8VueH/AdTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Kt/0/0/p/P9P6fz/T+n8/zHT6f8Ag6H/AIOh/wCD + of8AhKFRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVgAYAWm+1AF91/wCDof8Ag6H/AIOh/wOa + t/9J6Pv/T+n8/0/p/P9P6fz/Etnw/wDU7f8A1O3/ANTt/wDU7f8D1e3/SODy/1fj8/9b4/P/XuT0/2Ll + 9P9l5fT/aub0/23m9f9x5/X/dOj1/3jo9f976fb/f+n2/4Lq9v+G6/b/iev3/4zs9/+Q7Pf/k+33/5bt + +P+Z7vj/m+74/57v+P+f7/j/oe/4/6Lv+P+i7/j/oe/4/6Dv+P+e7/j/nO74/5nu+P+W7fj/k+33/5Ds + 9/+N7Pf/iev3/4fr9/+D6vb/f+n2/3zp9v9mx/n/aMH8/3HE/f96yP3/Mk9i/2+myv8SGiD/L0FM/1V0 + iP8AAAD/AgMD/w8PD/+BgYH/+fn5/////////////////////////////////+Ho7/8ncLz/A1q0/wNa + s/8DWrP/EqXX/xDX7v8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wfW + 7/9M6Pv/T+n8/0/p/P9O6Pz/DZm1/wCDof8Ag6H/AIOh5ACAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVVUDAFtwqQBab/8AWm//AHWQ/wCDof8Ag6H/AIOh/yXT6v9P6fz/T+n8/0/p/P8+5fn/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Qv9j/O8Xa/1rj8/9e5PT/YeT0/2Xl9P9o5vT/bOb1/2/n9f9z5/X/duj1/3rp + 9v996fb/gOr2/4Tq9v+H6/f/iuv3/47s9/+Q7Pf/k+33/5Xt+P+Y7vj/mu74/5zu+P+c7vj/ne74/53u + +P+c7vj/nO74/5ru+P+Y7vj/lu34/5Pt9/+Q7Pf/juz3/4vr9/+H6/f/hOr2/4Hq9v996fb/eun2/3Xl + 9v9nxfn/ccT9/2CeyP9ek7j/hMby/wEBAf9kiqT/f6/O/wAAAP8lNkL/AAAA/wQEBP8LCwv/SkpK/5CQ + kP+6urr/3t7e/9LV1v9rjrH/A1iw/wBXsf8AV7H/AFmy/xGd0v8c2e//CNXu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Ld/1/0/p/P9P6fz/T+n8/zLT6f8Ag6H/AIOh/wCD + of8AhKJ2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBZbp0AWm//AFpv/wBab/8XiqH/AYSi/wCD + of8Ag6H/Ape0/0bn+v9P6fz/T+n8/0/p/P8b2/L/ANTt/wDU7f8A1O3/AK/K/wCDof8Ag6H/N7/U/1zk + 8/9g5PT/Y+X0/2fl9P9q5vT/buf1/3Hn9f916PX/eOj1/3vp9v9/6fb/gur2/4Xq9v+I6/f/i+v3/47s + 9/+Q7Pf/k+33/5Tt9/+W7fj/mO74/5ju+P+Z7vj/me74/5ju+P+Y7vj/lu34/5Tt9/+S7ff/kOz3/43s + 9/+L6/f/h+v3/4Tq9v+C6vb/fun2/3vp9v946PX/dOj1/3Dk9v9rw/H/ZafU/4DK/f9spMn/FiEo/5HO + 9/+Rzfb/AAAA/1N9mf8AAAD/BQcI/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ADhz/ADl0/wNg + tv8Ytd7/H9nv/xrY7/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y + 8P9O6Pz/T+n8/0/p/P9O6Pz/C5az/wCDof8Ag6H/AIOg6ACLogsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnCQAFpv/wBab/8AWm//GIWZ/03m+f8cp8H/AIOh/wCDof8Ag6H/G8rj/0/p/P9P6fz/T+n8/0fm + +v8G1e7/ANTt/wDE3v8AhaP/AIOh/wCDof8mtc3/W+Pz/1/k9P9i5fT/ZeX0/2nm9P9s5vX/b+f1/3Pn + 9f926PX/eej2/3zp9v9/6fb/gur2/4Xq9v+I6/f/iuv3/4zs9/+O7Pf/kOz3/5Lt9/+T7ff/lO33/5Tt + 9/+U7ff/lO33/5Pt9/+S7ff/kOz3/47s9/+M7Pf/iev3/4fr9/+E6vb/gur2/3/p9v986fb/eOj1/3bo + 9f9y5/X/b+f1/2zk9f9w0vr/fcn9/0Zth/9wp83/jtD9/47Q/f8DBAX/aJ7D/xIdJP8AAAD/BQkL/wUK + Dv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/Cj1F/x7I2/8d2e//D9fu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8B1e3/QOX5/0/p/P9P6fz/T+n8/y7N5P8Ag6H/AIOh/wCD + of8AhKJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/xR/k/9M5Pb/T+n8/0LY + 7f8BhKL/AIOh/wCDof8Ai6n/PuT4/0/p/P9P6fz/T+n8/zTi9/8A0uv/AJCt/wCDof8Ag6H/CpCt/zze + 9P9U4/T/XeT0/2Dk9P9k5fT/Z+X0/2rm9P9u5/X/cef1/3Pn9f936PX/eun2/33p9v9/6fb/gur2/4Tq + 9v+H6/f/iev3/4vr9/+M7Pf/juz3/4/s9/+Q7Pf/kOz3/5Ds9/+P7Pf/juz3/47s9/+M7Pf/iuv3/4jr + 9/+G6/b/hOr2/4Hq9v9/6fb/fOn2/3no9v926PX/c+f1/3Dn9f9s5vX/aub0/2bl9P9p2/b/YqLI/4PM + /f+Hzf3/fb/r/wAAAP9ys9//LEhb/wAAAP8lQ1b/CREX/y1bev8AAQH/AAAA/wQLEP8AAQH/AAAA/wAA + AP8AAAD/BB4h/xi6zP8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/yXe + 9P9P6fz/T+n8/0/p/P9H5fn/BYqn/wCDof8Ag6H/AIOh5ACOqgkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb + b3MAWm//AFpv/wBab/8Pdor/OcHV/yqluf8XhZj/BWN3/wBzjf8Ag6H/AIOh/wCDof8Qssz/Tuj8/0/p + /P9P6fz/T+n8/wypxP8Ag6H/AIOh/wGEov880Ob/T+n8/0rn+v9X5fX/XuT0/2Ll9P9l5fT/aOb0/2vm + 9f9v5/X/cef1/3To9f936PX/eun2/33p9v9/6fb/ger2/4Pq9v+F6vb/h+v3/4nr9/+J6/f/iuv3/4vr + 9/+L6/f/i+v3/4vr9/+K6/f/iev3/4jr9/+G6/b/hOr2/4Lq9v+A6vb/fen2/3vp9v946PX/duj1/3Pn + 9f9w5/X/beb1/2rm9P9n5fT/Y+X0/2Dk9P9d4/T/aNn3/3jO+/9WiKv/ERwj/3rI/f84XXf/BAgK/1CN + t/8VFRX/OXSb/ytojv8LCwv/ChMV/yCXpf8OTFP/AAEB/wAAAP8AAAD/Ahga/wC90/8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8S2fD/Ten8/0/p/P9P6fz/T+n8/xqwyf8Ag6H/AIOh/wCD + of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29lAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AF1z/wB/nP8Ag6H/AIOh/wCDof8o1ev/T+n8/0/p/P86zeT/AIOh/wCDof8Ag6H/JbPM/0/p + /P9P6fz/T+n8/0/o/P9X5vj/YOT0/2Ll9P9m5fT/aeb0/2zm9f9v5/X/cef1/3To9f926PX/eej2/3vp + 9v996fb/f+n2/4Lq9v+D6vb/hOr2/4Xq9v+G6/b/h+v3/4fr9/+H6/f/h+v3/4br9v+E6vb/hOr2/4Lq + 9v+A6vb/fun2/3zp9v966fb/eOj1/3Xo9f9z5/X/cOf1/23m9f9q5vT/Z+X0/2Tl9P9h5PT/XuT0/1vj + 8/9X4/P/VOLz/xg+RP84hZf/XdT3/ypjdP8TMDj/S8/u/zI3OP8yYWf/Nd3x/y19hv8kJCT/Ij1B/yjQ + 5P8hr8D/Fzo+/xEREf8ODg7/CC0y/wDM5P8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/BtXu/0bn + +v9P6fz/T+n8/0/p/P832O3/AIOh/wCDof8Ag6H/AIShvgD//wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvVQBa + b/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv/wBab/8AWm/6AGqC2ACDof8Ag6H/AIOh/wKR + rv8/5Pn/SeL1/wiOq/8Ag6H/AIOh/w+Wsv9N5/r/T+n8/0/p/P9P6fz/T+n8/0/p/P9U5/n/X+T1/2Pl + 9P9m5fT/aeb0/2zm9f9v5/X/cef1/3Pn9f926PX/eOj1/3rp9v986fb/fen2/3/p9v+A6vb/ger2/4Lq + 9v+C6vb/gur2/4Lq9v+C6vb/ger2/4Dq9v9/6fb/fen2/3zp9v976fb/eOj1/3bo9f906PX/cef1/2/n + 9f9s5vX/aub0/2fl9P9k5fT/YeT0/17k9P9b4/P/WOPz/1Xi8/8+rLn/IV5l/0vh8v9I4PL/GVNZ/yV/ + iv8+3vH/SnJ3/0ldX/803fH/Mdvv/z1hZf89PT3/MXuE/yLa7/8f1On/IXiD/ysrK/8nJyf/FWhy/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wHV7f844/j/T+n8/0/p/P9P6fz/RuX5/waNq/8Ag6H/AIOh/wCD + ofcAhqEmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm/bAFpv/wBab/8AWm//AFpv/wBab/8AWm//AFpv9QBa + b70AWm5/AFlwQgBmZgoAAAAAAIOgrwCDof8Ag6H/AIOh/wyqxf8apcD/AIOh/wCDof8ChqT/Qtjt/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9S6Pr/XeX1/2Pl9P9m5fT/aeb0/2vm9f9u5/X/cOf1/3Ln + 9f906PX/duj1/3jo9f956Pb/e+n2/3zp9v996fb/fen2/33p9v996fb/fen2/33p9v996fb/fOn2/3vp + 9v956Pb/eOj1/3bo9f906PX/cuf1/3Dn9f9u5/X/bOb1/2nm9P9n5fT/ZOX0/2Hk9P9e5PT/W+Pz/1jj + 8/9V4vP/UuLz/zmir/9M4fL/SODy/0Xg8v8QNzz/O9Dh/zze8f9VnKX/ZW9w/zLc8f8u3PD/Nr/P/1pa + Wv9WWFn/KMTX/xvZ7/8D1e3/HJ6t/0NHSP9AQUH/EKy//wDU7f8A1O3/ANTt/wDU7f8A1O3/LuD2/0/p + /P9P6fz/T+n8/07o/P8TpL//AIOh/wCDof8Ag6H/AISgfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b7wAWm//AFpv/wBab/8AWm+1AFlveABYbjoAZmYFAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ0aAIOh7ACD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/y291P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9Q6fz/Wub3/2Pl9P9l5fT/aOb0/2rm9P9s5vX/b+f1/3Hn9f9y5/X/c+f1/3Xo9f926PX/d+j1/3jo + 9f946PX/eej2/3no9v956Pb/eOj1/3jo9f936PX/duj1/3Xo9f9z5/X/cuf1/3Hn9f9v5/X/bOb1/2rm + 9P9o5vT/ZeX0/2Pl9P9g5PT/XuT0/1vj8/9Y4/P/VeLz/1Li8/9P4fP/TeHy/0ng8v9G4PL/O8TV/yN6 + hf883vH/Od7x/1a8yP96kJL/MNzw/yzb8P8p2/D/ZIyR/3Nzc/9Mnab/CNXu/wDU7f8A1O3/Iqq6/11e + Xv9LbXH/AdPs/wDU7f8A1O3/ANTt/yXe9P9P6fz/T+n8/0/p/P9P6fz/JMDZ/wCDof8Ag6H/AIOh/wCD + ocgAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgBbcFQAXHAyAFVVAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaJHAIOh/ACDof8Ag6H/AIOh/wCDof8Vnrn/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Vuf4/2Hl9P9l5fT/Z+X0/2nm + 9P9r5vX/bOb1/27n9f9w5/X/cef1/3Ln9f9z5/X/c+f1/3To9f916PX/dej1/3To9f906PX/c+f1/3Pn + 9f9y5/X/cef1/2/n9f9u5/X/bOb1/2rm9P9o5vT/Z+X0/2Tl9P9i5fT/YOT0/13k9P9b4/P/WOPz/1Xi + 8/9S4vP/T+Hz/03h8v9K4PL/RuDy/0Pf8v8unqz/PNvu/zre8f833fH/SM/f/4GvtP8t3PD/Ktvw/yba + 8P9TvMj/j4+P/3eYnP8A1O3/ANTt/wDU7f8A1O3/Oam2/3Z2dv8nssL/ANTt/wDU7f8h3fT/T+n8/0/p + /P9P6fz/T+n8/y7S6P8Ag6H/AIOh/wCDof8Ag6HsAICiHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAg599AIOh/wCDof8Ag6H/BYqn/0fe8v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/TuP3/zm+1P8mqMH/QcLW/2fl9P9p5vT/aub0/2zm9f9s5vX/buf1/2/n + 9f9v5/X/cOf1/3Dn9f9w5/X/cOf1/3Dn9f9v5/X/b+f1/27n9f9s5vX/a+b1/2rm9P9o5vT/ZuX0/2Xl + 9P9i5fT/YOT0/17k9P9c5PP/WePz/1fj8/9U4vP/UuLz/0/h8/9M4fL/SeDy/0bg8v9D3/L/QNzu/zvT + 5f863vH/N93x/zTd8f821ej/W6Or/yvb8P8n2/D/JNrw/zfL3P+RkZH/jZmb/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/WKy2/3ebn/8B0+z/JN70/0/p/P9P6fz/T+n8/0/p/P832/D/AYSj/wCDof8Ag6H/AIOh/gCF + oUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEonYAg6H/AIOh/wCDof80x93/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/P9Tq/yCsxv8EiKb/AIOh/wCD + of8BhKL/W93u/2Xl9P9m5fT/Z+X0/2jm9P9q5vT/aub0/2vm9f9r5vX/bOb1/2zm9f9s5vX/a+b1/2rm + 9P9q5vT/aeb0/2jm9P9n5fT/ZeX0/2Tl9P9i5fT/YOT0/17k9P9d5PT/W+Pz/1jj8/9W4/P/VOLz/1Hi + 8/9O4fP/TOHy/0ng8v9G4PL/Q9/y/0Hf8v8+3vH/O97x/zfd8f813fH/Mtzx/y7c8P89qrf/KNvw/yXa + 8P8h2u//F9br/21xcv9tenz/ANTt/wDU7f8A1O3/ANTt/wDU7f8G0en/fpuf/0jJ2f9P6fz/T+n8/0/p + /P9P6fz/PN/0/wOKqP8Ag6H/AIOh/wCDof8Ag6GCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAhaEuAIOh+ACDof8Ag6H/HKfC/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9L5Pf/L8DX/xCYs/8Ag6H/AIOh/wCDof8Ag6H/AIOh/weKp/9c4vH/YOT0/2Ll9P9j5fT/ZOX0/2Xl + 9P9i4fD/Wtnq/2fl9P9n5fT/Z+X0/2fl9P9n5fT/ZuX0/2Xl9P9l5fT/ZOX0/2Ll9P9h5PT/YOT0/17k + 9P9c5PP/W+Pz/1nj8/9W4/P/VOLz/1Li8/9Q4fP/TeHy/0vh8v9I4PL/ReDy/0Pf8v9B3/L/Pt7x/zve + 8f833fH/Nd3x/zLc8f8v3PD/K9vw/yvL3v8m2vD/Itrv/xPX7v8B1O3/Rlxe/0JrcP8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wLU7f9Axtb/aJuh/0/p/P9P6fz/T+n8/zrg9P8Di6n/AIOh/wCDof8Ag6H/AIOipAD/ + /wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJKSBwCDodMAg6H/AIOh/wmPrP9K4vb/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8/1On/H6vF/wSIpf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCJ + p/8MqMP/RNPm/1vj8/9c5PP/XuT0/1/k9P9g5PT/W9/x/wyPq/8Ag6H/NLfN/2Ll9P9i5fT/YuX0/2Ll + 9P9i5fT/YeT0/2Dk9P9f5PT/XuT0/13k9P9b4/P/WuPz/1nj8/9W4/P/VOLz/1Pi8/9R4vP/T+Hz/0zh + 8v9K4PL/SODy/0Xg8v9C3/L/QN/y/z3e8f863vH/N93x/zXd8f8y3PH/L9zw/yzb8P8p2/D/Jtrw/yHa + 7/8L1u7/ANTt/wDU7f8jTVL/HHN+/wDU7f8A1O3/ANTt/wDU7f8G1e7/POT5/0/p/P9RipH/T9/w/0/p + /P843vT/Ao6r/wCDof8Ag6H/AIOh/wCDobkAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgqGNAIOh/wCDof8BhKL/O8/l/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0rj9v8vv9f/D5ey/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCXs/8At9H/ANLr/wDU7f8F1e3/Mdzw/1jj8/9Z4/P/W+Pz/1vj + 8/9Dydz/AIOh/wCDof8SlbH/XuT0/17k9P9e5PT/XuT0/13k9P9c5PP/W+Pz/1vj8/9a4/P/WePz/1fj + 8/9W4/P/VOLz/1Li8/9R4vP/T+Hz/03h8v9K4PL/SODy/0bg8v9D3/L/Qd/y/z/f8f883vH/Od7x/zfd + 8f813fH/Mtzx/y/c8P8s2/D/Kdvw/yXa8P8X2O//BdXt/wDU7f8A1O3/ANTt/wg6QP8Emqv/ANTt/wDU + 7f8A1O3/Etnw/0Xn+v9P6fz/T+n8/0rM2/9Fo6//MNnv/wKKqP8Ag6H/AIOh/wCDof8Ag6HIAICZCgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGhQQCDof4Ag6H/AIOh/ySxyv9P6fz/T+n8/0/p + /P9P6fz/T+n8/z7U6f8fqsT/BIil/wCDof8Ag6H/AIOh/wCDof8Ag6H/BYqo/wWpxP8Ax+H/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Dtbu/zve8f9W4/P/V+Pz/z7H2/8Ag6H/AIOh/w6Vsf9U5/f/WOT0/1nj + 8/9Z4/P/WePz/1jj8/9X4/P/VuPz/1Xi8/9U4vP/U+Lz/1Li8/9Q4fP/TuHz/03h8v9K4PL/SeDy/0fg + 8v9F4PL/Qt/y/0Df8v8+3vH/O97x/zne8f823fH/NN3x/zHc8P8u3PD/K9vw/yjb8P8V2O7/A9Xt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/AEZO/wDN5f8A1O3/AtTt/yjf9P8yw9v/IK3G/0LZ7f9P6fz/Tuj8/x2L + m/8AhaP/AIOh/wCDof8Ag6H/AIOiygCGoRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH + pREAg6HiAIOh/wCDof8OlbH/Tef5/0/p/P9P6fz/SuP2/y6+1v8PlrH/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/E5y3/zPF2/9N5/n/TOj7/yTe9P8B1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w7W + 7v823fH/O8fb/wCDof8Ag6H/DpWx/0/p/P9P6Pv/UuX4/1Pj9P9U4vP/VOLz/1Pi8/9S4vP/UeLz/0/h + 8/9P4fP/TeHy/0zh8v9K4PL/SODy/0fg8v9F4PL/Q9/y/0Hf8v8+3vH/PN7x/zre8f833fH/Nd3x/zLc + 8f8w3PD/Ldzw/yvb8P8b2e//BNXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Afoz/ANTt/xPZ + 8P9C5vr/SeL1/wCDof8Ag6H/AIOh/0jn+v8VwNn/AGZ+/wCBnv8Ag6H/AIOh/wCEobwAgKoMAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhpQCDof8Ag6H/AoWk/0HX7P9P6fz/PtPo/x6q + xP8Dh6X/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xe50v9M6Pv/T+n8/0/p/P9P6fz/T+n8/z7l + +f8N1/D/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8Gvtf/AIOh/wCDof8OlbH/T+n8/0/p + /P9P6fz/T+n7/0/l+P9P4fT/TuHz/03h8v9N4fL/S+Hy/0rg8v9J4PL/SODy/0bg8v9E3/L/Q9/y/0Hf + 8v8+3vH/Pd7x/zve8f853vH/Nt3x/zTd8f8y3PH/L9zw/y3c8P8h2u//Cdbu/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/weyx/8y4fb/T+n8/0/p/P9D2u7/AIOh/wCDof8BhaL/B6fC/wCD + of8AgqD/AHWP/wCDof8Ag6GrAICqBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + olgAg6H/AIOh/wCDof8ntc7/Lr3V/w6Vsf8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof4AgqLrAIOh/wCD + of8Ag6H/AIOh/w6nwf9C5fj/T+n8/0/p/P9P6fz/T+n8/07o/P8v4fb/CNfv/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wC91v8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/o+/9M4/f/SeDy/0jg + 8v9H4PL/ReDy/0Xg8v9D3/L/Qt/y/0Df8v8+3vH/PN7x/zve8f853vH/N93x/zXd8f8y3PH/MNzw/y7c + 8P8f2e//Ddbu/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV7v8q3/X/TOj7/0/p + /P9P6fz/T+n8/03n+v8BhaL/AIOh/wCDof8Ag6H/AIOh/wCDof8Af5z/AIKgjwCqqgMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhJ4dAIOh7gCDof8Ag6H/AIOh/wKFpP8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDoekAgqGLAIahJgCqqgMAgqCJAIOh/gCDof8Ag6H/AIOh/wKNqv8q0un/Tuj8/0/p + /P9P6fz/T+n8/0/p/P9M6Pv/K+D1/wbV7v8A1O3/ANTt/wDU7f8A1O3/AL3W/wCDof8Ag6H/DpWx/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/v/SOP2/0Pf8v9B3/L/QN/y/z/f8f8+3vH/PN7x/zre + 8f853vH/N93x/zXd8f8y3PH/K9vw/x7Z7/8R1+7/BdXt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wfW7/8o3/T/Suf7/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/wySr/8Ag6H/AIOh/wCD + of8Ag6H/AIOh8wCCoFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AQCD + oM0Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/gCDob0AhKFXAJKSBwAAAAAAAAAAAAAAAAAA + AAAAd5ZcAH+c/wCDof8Ag6H/AIOh/wCDof8QqsX/QeP4/0/p/P9P6fz/T+n8/0/p/P9P6fz/S+j7/y7g + 9f8O1+//ANTt/wDU7f8Avdb/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/R+f6/yfe8/8S0un/CanE/wqvyf8S1uz/Etfu/w7W7v8M1u7/BtXt/wHU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/w/Y7/8x4fb/Ten8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/GKG8/wCDof8Ag6H/AIOh/wCDodYAhp8oAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqFsAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh6QCC + oIkAgKMkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabn8AXnP/AHeS/wCDof8Ag6H/AIOh/wCD + of8ChqT/J8ff/0vo+/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P895Pn/Hdzz/wK91/8Ag6H/AIOh/w6V + sf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/xKeuP8Ag6H/AIOh/wC5 + 0/8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/B9bv/yPd + 9P8/5Pn/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P8jsMr/AIOh/wCD + of8Ag6HYAICqDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD + ocMAg6H/AIOh/wCDof8Ag6H+AIOhuwCEolUAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgIACAFpw4ABab/8AW3D/GZiw/wOGpP8Ag6H/AIOh/wCDof8Ag6H/BpGu/yzP5f9M6Pv/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/Ncvg/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P8uvtb/AIOh/wCDof8Ag6H/AL7Z/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8O2O//JN70/zvk+P9O6Pz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/Tun8/03o + +/9P6fz/T+n8/0/p/P9P6fz/T+n8/y+/1/8Ag6H/AIOh/wCDob0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOgaQCDofwAg6H/AIOhmACDoCMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABabkoAWm//AFpv/wJdcv9I3fD/RNrv/xeh + vP8Ag6H/AIOh/wCDof8Ag6H/AIOh/wmSr/8vz+X/TOj7/0/p/P9P6fz/T+n8/0/p/P84y+H/AIOh/wCD + of8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/RNrv/wOHpf8Ag6H/AIOh/wCd + uv8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8F1u7/FNrw/yTe9P814vf/R+b6/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/z7T6f8ltM3/Qdrv/0Di9v9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/O8/l/wCD + of8Ag6H/AIOh4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAImdDQCAvwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFpvrQBab/8AWm//Gomd/0/p/P9P6fz/T+n8/znM4v8Nk7D/AIOh/wCDof8Ag6H/AIOh/wCD + of8Fi6n/Ir/Z/0Lj9v9P6fz/T+n8/zjL4f8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/07o+/8SmrX/AIOh/wCDof8EjKn/Jdju/yfe9f8s4PX/MOH2/zbi9/8+5fn/Sej7/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9M5fj/A4el/wCDof8Ag6H/Qdfs/0/p + /P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9G3vH/AIOh/wCDof8Ag6H/AICqDAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVahgAWm/7AFpv/wBab/85wtX/T+n8/0/p + /P9P6fz/T+n8/03n+v8ru9P/Bouo/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/xOivP8wz+X/Mcje/wCD + of8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/KrnR/wCDof8Ag6H/AIOh/zfK + 4P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9D5fn/KdXq/w2uyf8Ag6L/AIOh/wCDof8Jj6v/Rt3y/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P8Dh6X/AIOh/wCDof8AhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFlveABab/8AWm//CmyB/07o+/9P6fz/T+n8/0/p/P9P6fz/T+n8/znA1P8IaX7/AGqE/wB+ + mv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0HX7P8ChaT/AIOh/wCDof8eqsT/T+n8/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/0/p/P9P6fz/SOf6/zbf9P8gy+P/CqnF/wCIpv8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8OlbH/SuL2/0/p/P9P6fz/T+n8/0/p/P9P6fz/T+n8/w6Vsf8Ag6H/AIOh/wCEoVcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAWm/dAFpv/wBab/8ppLj/T+n8/0/p + /P9P6fz/T+n8/0zj9v8gk6j/AFtw/wBab/8AWm//AFpv/wBrgtIAgp/MAIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/w6Vsf9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9N5/r/D5ax/wCDof8Ag6H/Co+s/0vj + 9/9P6fz/T+n8/0/p/P9P6fz/T+n8/0/p/P9I5/r/PuP4/zPc8f8p0uj/GsHa/wqlwf8Aiaj/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8RmbT/TOX4/0/p/P9P6fz/T+n8/0/p + /P9P6fz/GqW//wCDof8Ag6H/AIOiewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFtuQwBab/8AWm//AV1x/0fa7f9P6fz/T+n8/0/p/P89yNz/C26D/wBab/8AWm//AFpv/wBa + b/YAW3BwAAAAAQAAAAAAg58lAISifgCCodcAg6H/AIOh/wCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p + /P9P6fz/T+n8/ya0zf8Ag6H/AIOh/wCDof8Knbn/EKzF/xCuyP8Sr8j/EavG/wymwP8InLn/A5Sy/wCK + p/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6HMAIShegCD + oYoAg6H/AIOh/wCDof8Yor3/Tuj7/0/p/P9P6fz/T+n8/0/p/P8ltM3/AIOh/wCDof8Ag6GiAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+oAFpv/wBab/8YhZr/T+n8/0/p + /P9N5fj/JZ2x/wFccf8AWm//AFpv/wBab/8AWm/LAFdwKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC + oTEAg6GrAIOh/wCDof8OlbH/T+n8/0/p/P9P6fz/T+n8/0/p/P8+0+n/AYSi/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIKg/wB6lv8AeZOgAICfIAAAAAAAAAAAAAAAAACDopYAg6H/AIOh/wCDof8eq8T/T+n8/0/p + /P9P6fz/T+n8/zHC2v8Ag6H/AIOh/wCDocYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFlzFABab/kAWm//AFpv/ze/0v9P6fz/QM/i/w90if8AWm//AFpv/wBab/8AWm/7AFpwgABA + gAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFoEsAg6H/AIOh/w6Vsf9P6fz/T+n8/0/p + /P9P6fz/TOb5/wySr/8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOh/waKqP8Xobz/I67H/wBke/8AXHH/AFpv/gBddBYAAAAAAAAAAAAA + AAAAAAAAAKqqAwCDoK8Ag6H/AIOh/wCDof8ls8z/T+n8/0/p/P9P6fz/PdHn/wCDof8Ag6H/AIOh7QAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW29zAFpv/wBab/8Jan//Teb5/yqm + uv8CXnP/AFpv/wBab/8AWm//AFpv1wBYcTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIWgSwCDof8Ag6H/DpWx/0/p/P9P6fz/T+n8/0/p/P8ir8n/AIOh/wCDof8Ag6H+AIKhxgCD + ocAAg6HMAISh2wCDoeUAg6HjAISh2wB+m+YAepX/AHiT/wZ+mP8ot8//M8Xc/z7T6P9I4PT/T+n8/0/p + /P8xssb/AFpv/wBab/8AWm/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICqBgCDob0Ag6H/AIOh/wCD + of8uv9b/T+n8/0/p/P9J4vX/AIOh/wCDof8Ag6H/AICcEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAQBab9cAWm//AFpv/xyNof8Se4//AFpv/wBab/8AWm//AFpv/gBacJAAVXEJAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhaBLAIOh/wCDof8OlbH/T+n8/0/p + /P9P6fz/O8/l/wCDof8Ag6H/AIOh/wCCooMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdtIwBa + b/4AWm//AFpv/0DN4f9P6fz/T+n8/0/p/P9P6fz/T+n8/x2Po/8AWm//AFpv/wBabpQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAIiZDwCDotIAg6H/AIOh/wCDof8zxdv/T+n8/0/p/P8Giqj/AIOh/wCD + of8AgqE5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXHE9AFpv/wBab/8AWm//AFpv/wBa + b/8AWm//AFpv/wBab+EAWXBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACCoj8Ag6H/AIOh/wyTsP9P6fz/T+n8/0vj9/8Jj6z/AIOh/wCDof8Ag6HQAICqBgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvwQBab/8AWm//Ipis/0/p/P9P6fz/T+n8/0/p + /P9P6fz/CWqA/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICdGgCE + odsAg6H/AIOh/wKFo/87z+X/T+n8/xKatf8Ag6H/AIOh/wCEol0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABbb6MAWm//AFpv/wBab/8AWm//AFpv/wBab/8AWXCgAFV3DwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/0/p + /P9P6fz/HqrE/wCDof8Ag6H/AIOh+ACCny0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAWnBgAFpv/wBab/8GZHr/Teb5/0/p/P9P6fz/T+n8/0TW6f8AWm//AFpv/wBab/4AWXMUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICjJACDoeoAg6H/AIOh/wWJp/8/1On/HajC/wCD + of8Ag6H/AIOghAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWmkRAFpv/wBab/8AWm//AFpv/wBa + b/8AWm/pAFpwUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Jj6z/T+n8/zfK4P8Ag6H/AIOh/wCDof8Ag6F3AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVagwAWm/zAFpv/wBab/81us7/T+n8/0/p + /P9P6fz/MLLF/wBab/8AWm//AFpv1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIKiNwCDofMAg6H/AIOh/weLqf8eqsP/AIOh/wCDof8Ag6GqAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABacGkAWm//AFpv/wBab/8AWm//AFlvsQBZbxcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wmP + rP9J4fX/B42q/wCDof8Ag6H/AIOhxQCqqgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABab58AWm//AFpv/xeFmP9P6fz/T+n8/0/p/P8djqP/AFpv/wBab/8AWm6UAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOiQgCDofkAg6H/AIOh/wCD + of8Ag6H/AIOh/wCDodEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvgQBab/8AWm//AFpv/wBb + cGIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8Ag6H/CY+s/xqkv/8Ag6H/AIOh/wCDofQAgKMkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpvPgBab/8AWm//AV1x/0fb + 7v9P6fz/T+n8/wlqgP8AWm//AFpv/wBbcFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISgWwCDof4Ag6H/AIOh/wCDof8Ag6H/AIOh9QD//wEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAVW0VAFpupABab6UAWmsfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCD + of8Ag6H/AIOh/wCDof8Ag6H/AIOiawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAFpv3ABab/8AWm//KqW5/0/p/P9E1un/AFpv/wBab/8AWm/+AFlzFAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOhbwCD + of8Ag6H/AIOh/wCDof8Ag6H/AISeHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEojwAg6H/AIOh/wCDof8Ag6H/AIOh/wCEoLoA//8BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm98AFpv/wBa + b/8McIX/T+n8/zCyxf8AWm//AFpv/wBZb9MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKhhwCDof8Ag6H/AIOh/wCDof8AgaBDAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACD + of8Ag6H/AIOh/wCDof8Ag6HvAICkHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABebx4AWm/9AFpv/wBab/89yNz/HY6i/wBab/8AWm//AFpulAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAA//8BAIOhowCDof8Ag6H/AIOh/wCComgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKI8AIOh/wCDof8Ag6H/AIOh/wCCoF4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABa + b7oAWm//AFpv/x+Sp/8JaoD/AFpv/wBab/8AW3BUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqqoDAIOiygCDof8Ag6H/AIOgRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE + ojwAg6H/AIOh/wCDof8Ag6GtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtvWgBab/8AWm//AFpv/wBab/8AWm//AFpv/gBZ + cxQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAgKoGAIOhZwCDo1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOjJwCDof8Ag6H/AIOh/wCAohYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVXEJAFpv8ABab/8AWm//AFpv/wBab/8AWm/SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIShcgCDoKcAhZ8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm+XAFpv/wBab/8AWm//AFpv/wBa + b5MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABacTYAWm//AFpv/wBab/8AWm//AFtwVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////4///4H///////////////+H + //+B///8f///////////g///gf//+H///////////4H//wH///B///////////+A//8B///gf/////// + ////wH/+AP//wH///////////8A//gD//4D////////////AH/AAA/8A////////////wA4AAAA+AP// + ///////8f8AAAAAAAAH//////////B/AAAAAAAAB//////////wP4AAAAAAAAf/////////+A+AAAAAA + AAH//////////gDAAAAAAAAB//////////8AAAAAAAAAAP//////////AAAAAAAAAAA//////////4AA + AAAAAAAAH//////5//+AAAAAAAAAAAf/////8H//wAAAAAAAAAAD//////Af/4AAAAAAAAAAAP/////4 + B/8AAAAAAAAAAAB/////+AH+AAAAAAAAAAAAP/////wA/AAAAAAAAAAAAB/////+ADgAAAAAAAAAAAAP + /////gAAAAAAAAAAAAAAB/////8AAAAAAAAAAAAAAAP/////AAAAAAAAAAAAAAAB/////4AAAAAAAAAA + AAAAAP////+AAAAAAAAAAAAAAAB/////wAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAB/////gAAAA + AAAAAAAAAAAP//n/8AAAAAAAAAAAAAAAB//wH/AAAAAAAAAAAAAAAAP/8AfwAAAAAAAAAAAAAAAD//AD + 8AAAAAAAAAAAAAAAAf/wAeAAAAAAAAAAAAAAAAD/8AHgAAAAAAAAAAAAAAAAf/AAwAAAAAAAAAAAAAAA + AD/wAMAAAAAAAAAAAAAAAAA/8ACAAAAAAAAAAAAAAAAAH/AAgAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAA + AAAAAAAP8AAAAAAAAAAAAAAAAAAAB/AAAAAAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAD4AAAAAAA + AAAAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAIAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAA + AAAAAAAAAAAAAQA4AAAAAAAAAAAAAAAAAAMAeAAAAAAAAAAAAAAAAAADAPwAAAAAAAAAAAAAAAAAAwH8 + AAAAAAAAAAAAAAAAAAcH/AAAAAAAAAAAAAAAAAAHD/wAAAAAAAAAAAAAAAAADx/wAAAAAAAAAAAAAAAA + AA9/4AAAAAAAAAAAAAAAAAAP/4AAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAB/8AAAAAAAAAAAA + AAAAAAAf8AAAAAAAAAAAAAAAAAAAH+AAAAAAAAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAA/gAAAAAAA + AAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///+AAAAAAAAAAAAAAAAAf/// + gAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAA + Af//+AAAAAAAAAAAAAAAAAP///AAAAAAAAAAAAAAAAAD///wAAAAAAAAAAAAAAAAB///4AAAAAAAAAAA + AAAAAAf//8AAAAAAAAAAAAAAAAAP//+APAAAAAAAAAAAAAAAH///g/4AAAAAAAAAAAAAAB//////AAAA + AAAAAAAAAAA//////4AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAH//////AAAAAAAAAAAAAAD///// + /gAAAAAAAAAAAAAB//////wAAAAAAAAAAAAAA//////8AAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAP + //////AAAAAAAAAAAAAAH//////wAAAAAAAAAAAAAD//////4AGAAAAAAAAAAAD//////8AP4AAAAAAA + AAAB///////AP+AAAAAAAAAAA///////gf/AAAAAAAAAAAP//////8f/wAAAAAAAAAAD/////////4AA + AAAAAAAAA/////////+AAAAAAAAAAAP/////////gAAAAAAAAAAD/////////wAAAAAAAAAAA/////// + //8AB8AAAAAAgAH////////+AA/wAAAAA8AB/////////gAf+AAAAAfgAf////////4Af/gAAAAH8AH/ + ///////8AP/4AP8AB/gB/////////AP/+AH/AA/8Af////////gH//gD/4AP/gD////////4H//4B/+A + D/8A////////+D//+Af/gA//gP////////D///gP/8Af/8D////////5///4H//AH//g//////////// + +B//4B//4P////////////g//+Af//D////////////4f//gP//4////////////+H//8D////////// + //////j///A////////////////9///wP///////////////////+H///////ygAAAAwAAAAYAAAAAEA + IAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKHfAIOh/wCJnQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAACXXSnAFpv/wBcb44AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCoYcAgp81AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6CMAIOh/wOHpMQAgJ8IAAAAAAAA + AAAAAAAAAAAAAABVcRIBXHH6G4uf/wRhdssAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8EAoajoACD + of8AhKB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgqJYBoqo+QuT + sPcDh6S3AKqqAwAAAAAAgIACAIakKgBrhZYId475I6zF/wFshvsAgp9aAIOiQgCAphQAAAAAAAAAAACJ + nQ0Dh6S/AoWj/gCDof8AgJ0aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJcb3UAW2+BAAAAAQAA + AAAAgp8tAYSh/Ubd8f8Jj6z4AoWj0QCGpLcAhaL0AIOh/wCDof8AhKL/Aouo/wCHpf8AhKL+AIOh/wCD + of8AhaTgAIOhqQKGpOIPl7P4KrnR/wOIptwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABb + cMgAWm//A15zyQBbbSoAjo4JAYSh/UHX7P9F3PD/Boyo/w2fu/4p0Ob/M9jw/yfA1/8cqsP/Qub5/z/k + +f875Pj/Md/0/yXU6v8Qssv/Aoik/xiivf9M5vn/GaS+/ACFoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABecRsAWm//DnOH/QFkefwAgJzmA4ek/zTG3f9M6Pr/RN7x/0Xc8P8kts7/BoSf/zSe + tf8Yi6T/FrzT/yHS6P8q1ur/Ndzv/0bk9/8Mk6//I7DK/03o+/9O6Pv/BIim/wCEou0AhKFXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKgMwCD + oZAAgKoMAAAAAAAAAAAAAAAAAAAAAAAAAAADXnaiC4We/AGEov8UqcP/FaK9/ye1zv9B4/n/HtHo/wiW + sf8smrP/qd/w/8jw/v98xNb/BJSv/wDA1/8AwNf/AMDX/wDA1/8Kxt3/ItPo/0Di9/9B2O3/A4el/xu6 + 0/4AgqH/AISirACAnBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIOiUgCDof8Ag6H/AISgbgCAqgYAAAAAAAAAAACImQ8Ag6DHAoem/yzS5/9P6fz/LMjf/w+8 + 1f8AxNv/BZiy/z6mvv/A7f7/tev+/6no/v+u5vX/Doai/wC50P8Awdj/AMHY/wDB2P8Awdj/AMHY/wDB + 2P8Syt//LtHm/0/p/P8+4PX/DZq3+wCFo+UAhaEuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOHpcMBhKP/CI6r9wKGo+gAgqJgAIiiHgCFpNcFkq76PeH2/03p + /P8i2/L/AMjg/wDF3P8AtMz/CIah/7Dm+f+v6v7/oub+/4nj/P+b6vz/SbHG/wCdt/8AxNv/AMTb/wDE + 2/8Aw9r/AMPa/wDD2v8Aw9r/AMPa/xPN5P9B4/b/TOj7/yC40v0AhaL2AISiTQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCny0BhKL8P9Tp/y6+1v8Giqj4Aoek+QWS + r/xA4/j/Sej7/xHV7f8AyeH/AMjg/wDI4P8Bn7r/RKrD/7js/v+g5f3/h+P8/23g+/9r4/r/edrs/wOF + oP8AxNv/AMfe/wDH3v8Ax97/AMfe/wDH3v8Axt3/AMbd/wDG3f8DyN//Ldrv/0/p/P8ryuD/AISi/ACC + oFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChKOgFJy3+E/p + /P9M5fj/KrnR/wWJp/8Xo73/DdTs/wDM5P8AzOT/AMvj/wDL4/8GmbT/YrvT/67q/f+E4/z/a+D6/1Le + +f862/j/d+j6/yeft/8AqsP/AMri/wDJ4f8AyeH/AMnh/wDJ4f8AyeH/AMnh/wDJ4f8AyOD/AMjg/x/T + 6f9O5/v/Ls7k/wCDof0AhKFXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgJ8YAYWh+DjL4f9P6fz/T+n8/0ni9v8Nx9//AM7m/wDO5v8Azub/AM7m/wDO5v8En7n/UbPN/5no + /f9p4Pr/T935/zbb9/8n2vf/VuL5/17O4v8Bi6b/AMzk/wDN5f8AzeX/AM3l/wDN5f8AzOT/AMzk/wDM + 5P8AzOT/AMzk/yPT6P995fL/r/T9/4/j7/8nl7D8AIOiQgAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfSACD + of8Ag6KhAISjOgAAAAAAAAAAAoWijgqQrf9O5/v/T+n8/yPd8/8A0ur/ANLq/wDS6v8A0ur/ANHp/wDR + 6f8AsMn/CYej/3jg9v9r4/r/QN34/yfa9/8n2vf/Mtz3/33o+f8Qjqj/ALfQ/wDP5/8Az+f/AM/n/wDP + 5/8Az+f/AM/n/wDP5/8W0uj/VNbn/1HE1f8rqb//Ip63/1C6zf+R1+P/Sae99wCAoyQAAAAAAAAAAAAA + AAAAAAAAAIKhagCDof8AlLH7AIOh/QCDokoAAAAAAISiugOIpv8uvtb/QOX5/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8Azef/BJSw/waHo/8yu9T/VuH3/07h+P8w3Pf/J9r3/2bl+f9GvdT/ApSv/wDS + 6/8A0uv/ANLr/wDS6v8A0ur/ENTp/zDF2v8lts3/AKjD/wC2z/8At8//AK3H/w6ZtP8Bgp//W7XI/iaW + r9gAkpIHAAAAAAAAAAAAAAAAAIKgVgCDof8A1O3/AKK9/gCFo9oAhKE2AIOh/x250v8Gi6n/BtTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/AtTt/wO50/8Ci6f/DJGr/0HK4/9h5Pn/TuH4/2rl + +f985PX/AoCc/wDD3f8A1O3/ANTt/wTR6P8byuH/FcLb/wDO5/8A1O3/ANTt/wDU7f8A1O3/ANTt/wbV + 7v82z+X/CpCt/zKdtf0AhaKWAAAAAAAAAAAAAAAAAIOiZQCIpfoA1O3/ALnU/wCDof8AhKGeCZq3+03p + /P8Nqsb/ANTt/wDU7f8A1O3/ANTt/wDU7f8C1O3/BtXt/wnW7v8M1u7/Dtbu/xDX7v8R1ez/Cq/J/wKF + oP8an7n/W9nu/2jh9f8orsf/AH+b/wO2z/8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8P2PD/SOP2/xGduP8KiKT/AISiVQAAAAAAAAAAAIShmwCSsPkA0+z/ALHM/wCD + of8Ag6H/Kc/l/0fn+v8B1e3/ANTt/wDU7f8A1O3/BNXt/wnW7v8O1u7/Edfu/xXY7v8Y2O//Gtjv/xzZ + 7/8d2e//Htnv/xzS6P8MpL//AYCb/wKAnP8Im7X/EMvi/xDX7v8M1u7/B9Xt/wLU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/Id30/03n+v8No77+AISi7wCAnxAAjqoJAIWi8ACu + yf8A0en/AMbf/wCfu/4Aj6z8R+b6/yrf9f8A1O3/ANTt/wPV7f8J1u7/Dtbu/xTX7v8Y2O//Hdnv/yHa + 7/8k2vD/Jtrw/ynb8P8p2/D/Ktvw/yrb8P8p2/D/Iczi/xvB2f8i2u//H9nv/xrY7/8V2O7/ENfu/wvW + 7v8F1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/zzk+f853vP/AIqm+gCC + oYUAg6FnAIWj/ADL5P8A0en/ANPs/wDT7P8P1+//FZ65/wKLqP8Cob3/AqTA/waqxf8Krsj/DrLL/xO2 + z/8XvNT/Icvi/yzb8P8w3PD/Mtzx/zXd8f813fH/Nt3x/zbd8f813fH/Mtzx/zDc8P8t3PD/Kdvw/yXa + 8P8f2e//Gtjv/xTX7v8O1u7/B9Xt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/xXZ + 8f9P6fz/DbjS/wCFo+oAhaLuAKbB/gDT7P8A0+z/ANLq/wDS6/8i3fT/GKO9/wCDof8ChaP/Ia7H/x2p + w/8ZpL//FJ66/xGatv8OlrL/C5Wx/zfd8f873vH/Pt7x/0Hf8v9B3/L/Qt/y/0Lf8v9B3/L/Pt7x/zze + 8f843fH/NN3x/y/c8P8p2/D/JNrw/x3Z7/8X2O//ENfu/wjV7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f9N6fz/IdPq/wCDof8Aiqf6ANDo/wDT7P8A0ur/ANHp/wDS6v8u3/X/Qeb6/wC+ + 2P8Diqj/GaS//0vj9/9P6fz/T+n8/0/p/P9P6fz/P9/y/0Pf8v9H4PL/SuDy/03h8v9O4fP/TuHz/07h + 8/9M4fL/SuDy/0fg8v9D3/L/P97x/0/i8/9c4/T/ZOX0/2Lk9P9X4/P/Qd/y/yDa7/8H1e3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9E5vr/Ktft/wCDof8Awdv/ANHp/wDN5v8AyOD/AMzk/wDP + 5/864/f/OOP4/wDU7f8OzeX/CJaz/wuRrv9B2O3/T+n8/0/p/P9M5/r/SeDy/07h8/9S4vP/VuPz/1nj + 8/9a4/P/WuPz/1nj8/9Y4/P/VeLz/1Li8/9W4/P/duj1/3Th9v9gyPT/Xrby/1qw8f9ZufL/Vczz/1ni + 9P883vH/Cdbt/wDU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f9L6Pv/JM3k/wCEovcA0ur/ANLq/wC3 + 0v8Ag6H/AISi/gCHpf074ff/M+L3/wDU7f8T1+7/Jtru/xSpw/8Dh6X/MsTb/0/p/P9O5ff/U+Lz/1nj + 8/9e5PT/YeT0/2Tl9P9m5fT/ZuX0/2Xl9P9j5fT/YOT0/2Ll8/9/6vb/Z8vy/06d7f9tqeH/krTT/5m3 + 0P+Ksdb/WaPn/06j7P9K1fH/Od7x/wLU7f8A1O3/ANTt/wDU7f8A1O3/ANTt/wnW7/9P6fz/GLPM/ACE + orIA0uv/ALvV/wCEov0Ag6CvAIKiWACDof832u//LtTp/wDN5/8U1+7/Ltzw/zfd8f8qwNb/A4il/yCs + xv9S4/P/XeT0/2Pl9P9p5vT/beb1/3Dn9f9y5/X/cuf1/3Hn9f9v5/X/a+b1/33p9v9lxen/SZfl/6S7 + zv/Dycn/wcjI/8LIyP/Fy8v/ys/Q/5C22f9Dl+T/Otfw/xrZ7/8A1O3/ANTt/wDU7f8A1O3/ANTt/x/c + 8/9O6Pz/BYuo/QCEoFkAp8P/AISi/gCEoo8A//8BAAAAAACFo/AVpsL/AYWi/we91v8P1+7/Nd3x/z3e + 8f9G4PL/Q9Pm/w6Trv8xts3/ZuX0/23m9f9z5/X/eOj1/3zp9v9+6fb/fun2/33p9v966fb/fOn1/3ne + 9/80gL//qsDS/8vQ0P++xcX/vcTE/7m1rf+0oY7/vbeu/9jc3P+Br9v/OKTi/yTa7/8C1O3/ANTt/wDU + 7f8A1O3/ANTt/zji+P882u//AISj6wCAqgYAhaPxAIShXwAAAAAAAAAAAIGjRQKHo/oNlLH/P9Tq/zfj + 9/8H1e3/Od7x/0Pf8v9M4fL/VeLz/1zj8v9l4/L/b+f1/3bo9f996fb/g+r2/4fr9/+K6/f/iuv3/4nr + 9/+E6vb/hur2/1S27v86mt7/wcTE/8DHx//Ax8f/vLCj/6JlNv+IVzH/omU2/8Gwof/a4uj/MILX/xvQ + 7P8H1e3/ANTt/wDU7f8A1O3/B9bv/03o/P8htMz8AIKikQAAAAAAgJwSAAAAAACAnBIChaSlAYWj/Cm5 + 0f5N5/r/T+n8/0/p/P8X2/H/M93x/0jg8v9R4vP/W+Pz/2Tl9P9t5vX/duj1/37p9v+H6/f/juz3/5Pt + 9/+W7fj/lu34/5Pt9/+O7Pf/iev3/0Ko6v8+r/z/gpek/8TKyv/O09P/p3lU/0suGP8AAAD/QSgV/6Nw + SP/4+fn/O4fU/xG95f8C1O3/ANTt/wDU7f8A1O3/Id30/03o/P8Gi6j/AIOgRgAAAAAAAAAAAIKiWAKG + o+8RmbX4Q9nu/0/p/P9P6fz/T+n8/0/p/P9B4fX/INXt/0vh8v9U4vP/XuT0/2jm9P9x5/X/e+n2/4Tq + 9v+O7Pf/lu34/53u+P+i7/j/ou/4/53u+P+W7fj/juz3/0Go6v9Is/z/MlVt/3Z5ef/a3t7/sIZk/3RS + Of+DgoL/uKqf/7GIZ///////LX3M/wm65P8A1O3/ANTt/wDU7f8A1O3/PuX5/zfc8P8Ag6L8AIiZDwAA + AAAAgqJgAIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AIOh/wCDof8Ag6H/AYel/0nf8P9W4/P/YeT0/2vm + 9f916PX/f+n2/4nr9/+S7ff/nO74/6bw+f+t8fn/rfH5/6Tw+f+a7vj/kez3/1C38P9LpuP/PnWa/yw2 + Pf/Lzs7/5NzV/6lzSP/Xu6b/1Lmk/9zSyf/n7fH/Fm3D/wHO6v8A1O3/ANTt/wDU7f8L1/D/T+n8/xu5 + 0/4AhaLDAAAAAAAAAAAAgJkKAIWgSwCCoDMAgqAzAIKgMwCCoDMAhKHJC6XB/0Tb7/8PzOX/AMzm/yvb + 8P9X4/P/YeT0/2zm9f926PX/f+n2/4nr9/+T7ff/nO74/6bw+f+u8fn/rPH5/6Tw+f+a7vj/kOz3/3rg + 9v9auPn/Tomx/zJNXv8qLTD/4+Tk//b19P/j29T/6erp//39/f9imc3/EZTQ/wDU7f8A1O3/ANTt/wDU + 7f8q3/X/S+f7/wOJp/sAg6JjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVdw8BaoTdAISi/zfh + 9v834/f/ANTt/wXV7f9O4fP/YOT0/2rm9P9z5/X/fen2/4fr9/+P7Pf/mO74/5/v+P+i7/j/oe/4/5zu + +P+V7fj/i+v3/4Lq9v9cve3/b7zw/0Zof/8xQUz/JSkr/6usrP/4+Pj/+vv8/3qn0v8PcL3/Cc/r/wDU + 7f8A1O3/ANTt/wfW7v9L6Pv/LdHn/wCFo/IAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZmCgNf + dMYPdor4Dpe0/w2wzP9O6Pz/Ftrx/wC81v8EjKr/XuT0/2fl9P9w5/X/eOj1/4Hq9v+J6/f/j+z3/5Tt + 9/+X7fj/le34/5Lt9/+M7Pf/hOr2/33p9v905/X/Xrfk/1+Rs/9mk7L/KD1M/w8VG/8AAQH/AQUJ/wc2 + Vf8axuH/AdTt/wDU7f8A1O3/ANTt/y/h9v9K5/v/BpGv+QCEoXoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAVYAGA11zyABab/8glKn+C3qR9wCEof8s2u//QNzx/wKKqP8tvdT/Q+T3/2Hl9P9q5vT/cuf1/3rp + 9v+A6vb/her2/4nr9/+L6/f/iev3/4fr9/+C6vb/fOn2/3Xo9f9t5vX/ZeX0/2DZ8f8+d5P/PG2M/y1T + a/8jUmv/ECsz/wkzOP8EOD//AM7m/wDU7f8A1O3/Fdnx/07o/P8ixt3+AIaj5ACAmQoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAWW+MAFpv/wBbcP0DX3WvAFlubQCBnq4Dk7D4FKC7/xagu/9P6fz/T+n8/0rm + +v9g5fX/aub0/3Hn9f926PX/e+n2/33p9v9/6fb/fen2/3vp9v936PX/cuf1/2zm9f9l5fT/XeT0/1Xi + 8/9Bvsz/Lpaj/0fBz/9BtMP/R4GI/yq0xP8jjJj/HoKO/wDU7f8L1/D/Sej7/zbc8f8Ag6H9AIOgRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWXMUAFhxNAAAAAAAAAAAAAAAAACApA4AhKLsBImm/0ff + 8/9P6fz/T+n8/0/p/P9N6Pv/StTm/1bV5/9s5vX/b+f1/3Hn9f9y5/X/cef1/2/n9f9s5vX/Z+X0/2Ll + 9P9b4/P/VOLz/03h8v9F4PL/OM3f/zvW6P9Dydn/RMnZ/1mzvv8A1O3/RKy4/xnN4/9E5vr/QeP4/wOO + q/kAhKKRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACG + oSYBhKP5Ncfe/0/p/P9P6fz/S+T4/y/A1/8Nl7P/AYWk/zC6z/9h5PT/YuTy/0jJ2/9m5fT/ZeX0/2Tl + 9P9g5PT/XeT0/1jj8/9S4vP/TOHy/0Xg8v893vH/Nt3x/y3c8P8l0OX/D8bc/yiPm/8A1O3/Edfu/1TB + z/9B5Pn/CJez+gCFo8IAgKoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIC/BAKIpM8cqMH7T+n8/z/U6v8eqsT/BIil/wqRrv8GsMz/AM3n/wbV7f8z3fH/Qszg/wWJ + p/9X5fX/WePz/1jj8/9V4vP/UuLz/03h8v9I4PL/Qt/y/zze8f813fH/LNvw/xbY7/8E1e3/ANDo/wGa + q/8j2PD/Ncrg/zW/0f8Fkq76AISj0gCImQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAISigQSJp/squtL/DZSw9gGEovwChqP5Cpm1/kTk+P9M6Pv/Jd70/wPV + 7v8A1O3/BcLb/wSIp/9P6fz/Tuf6/03j9P9J4PL/RuDy/0Lf8v893vH/N93x/y/c8P8a2O//BdXt/wDU + 7f8A1O3/Fdnx/zzX6/85zeP/Aoek/wKIpfwAgZ7FAICkDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgaBDAIOh/wCDof8Dh6ThAIOicwCAphQAgKEmAH2a/ASK + qP8sz+X/Tuj8/0fm+v8k3vT/BcPc/wOIp/9P6fz/T+n8/0/p/P9A5fn/Fr3V/xLK4v8Q1+7/Ctbu/wHU + 7f8A1O3/A9Xu/xrb8v8+5fn/T+n8/0nm+v9F3PD/AIOh/wCDopYAmZkFAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6FUAIOh+gCCoj8A//8BAAAAAAAA + AAAAW3BUBWR6+Ci3z/8DiKX/DZu3/zLU6v9M6Pv/O9Hn/wSJp/9P6fz/T+n8/0/p/P87z+X/AIOh/wi/ + 2f8L1/D/Ftrx/yPd9P814vf/TOj7/0fe8v8uyeD/R+H1/0/p/P9O6Pv/AYWi/QCCoDMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAADX3bBJZ2x/k/p/P9C1ur/DYmj/QCCoP8Gjar9GLDK/gSIp/9P6fz/T+n8/0vk + 9/8Kj6z/KbjP/0/p/P9P6fz/T+n8/0rn+/874fX/I8vj/wiat/0BhKL/N8rg/0/p/P9P6fz/CpCt9QCE + oFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABdbCEAW3D9RNXo/zvF2f8IaXz4AmB00ABogDYAg6BpAISj0gOH + pf9P6fz/T+n8/x2pw/8Gjqz/GLbQ/xWzzv8Ur8r/C566/wGJpv8Ag6H/AIKg/wCCoKwAgqKPAoWj+z7T + 6P9P6fz/F5+7+ACEon4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcb4UQd4z4I5mt/AFccfsCXHB/AECABAAA + AAAAAAAAAISiPAOHpfpP6fz/N8vh/wGEov0Ag6K3AISjtwCBnMIAdZH+I67H/zDC2f8pq8H/A2B13AAA + AAAAAAAAAoajegOHpfpB1+z/JbPM/wKGpKkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECABABab/8AWm//Al913QBc + cDIAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfpJ4vX/BYuo+ACEonAAAAAAAAAAAAAAAAADYHXfLq7C/0/p + /P8cjaL7AlxwlgAAAAAAAAAAAAAAAAKEopcGjKn4KLbO/wSIptQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhuOgBa + b/8CXnKWAGBgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPAOHpfoao735A4ikxQD//wEAAAAAAAAAAAAA + AAAAW252D3SI9k/p/P8HaHz2AFtwVAAAAAAAAAAAAAAAAAD//wEDhqSpAIOh/wCDof8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAGZmBQBccSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISiPACDof8BhKP3AISlHwAA + AAAAAAAAAAAAAAAAAAAAXHAZAVxw/Ti+0/8AWm/+AFlzFAAAAAAAAAAAAAAAAAAAAAAAgKoGAYSj2gCD + of8Ahp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOfQACD + of8AgqFiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA190uwZlef8DYHXcAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAIiZDwCDoqEAi6ILAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICfCACEoFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpwUgBab/8CXHGZAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD//n8f7/8AAP/+Px/P/wAA//8eH4//AAD/9wAAD/8AAP/jAAAP/wAA//AAAA//AAD78AAAA/8AAPnw + AAAB/wAA+GAAAAD/AAD8AAAAAH8AAPwAAAAAPwAA/gAAAAAfAADOAAAAAA8AAMYAAAAABwAAwgAAAAAD + AADAAAAAAAMAAIAAAAAAAQAAgAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAACAAAAAABAAAYAAAAAAEAAHgAAAAAAQAA4AAAAAADAADAAAAAAAMAAIAAAAAAAwAA/AAAAAAH + AAD8AAAAAAcAAPgAAAAADwAA8AAAAAAPAADhAAAAAB8AAP+AAAAAHwAA/4AAAAA/AAD/AAAAAH8AAP4A + AAAA/wAA/jgAAAH/AAD++AAAA/8AAP/wAAAD/wAA//BgAAP/AAD/4fAA4f8AAP/j8eDh/wAA/+fx8fH/ + AAD///Px+f8AAP//9/H9/wAA////+f//AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDof8Giql2AAAAAAAA + AAAAAAAAAAAAAAVjeegGZXrcAAAAAAAAAAAAAAAAAAAAAAD//wEBhqS4AIKjPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACY2qrwWJ + pvoGi6daAAAAAACOqgkAb4haFIif8Qhzie4AhJ46AIWmFwAAAAAAgKoGB4ypsgCDof8AhKUfAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXnJyA2F1swBV + gAYFiaZwLsDX/g2UsfYBiqfhAYqo8Amct/oTq8b/D6vF/gehvPEBj6ztAIin6geNqOstu9T5CI2r4AAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABc + bycAWm//B2qA4gCEoM8grsf+RN/y/irC2f8ru9P/JZu0/xaSrP8z2u7/O9/z/0Xl+P8Rnbj/N8vh/zvQ + 5f8GjKrnAIOjJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABhKLkAIKiNwAA + AAAAAAAAAAAAAAV1jdIIkq7/Ksri/xijv/8l1Ov/Ep23/3jG2//E7v3/W7bL/wCzzP8AwNf/AMDX/w7I + 3v8m1ev/L8Tb/yTD2v8KlbPxAIakcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASJ + psMBhKP+CI2qwwCFoiwAiad3DJ659UPk+P8p3PH/A8fe/waqwv9rwNf/ser+/5bk/f+Q3vD/CZu2/wDD + 2v8Aw9r/AMPZ/wDD2f8DxNv/ItPp/0bm+P8as8z2AIimqQCAqgYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAICmFAqRrug+1On/Epu28gqUsPs+3PD/F9ft/wDK4v8AyeH/D5ax/6vl+f+T5P3/beD6/2zk + +f8qpL3/AMDY/wDI3/8AyN//AMfe/wDH3v8Ax97/Csvi/z/h9v8qyd/6AImmtQCqqgMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAABoupfyq60vhP6fz/O9Dn/xHA2f8Azeb/AM3m/wDN5v8QmLP/muX5/2ng + +v9D3Pj/L9v3/17P5P8CpcD/AMzk/wDM5P8AzOT/AMvj/wDL4/8Ay+P/E8/m/3Pp9/902ur8F5SvsAD/ + /wEAAAAAAAAAAAAAAAAAg6H/AIumwQCAoyQAi6ILB42q+0ri9v8x4fb/ANLr/wDS6/8A0ur/ANLq/wio + w/88s8v/XuH3/zXc+P8n2vf/YuP4/xWXsv8AzeX/ANDo/wDQ6P8A0Oj/CNHo/zLM4P87vdH/G5+4/zOm + vP90ydj7Lp+2lwAAAAAAAAAAAAAAAACHpPIAvNb/AImm3ACGpWkQp8L5ILHK/wDU7f8A1O3/ANTt/wDU + 7f8A1O3/ANLr/wOtyP8LlrH/QMri/1Hh+f9W4vn/TsTa/wGyzf8A1O3/AdLr/xXK4f8YxN3/AMni/wDT + 7P8A0+z/Csvj/xunwf82pbz5AIOkRgAAAAAAgIACAIim8QDU7f8Ah6T1AIin3z/i9/8Lw93/ANTt/wDU + 7f8A1O3/BtXt/wzW7v8Q1+7/E9fu/xPR5/8KpL//G6G7/0jF3P8SlbH/BrHN/wLU7f8A1O3/ANTt/wDU + 7f8A1O3/ANTt/wDU7f8A1O3/Gdvy/yvA1/8Kj6ruAIaeFQCEojwAmLTtANLq/wCTsPUMnLj6P+X5/wDU + 7f8A1O3/CNXu/xDX7v8X2O//Hdnv/yHa7/8l2vD/Jtrw/yfb8P8gy+L/EKfB/xvQ5/8Y2O//Etfu/wrW + 7v8B1O3/ANTt/wDU7f8A1O3/ANTt/wDU7f8A1O3/MuH2/x3B2/wAiqejAIqnwgC71v8A0ur/ANLr/yDP + 5/8Ag6H/B5az/wqduP8NoLz/EafB/xSrxP8q1er/M93x/zfd8f853vH/Od7x/zfd8f803fH/L9zw/ynb + 8P8h2u//GNjv/w7W7v8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f8K1u//QeX6/wCJp+4AlbHwANPs/wDS + 6v8A0uv/N+P4/w650/8LlrL/QNXr/0Tb7/9B1+z/Nszi/z3d7v9F4PL/SeDy/0vh8v9L4fL/SeDy/0Xg + 8v8/3/H/Qd/y/07h8v9M4fP/P9/y/x/a7/8E1e3/ANTt/wDU7f8A1O3/ANTt/wDU7f9I5/v/AY6s7QDL + 5f8AzuX/AK3I/wC81v9A5fr/Ctbv/xHI3/8Ml7P/N8vh/0/p/P9K5Pf/T+Hz/1bj8/9b4/P/XeT0/13k + 9P9a4/P/VeLz/2rm9f9v2fX/Y7fu/1+w7f9ete3/Vc/y/znd8f8B1O3/ANTt/wDU7f8A1O3/AdXt/0jn + +v8AiaXvAM/n/wCVsvEAh6TCAIOh/zve8/8Mzuf/G9nv/zDX7P8VoLv/JbTM/1Tk9P9f5PT/Z+X0/2zm + 9f9v5/X/b+f1/2vm9f9y5/X/Z8jr/2io3/+0wsr/wcjI/73HzP93r93/R7np/yDa7/8A1O3/ANTt/wDU + 7f8R2PD/O9zx/wCKp58Aka3wAIimgQAAAAAAiaamD5i0/x2+1/8Z2O//PN7x/0rg8v8vuM//TM/i/27n + 9f936PX/fun2/4Lq9v+A6vb/fOn2/3TZ9P9EisL/y9HS/77Fxf+yo5X/oYBl/8rEvf9tq97/Jszs/wLU + 7f8A1O3/ANTt/yrf9f8lu9P2AIWfMACCoz0AjqoJB4ypkg+WsvRD2u7/Tun8/xfZ8P9E3/L/UuLz/2Dk + 9P9u5/X/e+n2/4br9v+P7Pf/k+33/5Lt9/+L6/f/Vbfq/0ag3/+5vr7/x8W//4pcOP8mFwz/mmxH/8ra + 5/8freD/A9Xt/wDU7f8B1O3/Reb5/wqUsOwAAAAAA4SiTQCDof8Unbj8NMbd/zXH3v81x97/JcPb/zzW + 6v9Y4/P/Z+X0/3Xo9f+E6vb/kez3/57v+P+m8Pn/oe/4/5Xt+P9UuOr/RJzY/0RRWf/b2dX/mm1J/8S9 + t/+7nIP/z93q/xOp3P8A1O3/ANTt/xTa8P873vL/AIqorAAAAAAChKJuA4immAaLp4AGi6eAA4mn8yW6 + 0v8HtND/H8/m/1rj8/9p5vT/eOj1/4fr9/+V7fj/pPD5/7Ly+v+n8Pn/mO74/3ba9v9Xr+n/N1Vo/3l+ + gP/r4Nf/zLWi/+zo5P9bm8//BMXm/wDU7f8A1O3/MuH2/x+80/YAg6BOAAAAAAAAAAAAAAAAAAAAAABc + cSQFepPxHMLZ/zDh9v8C0ur/QNPm/2fl9P926PX/g+r2/5Ds9/+b7vj/oO/4/5vu+P+Q7Pf/hOr2/1q2 + 5f9WgqD/OUxY/01UWf+Xmp3/YI61/xik1f8A1O3/ANTt/wvX8P9F5vn/BI2q5wCAgAIAAAAAAAAAAAAA + AAAAW20cAVxy+iGZrfwDi6j8PuP4/wyqxf8iuNH/W+X1/27n9f966fb/hOr2/4vr9/+O7Pf/i+v3/4Pq + 9v946PX/bOb1/1m73f9Og6T/JkRY/wsfK/8HKC//CIub/wDU7f8A1O3/OeP4/x+91vYAhaRiAAAAAAAA + AAAAAAAAAAAAAAFdccwDYHbfAl52bAB2lGYJmrb1GKK9/0/p/P9K6Pv/W+X2/23m9f916PX/eun2/3vp + 9v956Pb/c+f1/2vm9f9g5PT/VeLz/0HH1/87s8H/Qr7M/z+eqf8ol6T/CMPZ/yrf9f8z2u/+AIuqvwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIelEQiNq/JI4PT/T+n8/z/U6v8bq8X/Ja/H/2Xl + 9P9Z2On/aub0/2fl9P9i5fT/W+Pz/1Pi8/9I4PL/Pt3w/zLa7/8i0OT/LaGu/wDT7P9FxdT/O9/0/wON + qegAhp4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjKm4M8Xa+y291v0NlbH+GqfB/xTG + 3/8D1e3/KNvw/xObtf9T5ff/VOLz/1Li8/9L4fL/Q9/y/zve8f8t3PD/Etfu/wHU7f8JtMn/Kcbe/yrB + 1v8CiqjoAICeIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIWlggCDof8IjqrfBoundwCA + mpwQnbj+O9/0/zvk+P8V2fH/CJi0/0/p/P9K5/r/M9vx/xzL4v8b2e//D9fu/wLU7f8M2PD/L+H2/0nn + +/8outH+AIek4gCAnyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAChqNnAIOfSAAA + AAAAAAAACWp+sjG70f8PnLf/HLLL/jfc8f8Rm7b/T+n8/0/p/P8cq8T/FbvU/yLd9P8v4fb/Qeb6/zjX + 7f8ludL9T+n8/zfL4f8HjKmMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABVdw8Lb4LpRtns/xF6j+wEcIqvAIuosgmOq/1P6fz/N8vg/xGfuv4qz+X/J8jg/x26 + 0/8MmLX/AIej6geLquE1yN75Q9nu/wqQrboAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAABWJ5cBB3i/oIaH3hAFxyOgAAAAAAAAAACY+s3Eni9f8JjqvtAIOhdQB+ + m4AKe5TwOc3j/xeHm+sAW3EtAICZCgeMqNU4zOP9CI2q3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWm//BmV6tABddAsAAAAAAAAAAAAAAAAJj6rcF6C77QOI + o0sAAAAAAAAAAAlpf8dF1+n/CWp/5wAAAAEAAAAAAIaeFQiNquEBhaP7AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVcRIAAAAAAAAAAAAAAAAAAAAAAAAAAACD + of8IjKupAAAAAAAAAAAAAAAAA19yUxuKnvUJa4DEAAAAAAAAAAAAAAAAAISeHQCDof8AiJkPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAoOjoQCAqgwAAAAAAAAAAAAAAAAAVYAGAFpv/wRidnUAAAAAAAAAAAAAAAAAAAAAAJmZBQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/fPv//zzz//2AA//8AAP/vAAB/4wAAH/AAAA/4AAAHmAAAA4gA + AAOAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABwAAAAYAAAAGAAAAD8AAAA+AAAAfMAAAH/AAAD/gA + AB/xAAA//wAAP/8AAD//OIY//nnHP//557//++//KAAAABAAAAAgAAAAAQAgAAAAAAAABAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4yp0wuVqhgAcI8ZCnKI5QCAnwgA//8BCI2rtwCG + nhUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX3UjC2yAwRunw9khtc7vKLfQ+ia91f0nyeDxGarF8Ruo + wegAgJ8IAAAAAAAAAAAAAAAAAAAAAAeNqWsSmLSaAIOoIxmyy/IjyuD/K7bP/5/e8f9AutH/AMHY/w7J + 3f8gyeD/HLrU2gCMsCoAAAAAAAAAAAAAAAAAmZkFIa7H0ia40vgc0ej/AMvj/0++1f9r4Pr/Qcvj/wG/ + 2f8AyeH/AMnh/xbR5v9GzuHtFJuvMwAAAAAAlLCSAJu4qAyducYv0+v/ANTt/wDU7f8VttH/MMfg/03g + +P8ct9H/AdLr/wzM4/8cxt3/G7fP/z+2y/AAh6URAJu5pACuyegmyOHuBNLr/wTV7f8R1+7/Gtjv/xnK + 4v8hts7/ErrU/wfV7f8A1O3/ANTt/wDU7f8i1Oz/C6S8uwCtydwA0uv/F7XQ/xOmwf8mvtb/LMzh/z7e + 8f9C3/L/Pt7x/zbd8f803fH/G9nv/wDU7f8A1O3/Bdbu/yPG3uoAwNr1AJq21ibZ8f8cxd3/L8HZ/1Lj + 9P9h5PT/ZuX0/2Pl9P9wzev/fbvd/2+/4f8o0+7/ANTt/wfW7v8jw9vhAJWyexCatZ8sw9v/J9rw/0vY + 6f9p4vH/g+r2/4rr9/911vL/c6PF/6WYi/+NdWH/a77h/wHU7f8c2/L/EanHpwaKqaQapb/EJLfP+xrG + 3f9h5PT/f+n2/5zu+P+t8fn/e9rz/0Jzk/+mnpb/wbKm/2W42v8A1O3/KNXt+wCJqUEAAAAAEHiNYBGh + uvgbz+b/Ptbp/3jo9f+P7Pf/le34/4Tq9v9XoMP/O1Zo/zVVZv8IvNj/Edjw/x+/19cAAAAAAAAAAApq + gGoFdIs3HrHJ90nj+P81yN3/bOXz/3Hn9f9n5fT/VOLz/zzP4P9GxNP/J8XX/yfQ6PEAkbA6AAAAAAAA + AAAAAAAACpGsrB2pxNMWqcP3HtXs/xi40P9C5Pf/Ntrv/yfb8P8T2O//ItPq/x+40PIAmLZNAAAAAAAA + AAAAAAAAAAAAAACJoycAQIAEJJ6x1ROju+wbrsb2SeL1/x250f4n0+n/IsTa+Sa3z+4ks83FAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAABWZ8axJ6j7oAW20OFJ24jB+qw8wAhJw+Ip+13hqJn5oWmrdHD5ey4QAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVVQMAAAAAAAAAAAqProQPlLMyAAAAABJ7kbQMcodVAAAAAAiK + qGYAqqoDAAAAAAAAAAD7bwAA8A8AANAHAADAAwAAAAEAAAAAAAAAAAAAAAAAAIAAAAAAAQAAwAEAAOAD + AADABwAA8AcAAPSXAAD9vwAA + + \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index 6208921..ed93f70 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -42,7 +42,8 @@ public class SftpDrive : IDisposable, ISerializable private readonly AutoResetEvent _pauseEvent = new AutoResetEvent(false); private readonly CancellationTokenSource _threadCancel = new CancellationTokenSource(); private bool _exeptionThrown; - private SftpFilesystem _filesystem; + private SftpFilesystem _filesystem; + public VirtualDrive _virtualDrive { get; set; } private Exception _lastExeption; private Thread _mountThread; @@ -149,6 +150,10 @@ private void MountLoop() try { + //if (_virtualDrive.Status == DriveStatus.Mounted) + if (_virtualDrive != null) + _virtualDrive.AddSubFS("VFS-" + Letter, _filesystem); + int threadCount = 8; #if DEBUG threadCount=1; diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 8f90b3c..b681704 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -31,6 +31,8 @@ using Renci.SshNet.Sftp; using FileAccess = DokanNet.FileAccess; +//using System.Runtime.CompilerServices; +//[assembly: InternalsVisibleTo("WinSshFS, PublicKey=0024000004800000940000000602000000240000525341310004000001000100bb1df492d3d63bb4aedb73672b0c0694ad7838ea17c6d49685ef1a03301ae6de5a4b4b1795844de0ddab49254d05bd533b5a02c24a580346274b204b8975536ffe36b4e7bb8c82e419264c335682ad44861e99eef16e95ee978742064c9335283ecb6e2fd325ea97942a51a3fc1a7d9948dd919b0d4ad25148d5490cc37f85b4")] using System.Text.RegularExpressions; diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index cdb71ad..b163f6a 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -128,6 +128,8 @@ + + AboutForm.cs diff --git a/Sshfs/Sshfs/VirtualDrive.cs b/Sshfs/Sshfs/VirtualDrive.cs new file mode 100644 index 0000000..cad2fb4 --- /dev/null +++ b/Sshfs/Sshfs/VirtualDrive.cs @@ -0,0 +1,230 @@ + +#region + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using DokanNet; +using Sshfs.Properties; +using System.Collections.Generic; +#endregion + +namespace Sshfs +{ + [Serializable] + public class VirtualDrive : IDisposable + { + private readonly CancellationTokenSource _mountCancel = new CancellationTokenSource(); + private readonly AutoResetEvent _pauseEvent = new AutoResetEvent(false); + private readonly CancellationTokenSource _threadCancel = new CancellationTokenSource(); + private Thread _mountThread; + private Exception _lastExeption; + private bool _exeptionThrown; + + private VirtualFilesystem _filesystem; + + public string Name { get; set; } + + public char Letter { get; set; } + + public DriveStatus Status { get; private set; } + + //private readonly Dictionary _subsytems = new Dictionary(); + + + + public VirtualDrive() { } + + + internal void AddSubFS(string path, SftpFilesystem fileSystem) + { + _filesystem.AddSubFS(path, fileSystem); + } + + internal void RemoveSubFS(string path, SftpFilesystem fileSystem) + { + _filesystem.RemoveSubFS(path, fileSystem); + } + + + private void OnStatusChanged(EventArgs args) + { + if (StatusChanged != null) + { + StatusChanged(this, args); + } + } + + public event EventHandler StatusChanged; + + + + private void SetupFilesystem() + { + Debug.WriteLine("SetupVirtualFilesystem"); + + + } + + private void SetupMountThread() + { + if (_mountThread == null) + { + Debug.WriteLine("Thread:Created"); + _mountThread = new Thread(MountLoop) { IsBackground = true }; + _mountThread.Start(); + } + } + + private void MountLoop() + { + while (true) + { + Debug.WriteLine("Thread:Pause"); + + _pauseEvent.WaitOne(-1); + if (_threadCancel.IsCancellationRequested) + { + Debug.WriteLine("Thread:Cancel"); + break; + } + + Debug.WriteLine("Thread:Mount"); + + + try + { + _filesystem = new VirtualFilesystem("WinSshFS spool"); + _filesystem.Mount(String.Format("{0}:\\", Letter), Settings.Default.UseNetworkDrive ? DokanOptions.NetworkDrive | DokanOptions.KeepAlive : DokanOptions.RemovableDrive | DokanOptions.KeepAlive); + + } + catch (Exception e) + { + + _lastExeption = e; + _exeptionThrown = true; + _mountCancel.Cancel(); + } + Status = DriveStatus.Unmounted; + if (!_exeptionThrown) + { + + OnStatusChanged(EventArgs.Empty); + } + + } + } + + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Mount() + { + Debug.WriteLine("Mount"); + + if (Directory.GetLogicalDrives().Any(drive => drive[0] == Letter)) + { + throw new Exception("Drive with the same letter exists"); + } + + + Status = DriveStatus.Mounting; + + try + { + SetupFilesystem(); + } + catch + { + + Status = DriveStatus.Unmounted; + throw; + } + + SetupMountThread(); + + + + var mountEvent = Task.Factory.StartNew(() => + { + while (!_mountCancel.IsCancellationRequested && + Directory.GetLogicalDrives().All( + drive => drive[0] != Letter)) + { + Thread.Sleep(200); + } + }, _mountCancel.Token); + + + _pauseEvent.Set(); + + mountEvent.Wait(); + + if (_exeptionThrown) + { + + _exeptionThrown = false; + + throw _lastExeption; + } + if (Settings.Default.UseNetworkDrive) + Utilities.SetNetworkDriveName("WinSshFS spool drive" , Name); + Status = DriveStatus.Mounted; + OnStatusChanged(EventArgs.Empty); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Unmount() + { + Debug.WriteLine("Unmount"); + + Status = DriveStatus.Unmounting; + try + { + // Dokan.Unmount(Letter); + Dokan.RemoveMountPoint(String.Format("{0}:\\", Letter)); + } + catch + { + //Status = DriveStatus.Unmounted; + // OnStatusChanged(EventArgs.Empty); + } + finally + { + _filesystem = null; + } + + } + + public override string ToString() + { + return String.Format("{0}[{1}:]", Name, Letter); + } + #region Implementation of IDisposable + + public void Dispose() + { + Debug.WriteLine("Dispose"); + + try + { + Dokan.RemoveMountPoint(String.Format("{0}:\\", Letter)); + } + catch + { + Status = DriveStatus.Unmounted; + } + finally + { + _filesystem = null; + } + } + + #endregion + + } +} \ No newline at end of file diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs new file mode 100644 index 0000000..92ce880 --- /dev/null +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.Caching; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Text; +using DokanNet; + +using FileAccess = DokanNet.FileAccess; + + +namespace Sshfs +{ + internal sealed class VirtualFilesystem : IDokanOperations + { + + #region Fields + + private readonly string _volumeLabel; + private bool _debugMode = false; + + private readonly Dictionary _subsytems = new Dictionary(); + + #endregion + + #region Constructors + + public VirtualFilesystem(string label = null) + { + _volumeLabel = label; + } + + #endregion + + #region Methods + + internal void AddSubFS(string path, SftpFilesystem fileSystem) + { + _subsytems.Add(path, fileSystem); + } + + internal void RemoveSubFS(string path, SftpFilesystem fileSystem) + { + _subsytems.Remove(path); + } + + + [Conditional("DEBUG")] + private void Log(string format, params object[] arg) + { + if (_debugMode) + { + Console.WriteLine(format, arg); + } + Debug.Write(DateTime.Now.ToLongTimeString() + " "); + Debug.WriteLine(format, arg); + } + + #endregion + + #region DokanOperations + + DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, + FileMode mode, FileOptions options, + FileAttributes attributes, DokanFileInfo info) + { + if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || + fileName.EndsWith("autorun.inf", StringComparison.OrdinalIgnoreCase)) //.... + { + return DokanError.ErrorFileNotFound; + } + return DokanError.ErrorAccessDenied; + } + + private string GetSubSystemFileName(string fileName, out string subfs) + { + string[] parts = fileName.Split(new char[] { '\\'}, 3); + if (parts.Count() > 1) + { + if (parts[0] != "") + { + subfs = null; + return null; + } + subfs = parts[1]; + + if (!this._subsytems.ContainsKey(subfs)) + { + subfs = null; + return "\\"; + } + + if (parts.Count()==3){ + return "\\"+parts[2]; + } + else + { + return "\\"; + } + } + + subfs = null; + return fileName; + } + + DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) + { + Log("VFS OpenDir:{0}", fileName); + string subfs; + string subfilename = GetSubSystemFileName(fileName, out subfs); + if (subfs != null) + return ((IDokanOperations)this._subsytems[subfs]).OpenDirectory(subfilename, info); + + if (fileName == "\\") + { + info.IsDirectory = true; + return DokanError.ErrorSuccess; + } + + return DokanError.ErrorPathNotFound; + } + + DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) + { + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) + { + Log("Cleanup:{0},Delete:{1}", info.Context, info.DeleteOnClose); + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) + { + Log("Close:{0}", info.Context); + + return DokanError.ErrorSuccess; + } + + + DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, + DokanFileInfo info) + { + bytesRead = 0; + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, + DokanFileInfo info) + { + bytesWritten = 0; + return DokanError.ErrorAccessDenied; + } + + + DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) + { + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, + DokanFileInfo info) + { + Log("GetInfo:{0}:{1}", fileName, info.Context); + fileInfo = new FileInformation(); + fileInfo.FileName = fileName; + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) + { + Log("FindFiles:{0}", fileName); + + string subfs; + string subfilename = GetSubSystemFileName(fileName, out subfs); + if (subfs != null) + { + return ((IDokanOperations)this._subsytems[subfs]).FindFiles(subfilename, out files, info); + } + + files = new List(); + + foreach (string dir in _subsytems.Keys) + { + //SftpFilesystem fs = _subsytems[path]; + FileInformation fi = new FileInformation(); + fi.FileName = dir; + fi.Attributes = FileAttributes.Directory | FileAttributes.Offline; + fi.CreationTime = DateTime.Now; + fi.LastWriteTime = DateTime.Now; + fi.LastAccessTime = DateTime.Now; + files.Add(fi); + } + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.SetFileAttributes(string fileName, FileAttributes attributes, DokanFileInfo info) + { + Log("TrySetAttributes:{0}\n{1};", fileName, attributes); + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.SetFileTime(string filename, DateTime? creationTime, DateTime? lastAccessTime, + DateTime? lastWriteTime, DokanFileInfo info) + { + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) + { + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) + { + Log("DeleteDirectory:{0}", fileName); + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replace, DokanFileInfo info) + { + Log("MoveFile |Name:{0} ,NewName:{3},Reaplace{4},IsDirectory:{1} ,Context:{2}", + oldName, info.IsDirectory, + info.Context, newName, replace); + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.SetEndOfFile(string fileName, long length, DokanFileInfo info) + { + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.SetAllocationSize(string fileName, long length, DokanFileInfo info) + { + Log("SetSize"); + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.LockFile(string fileName, long offset, long length, DokanFileInfo info) + { + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.UnlockFile(string fileName, long offset, long length, DokanFileInfo info) + { + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, + out long used, DokanFileInfo info) + { + Log("GetDiskFreeSpace"); + + free = 0; + total = 1024; + used = 4; + free = total - used; + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features, + out string filesystemName, DokanFileInfo info) + { + Log("GetVolumeInformation"); + + volumeLabel = _volumeLabel; + + filesystemName = "SSHVFS"; + + features = FileSystemFeatures.CasePreservedNames | FileSystemFeatures.CaseSensitiveSearch | + FileSystemFeatures.SupportsRemoteStorage | FileSystemFeatures.UnicodeOnDisk; + //FileSystemFeatures.PersistentAcls + + + return DokanError.ErrorSuccess; + } + + DokanError IDokanOperations.GetFileSecurity(string filename, out FileSystemSecurity security, + AccessControlSections sections, DokanFileInfo info) + { + Log("GetSecurrityInfo:{0}:{1}", filename, sections); + + security = null; + + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.SetFileSecurity(string filename, FileSystemSecurity security, + AccessControlSections sections, DokanFileInfo info) + { + Log("TrySetSecurity:{0}", filename); + + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.Unmount(DokanFileInfo info) + { + Log("UNMOUNT"); + + // Disconnect(); + return DokanError.ErrorSuccess; + } + + #endregion + + /* + #region Events + + public event EventHandler Disconnected + { + add { Session.Disconnected += value; } + remove { Session.Disconnected -= value; } + } + + #endregion*/ + } +} \ No newline at end of file From 03fce15816b254b8c89aa23fe46b03b159e2c09d Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 22 Jul 2014 01:59:37 +0200 Subject: [PATCH 033/134] automounting by folder and gui update --- Sshfs/Sshfs/MainForm.Designer.cs | 141 +++++++++--- Sshfs/Sshfs/MainForm.cs | 128 +++++++++-- Sshfs/Sshfs/MainForm.resx | 372 +++++++++++++++---------------- Sshfs/Sshfs/SftpDrive.cs | 67 +++--- Sshfs/Sshfs/Utilities.cs | 137 ++++++++++++ Sshfs/Sshfs/VirtualDrive.cs | 4 +- Sshfs/Sshfs/VirtualFilesystem.cs | 41 +++- 7 files changed, 605 insertions(+), 285 deletions(-) diff --git a/Sshfs/Sshfs/MainForm.Designer.cs b/Sshfs/Sshfs/MainForm.Designer.cs index 2620fe4..68f9d05 100644 --- a/Sshfs/Sshfs/MainForm.Designer.cs +++ b/Sshfs/Sshfs/MainForm.Designer.cs @@ -54,6 +54,8 @@ private void InitializeComponent() this.privateKeyButton = new System.Windows.Forms.Button(); this.passwordBox = new System.Windows.Forms.TextBox(); this.authLabel = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); this.driveListView = new System.Windows.Forms.ListView(); this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.imageList = new System.Windows.Forms.ImageList(this.components); @@ -63,6 +65,9 @@ private void InitializeComponent() this.buttonPanel = new System.Windows.Forms.TableLayoutPanel(); this.muButton = new System.Windows.Forms.Button(); this.saveButton = new System.Windows.Forms.Button(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.virtualDriveCombo = new System.Windows.Forms.ComboBox(); + this.label9 = new System.Windows.Forms.Label(); this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components); this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); @@ -81,6 +86,7 @@ private void InitializeComponent() this.panel2.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); this.buttonPanel.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); this.contextMenu.SuspendLayout(); this.SuspendLayout(); // @@ -93,22 +99,24 @@ private void InitializeComponent() this.tableLayoutPanel1.Controls.Add(this.driveListView, 0, 0); this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 1); this.tableLayoutPanel1.Controls.Add(this.buttonPanel, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 0, 2); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0); - this.tableLayoutPanel1.RowCount = 2; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 85.99348F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.00651F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(528, 328); + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 41F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(583, 418); this.tableLayoutPanel1.TabIndex = 0; // // fieldsPanel // this.fieldsPanel.ColumnCount = 3; - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 30.60498F)); - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 69.39502F)); - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 18F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 26.10063F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 73.89937F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 9F)); this.fieldsPanel.Controls.Add(this.nameBox, 1, 0); this.fieldsPanel.Controls.Add(this.label1, 0, 0); this.fieldsPanel.Controls.Add(this.hostBox, 1, 1); @@ -125,8 +133,10 @@ private void InitializeComponent() this.fieldsPanel.Controls.Add(this.label7, 0, 6); this.fieldsPanel.Controls.Add(this.panel2, 1, 5); this.fieldsPanel.Controls.Add(this.authLabel, 0, 5); + this.fieldsPanel.Controls.Add(this.label8, 0, 8); + this.fieldsPanel.Controls.Add(this.textBox1, 1, 8); this.fieldsPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.fieldsPanel.Location = new System.Drawing.Point(229, 5); + this.fieldsPanel.Location = new System.Drawing.Point(253, 5); this.fieldsPanel.Name = "fieldsPanel"; this.fieldsPanel.RowCount = 12; this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F)); @@ -137,16 +147,16 @@ private void InitializeComponent() this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 63F)); this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F)); this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F)); - this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 54F)); - this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F)); + this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 45F)); this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.fieldsPanel.Size = new System.Drawing.Size(296, 274); + this.fieldsPanel.Size = new System.Drawing.Size(327, 339); this.fieldsPanel.TabIndex = 3; // // nameBox // - this.nameBox.Location = new System.Drawing.Point(88, 3); + this.nameBox.Location = new System.Drawing.Point(86, 3); this.nameBox.Name = "nameBox"; this.nameBox.Size = new System.Drawing.Size(186, 20); this.nameBox.TabIndex = 0; @@ -165,7 +175,7 @@ private void InitializeComponent() // // hostBox // - this.hostBox.Location = new System.Drawing.Point(88, 32); + this.hostBox.Location = new System.Drawing.Point(86, 32); this.hostBox.Name = "hostBox"; this.hostBox.Size = new System.Drawing.Size(186, 20); this.hostBox.TabIndex = 1; @@ -184,7 +194,7 @@ private void InitializeComponent() // // portBox // - this.portBox.Location = new System.Drawing.Point(88, 62); + this.portBox.Location = new System.Drawing.Point(86, 62); this.portBox.Name = "portBox"; this.portBox.Size = new System.Drawing.Size(68, 20); this.portBox.TabIndex = 2; @@ -213,7 +223,7 @@ private void InitializeComponent() // // userBox // - this.userBox.Location = new System.Drawing.Point(88, 90); + this.userBox.Location = new System.Drawing.Point(86, 90); this.userBox.Name = "userBox"; this.userBox.Size = new System.Drawing.Size(186, 20); this.userBox.TabIndex = 3; @@ -227,7 +237,7 @@ private void InitializeComponent() "Password", "PrivateKey", "Pageant"}); - this.authCombo.Location = new System.Drawing.Point(88, 119); + this.authCombo.Location = new System.Drawing.Point(86, 119); this.authCombo.Name = "authCombo"; this.authCombo.Size = new System.Drawing.Size(121, 21); this.authCombo.TabIndex = 4; @@ -239,7 +249,7 @@ private void InitializeComponent() this.label5.Dock = System.Windows.Forms.DockStyle.Left; this.label5.Location = new System.Drawing.Point(3, 116); this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(78, 29); + this.label5.Size = new System.Drawing.Size(75, 29); this.label5.TabIndex = 9; this.label5.Text = "Authentication method:"; this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -259,7 +269,7 @@ private void InitializeComponent() // this.panel1.Controls.Add(this.letterBox); this.panel1.Controls.Add(this.mountCheck); - this.panel1.Location = new System.Drawing.Point(88, 238); + this.panel1.Location = new System.Drawing.Point(86, 238); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(182, 23); this.panel1.TabIndex = 12; @@ -271,9 +281,10 @@ private void InitializeComponent() this.letterBox.FormattingEnabled = true; this.letterBox.Location = new System.Drawing.Point(0, 0); this.letterBox.Name = "letterBox"; - this.letterBox.Size = new System.Drawing.Size(43, 21); + this.letterBox.Size = new System.Drawing.Size(68, 21); this.letterBox.Sorted = true; this.letterBox.TabIndex = 9; + this.letterBox.SelectedIndexChanged += new System.EventHandler(this.letterBox_SelectedIndexChanged); // // mountCheck // @@ -292,7 +303,7 @@ private void InitializeComponent() this.directoryBox.Items.AddRange(new object[] { ".", "/"}); - this.directoryBox.Location = new System.Drawing.Point(88, 211); + this.directoryBox.Location = new System.Drawing.Point(86, 211); this.directoryBox.Name = "directoryBox"; this.directoryBox.Size = new System.Drawing.Size(186, 21); this.directoryBox.TabIndex = 8; @@ -314,7 +325,7 @@ private void InitializeComponent() this.panel2.Controls.Add(this.privateKeyBox); this.panel2.Controls.Add(this.privateKeyButton); this.panel2.Controls.Add(this.passwordBox); - this.panel2.Location = new System.Drawing.Point(88, 148); + this.panel2.Location = new System.Drawing.Point(86, 148); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(186, 57); this.panel2.TabIndex = 15; @@ -366,6 +377,25 @@ private void InitializeComponent() this.authLabel.TabIndex = 16; this.authLabel.Text = "______"; // + // label8 + // + this.label8.AutoSize = true; + this.label8.Dock = System.Windows.Forms.DockStyle.Left; + this.label8.Location = new System.Drawing.Point(3, 264); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(63, 29); + this.label8.TabIndex = 17; + this.label8.Text = "Virtual path:"; + this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // textBox1 + // + this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.textBox1.Location = new System.Drawing.Point(86, 267); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(228, 20); + this.textBox1.TabIndex = 18; + // // driveListView // this.driveListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { @@ -378,7 +408,7 @@ private void InitializeComponent() this.driveListView.Location = new System.Drawing.Point(3, 5); this.driveListView.MultiSelect = false; this.driveListView.Name = "driveListView"; - this.driveListView.Size = new System.Drawing.Size(220, 274); + this.driveListView.Size = new System.Drawing.Size(244, 339); this.driveListView.SmallImageList = this.imageList; this.driveListView.TabIndex = 0; this.driveListView.UseCompatibleStateImageBehavior = false; @@ -408,11 +438,11 @@ private void InitializeComponent() this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel2.Controls.Add(this.removeButton, 1, 0); this.tableLayoutPanel2.Controls.Add(this.addButton, 0, 0); - this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 285); + this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 350); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; this.tableLayoutPanel2.RowCount = 1; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); - this.tableLayoutPanel2.Size = new System.Drawing.Size(200, 40); + this.tableLayoutPanel2.Size = new System.Drawing.Size(200, 35); this.tableLayoutPanel2.TabIndex = 2; // // removeButton @@ -422,7 +452,7 @@ private void InitializeComponent() this.removeButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; this.removeButton.Location = new System.Drawing.Point(103, 3); this.removeButton.Name = "removeButton"; - this.removeButton.Size = new System.Drawing.Size(94, 34); + this.removeButton.Size = new System.Drawing.Size(94, 29); this.removeButton.TabIndex = 2; this.removeButton.Text = "Remove"; this.removeButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; @@ -436,7 +466,7 @@ private void InitializeComponent() this.addButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; this.addButton.Location = new System.Drawing.Point(3, 3); this.addButton.Name = "addButton"; - this.addButton.Size = new System.Drawing.Size(94, 34); + this.addButton.Size = new System.Drawing.Size(94, 29); this.addButton.TabIndex = 1; this.addButton.Text = "Add"; this.addButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; @@ -452,12 +482,12 @@ private void InitializeComponent() this.buttonPanel.Controls.Add(this.muButton, 1, 0); this.buttonPanel.Controls.Add(this.saveButton, 1, 0); this.buttonPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.buttonPanel.Location = new System.Drawing.Point(229, 285); + this.buttonPanel.Location = new System.Drawing.Point(253, 350); this.buttonPanel.Name = "buttonPanel"; this.buttonPanel.Padding = new System.Windows.Forms.Padding(0, 0, 15, 0); this.buttonPanel.RowCount = 1; this.buttonPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.buttonPanel.Size = new System.Drawing.Size(296, 40); + this.buttonPanel.Size = new System.Drawing.Size(327, 35); this.buttonPanel.TabIndex = 4; // // muButton @@ -465,9 +495,9 @@ private void InitializeComponent() this.muButton.Dock = System.Windows.Forms.DockStyle.Fill; this.muButton.Image = global::Sshfs.Properties.Resources.mount; this.muButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.muButton.Location = new System.Drawing.Point(187, 3); + this.muButton.Location = new System.Drawing.Point(202, 3); this.muButton.Name = "muButton"; - this.muButton.Size = new System.Drawing.Size(91, 34); + this.muButton.Size = new System.Drawing.Size(107, 29); this.muButton.TabIndex = 4; this.muButton.Text = "Mount"; this.muButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; @@ -481,13 +511,53 @@ private void InitializeComponent() this.saveButton.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; this.saveButton.Location = new System.Drawing.Point(90, 3); this.saveButton.Name = "saveButton"; - this.saveButton.Size = new System.Drawing.Size(91, 34); + this.saveButton.Size = new System.Drawing.Size(106, 29); this.saveButton.TabIndex = 3; this.saveButton.Text = "Save"; this.saveButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; this.saveButton.UseVisualStyleBackColor = true; this.saveButton.Click += new System.EventHandler(this.saveButton_Click); // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.ColumnCount = 2; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33F)); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67F)); + this.tableLayoutPanel3.Controls.Add(this.virtualDriveCombo, 1, 0); + this.tableLayoutPanel3.Controls.Add(this.label9, 0, 0); + this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 391); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 1; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Size = new System.Drawing.Size(244, 24); + this.tableLayoutPanel3.TabIndex = 6; + // + // virtualDriveCombo + // + this.virtualDriveCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.virtualDriveCombo.DropDownWidth = 43; + this.virtualDriveCombo.FormattingEnabled = true; + this.virtualDriveCombo.Items.AddRange(new object[] { + "off"}); + this.virtualDriveCombo.Location = new System.Drawing.Point(83, 3); + this.virtualDriveCombo.Name = "virtualDriveCombo"; + this.virtualDriveCombo.Size = new System.Drawing.Size(63, 21); + this.virtualDriveCombo.Sorted = true; + this.virtualDriveCombo.TabIndex = 5; + this.virtualDriveCombo.SelectedIndexChanged += new System.EventHandler(this.virtualDriveCombo_SelectedIndexChanged); + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Dock = System.Windows.Forms.DockStyle.Left; + this.label9.Location = new System.Drawing.Point(3, 0); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(65, 24); + this.label9.TabIndex = 6; + this.label9.Text = "Virtual drive:"; + this.label9.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // // openFileDialog // this.openFileDialog.FileName = "id_rsa"; @@ -591,7 +661,7 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(528, 328); + this.ClientSize = new System.Drawing.Size(583, 418); this.Controls.Add(this.tableLayoutPanel1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); @@ -612,6 +682,8 @@ private void InitializeComponent() this.panel2.PerformLayout(); this.tableLayoutPanel2.ResumeLayout(false); this.buttonPanel.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.tableLayoutPanel3.PerformLayout(); this.contextMenu.ResumeLayout(false); this.ResumeLayout(false); @@ -663,6 +735,11 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem mountMenuItem; private System.Windows.Forms.ToolStripMenuItem unmountMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.ComboBox virtualDriveCombo; + private System.Windows.Forms.Label label9; diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index 5ebe881..d36a070 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -27,7 +27,10 @@ public partial class MainForm : Form private readonly List _drives = new List(); private readonly Regex _regex = new Regex(@"^New Drive\s\d{1,2}$", RegexOptions.Compiled); private readonly Queue _suspendedDrives = new Queue(); + private VirtualDrive virtualDrive; + //private char virtualDriveLetter; + private bool _balloonTipVisible; private int _lastindex = -1; @@ -35,6 +38,10 @@ public partial class MainForm : Form private bool _suspend; private bool _dirty; + private bool _updateLockvirtualDriveBox = false; + private bool _updateLockLetterBox = false; + + public MainForm() { InitializeComponent(); @@ -65,14 +72,13 @@ protected override void OnLoad(EventArgs e) startupMenuItem.Checked = Utilities.IsAppRegistredForStarup(); - // _drives.Presist("config.xml",true); - - + // _drives.Presist("config.xml",true); virtualDrive = new VirtualDrive { Letter = 'Z' }; + updateVirtualDriveCombo(); virtualDrive.Mount(); @@ -86,7 +92,8 @@ protected override void OnLoad(EventArgs e) new ListViewItem(_drives[i].Name, 0) {Tag = _drives[i]}) as ListViewItem); _drives[i].StatusChanged += drive_StatusChanged; if (_drives[i].Name.StartsWith("New Drive")) _namecount++; - _drives[i]._virtualDrive = virtualDrive; + + virtualDrive.AddSubFS(_drives[i].Name, _drives[i]); } @@ -114,6 +121,66 @@ protected override void OnLoad(EventArgs e) base.OnLoad(e); } + private void updateVirtualDriveCombo() + { + if (_updateLockvirtualDriveBox) + return; + this.virtualDriveCombo.BeginUpdate(); + + this.virtualDriveCombo.Items.Clear(); + + this.virtualDriveCombo.Items.Add(" Off"); + this.virtualDriveCombo.Items.AddRange( + Utilities.GetAvailableDrives() + .Except(_drives.Select(d => d.Letter)) + .Except(new char[] { virtualDrive.Letter }) + .Select(l => String.Format("{0} :", l)) + .ToArray() + ); + if (virtualDrive.Letter!=' ') + this.virtualDriveCombo.Items.Add(String.Format("{0} :", virtualDrive.Letter)); + + + this.virtualDriveCombo.SelectedIndex = this.virtualDriveCombo.FindString(virtualDrive.Letter.ToString()); + + this.virtualDriveCombo.EndUpdate(); + } + + private void updateLetterBoxCombo(SftpDrive drive) + { + if (_updateLockLetterBox) + return; + if (drive == null) + { + if (driveListView.SelectedItems.Count == 0) + return; + drive = driveListView.SelectedItems[0].Tag as SftpDrive; + if (drive == null) + return; + } + + letterBox.BeginUpdate(); + + letterBox.Items.Clear(); + + letterBox.Items.Add(" None"); + + letterBox.Items.AddRange( + Utilities.GetAvailableDrives() + .Except(_drives.Select(d => d.Letter)) + .Except(new char[] {virtualDrive.Letter}) + .Select(l => String.Format("{0} :", l)) + .ToArray()); + + + if (drive.Letter!=' ') + letterBox.Items.Add(String.Format("{0} :", drive.Letter)); + + letterBox.SelectedIndex = letterBox.FindString(drive.Letter.ToString()); + + letterBox.EndUpdate(); + } + private void startupMenuItem_CheckedChanged(object sender, EventArgs e) { @@ -184,8 +251,7 @@ private void addButton_Click(object sender, EventArgs e) Name = String.Format("New Drive {0}", ++_namecount), Port = 22, Root = ".", - Letter = letter, - _virtualDrive = virtualDrive + Letter = letter }; drive.StatusChanged += drive_StatusChanged; _drives.Add(drive); @@ -289,19 +355,8 @@ private void listView_ItemSelectionChanged(object sender, ListViewItemSelectionC case ConnectionType.PrivateKey: authCombo.SelectedIndex = 1; break; default: authCombo.SelectedIndex=0; break; } - letterBox.BeginUpdate(); - - letterBox.Items.Clear(); - - letterBox.Items.AddRange( - Utilities.GetAvailableDrives().Except(_drives.Select(d => d.Letter)).Select( - l => String.Format("{0} :", l)).ToArray()); - letterBox.Items.Add(String.Format("{0} :", drive.Letter)); - - letterBox.SelectedIndex = letterBox.FindString(drive.Letter.ToString()); - - letterBox.EndUpdate(); + updateLetterBoxCombo(drive); passwordBox.Text = drive.Password; directoryBox.Text = drive.Root; @@ -615,5 +670,42 @@ private void mitem_Click(object sender, EventArgs e) muButton.Enabled = false; MountDrive(drive); } + + private void virtualDriveCombo_SelectedIndexChanged(object sender, EventArgs e) + { + if (_updateLockvirtualDriveBox) + return; + _updateLockvirtualDriveBox = true; ; + + if (virtualDrive.Letter != virtualDriveCombo.Text[0]) + { + virtualDrive.Letter = virtualDriveCombo.Text[0]; + + //TODO: this shoud be in thread - blocks gui: + if (virtualDrive != null && (virtualDrive.Status == DriveStatus.Mounted)) + virtualDrive.Unmount(); + + if (virtualDrive != null && virtualDrive.Letter != ' ') + { + virtualDrive.Letter = virtualDrive.Letter; + virtualDrive.Mount(); + } + } + + updateLetterBoxCombo(null); + + _updateLockvirtualDriveBox = false; + } + + private void letterBox_SelectedIndexChanged(object sender, EventArgs e) + { + _updateLockLetterBox = true; + + SftpDrive drive = driveListView.SelectedItems[0].Tag as SftpDrive; + drive.Letter = letterBox.Text[0]; + + this.updateVirtualDriveCombo(); + _updateLockLetterBox = false; + } } } \ No newline at end of file diff --git a/Sshfs/Sshfs/MainForm.resx b/Sshfs/Sshfs/MainForm.resx index 875b392..8afbe8c 100644 --- a/Sshfs/Sshfs/MainForm.resx +++ b/Sshfs/Sshfs/MainForm.resx @@ -124,198 +124,198 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 - ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACm - LAAAAk1TRnQBSQFMAgEBBAEAARQBAAEUAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACi + LAAAAk1TRnQBSQFMAgEBBAEAASQBAAEkAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ - AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABtgH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ - AoEBuAH/Al8BcAHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK + AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABtAH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ + AoEBuAH/Al8BbgHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK AwgBCwMHAQoDBwEJAwUBBwMBAQIgAAMPARQDDwEUAxIBGAMeASsDJgE5AygBPAMoATwDJgE4AyIBMQMY - ASIDEAEWAxABFQMRARcDSAGDAisBpAH/AisBpAH/AisBpAH/AisBpAH/AisBpAH/AisBpAH/AisBpAH/ + ASIDEAEWAxABFQMRARcDSAGDAikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/ A0YBfggAAw8BFAMPARQDEgEYAx4BKwMmATkDKAE8AygBPAMmATgDIgExAxgBIgMQARYDEAEVAxEBFwMH AQkDAgEDAwsBDwMWAR4DFgEfAwsBDwMBAQIQAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGz - A1YBtANVAbUDVQG1A1UBtQJbAV0ByANqAfkCgQHTAf8CgQHTAf8CgQHTAf8CgQHTAf8CgQHTAf8CgQHT - Af8CgQHTAf8CgQHUAf8CZAFyAfEEAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGzA1YBtANV - AbUDVQG1A1UBtQNVAbUDVgG0Az0BaAMOARMDAwEEHAABgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ - AYEBhAGCAf8BgQGEAYIB/wIrAaQB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIr - AcwB/wIrAaQB/wgAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ - AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DGQEj - AxQBGwM1AVYDYQHaAXoCewH6A10ByAMeASsQAAGoAasBqgH/AeQC5QH/AeEB5AHjAf8B3wLhAf8B3wHh - AeAB/wHfAuEB/wHfAuEB/wHfAuEB/wHfAuEB/wHhAeIB4QH/Ad4B4QHfAf8B4QHiAeEB/wGYAZoBzwH/ - AoEBzAH/AmkBzAH/AoEB0QH/AoEB0AH/AmkBzAH/AmkBzAH/AoEB1AH/Am0BzQH/AoEB0wH/AoEBuAH/ - BAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHgAf8B3wLhAf8B3wLhAf8B3wLhAf8B3wLh - Af8B4QHiAeEB/wHeAeEB3wH/AeEB4gHhAf8B4wHlAeQB/wHhAeMB4gH/A38B/gMYASIDBgEIHAABgQGE - AYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8CKwGk - Af8CKwHMCf8CKwHMCf8CKwHMAf8CKwGkAf8IAAGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/ - A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGEAYIB/wOBAf8DgQH/A4EB/wGcAp0B/wO2Af8BrAKtAf8DWgHA - EAADQAFuA2UB6gG6ArsB/wG2ArcB/wGtAq4B/wGqAasBqgH/AaoCqwH/AaoCqwH/AaoCqwH/A7gB/wGu - Aq8B/wHIAskB/wKBAaEB/wKBAcwB/wJpAcwB/wKBAd8B/wLBAfMB/wKBAdYB/wKBAd8B/wLNAfUB/wKB - AdMB/wKBAdMB/wKBAbgB/wQAA0ABbgNlAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGq - AqsB/wGqAqsB/wO4Af8BrgKvAf8ByALJAf8BnQGfAZ4B/wNlAe8DSQGIAyABLgMEAQUcAAGBAYQBggH/ - A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/A8UB/wPoAf8BgQGEAYIB/wIrAaQB/wIr - AcwV/wIrAcwB/wIrAaQB/wgAAYEBhAGCAf8D8wH/A/AB/wPRAf8D7AH/A+sB/wPrAf8D6wH/A8gB/wPF - Af8DxQH/A+gB/wGBAYQBggH/A5AB/wOzAf8DgQH/A7YB/wPrAf8D1QH/A2oB+RAAAwgBCwM4AV0DfwH+ - AZYBmgGYAf8BlQGZAZgB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGa - AZkB/wGYAZwBmwH/AoEBlAH/AoEBzAH/AmkBzAH/AnkBzwH/AogB5wH/AtoB+AH/As0B9QH/AoEB3wH/ - AmkBzAH/AoEB0wH/AoEBuAH/BAADCAELAzgBXQN/Af4BlgGaAZgB/wGVAZkBmAH/AZIBlQGUAf8BkgGV - AZQB/wGSAZUBlAH/AZIBlQGUAf8BkQGUAZMB/wGXAZoBmQH/AZgBnAGbAf8DYgHVAz8BbQNMAZIDRwGA - AwABARwAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9AisBpAH/AisBzAH/AisBzA3/ - AisBzAH/AisBzAH/AisBpAH/CAADTwGXAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNFAX0DgQH/ - A4EB/wOBAf8BowKkAf8D1QH/A9cB/wNZAb8UAAMBAQIDBAEFAZUBmQGYAf8BywHPAc4B/wHbAd0B3AH/ - AdgB2gHZAf8BywHPAc4B/wHAAcQBwwH/AbcBuwG6Af8DXAHNAYEBhAGCAf8CVAFWAasCgQHMAf8CaQHM - Af8CaQHMAf8CgQHfBf8C2gH4Af8CgQHWAf8CaQHMAf8CgQHTAf8CgQG4Af8IAAMBAQIDBAEFAZUBmQGY - Af8BywHPAc4B/wHbAd0B3AH/AdgB2gHZAf8BywHPAc4B/wHAAcQBwwH/AbcBuwG6Af8DXAHNAYEBhAGC - Af8DSgGJA1sBxANeAc4DGQEjIAABgQGEAYIB/wPWAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/ - A+0B/wPtAf8D1AH/AYEBhAGCAf8CKwGkAf8CKwHMFf8CKwHMAf8CKwGkAf8IAAGBAYQBggH/A9YB/wH3 - AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHr - Af8B9wHxAesB/wPUAf8BgQGEAYIB/wQAAwUBBwMmATgDgQH/A4EB/wOBAf8DGgElEAADHwEsA2IB1QGh - AaYBpAH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaEBpQGjAf8BpAGoAacB/wGlAaoBqAH/AaUBqgGo - Af8BpQGpAacB/wGlAakBpwH/AoEBpgH/AoEBzAH/AmkBzAH/AoEB2wH/As0B9QH/AoEB3wH/AocB5wH/ - AsEB8wH/AnsBzwH/AoEB0wH/AoEBuAH/BAADHwEsA2IB1QGhAaYBpAH/AaEBpQGjAf8BoQGlAaMB/wGh - AaUBowH/AaEBpQGjAf8BpAGoAacB/wGlAaoBqAH/AaUBqgGoAf8BpQGpAacB/wGlAakBpwH/AaABpQGj - Af8DWwHDAwcBCQMHAQoDBwEKAwcBCgMIAQsDBwEKAwcBCQMEAQYDAQECBAABgQGEAYIB/wPzAf8BgQFz - AWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/ - AYEBcwFhAf8D7QH/AYEBhAGCAf8CKwGkAf8CKwHMCf8CKwHMCf8CKwHMAf8CKwGkAf8IAAGBAYQBggH/ - AfoB9gHyAf8BpAGBAWcB/wGoAYEBbQH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFtAf8BqAGB - AW0B/wGoAYEBbQH/AaQBgQFnAf8B9wHxAesB/wGBAYQBggH/AwsBDwMMARADDAEQA4EB/wPTAf8DgQH/ - FAADNQFVAb8BwwHBAf8B1wHcAdkB/wHiAeYB5AH/AeIB5gHkAf8B4AHkAeIB/wHGAcsByAH/AcUBygHI - Af8B3QHiAd8B/wHfAeQB4QH/Ad8B4wHhAf8B3wHjAeEB/wGPAZIBxgH/AoEBzAH/AmkBzAH/AoEB1wH/ - AoEB2wH/AmkBzAH/AnkBzwH/AoEB4QH/An0B0AH/AoEB0wH/AoEBuAH/BAADNQFVAb8BwwHBAf8B1wHc - AdkB/wHiAeYB5AH/AeIB5gHkAf8B4AHkAeIB/wHGAcsByAH/AcUBygHIAf8B3QHiAd8B/wHfAeQB4QH/ - Ad8B4wHhAf8B3wHjAeEB/wHWAdoB2AH/AZwBoQGfAf8DVgG0A1YBtANVAbUDVQG1A1UBtQNVAbUDVgGz - AzEBTgMLAQ8EAAGBAYQBggH/A+4B/wGBAXMBYQH/AZMBgQF3Af8BkwGBAXcB/wGUAYEBdwH/AZMBgQF3 - Af8BkwGBAXcB/wGTAYEBeAH/AZQBgQF3Af8BgQFzAWEB/wPuAf8BgQGEAYIB/wIrAaQB/wIrAcwB/wIr - AcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAcwB/wIrAaQB/wgAAYEBhAGCAf8B+AHyAewB/wGo - AYEBbQH/AboBgQFxAf8BugGBAXEB/wG7AYEBcQH/AboBgQFxAf8BugGBAXEB/wG6AYEBcgH/AbsBgQFx - Af8BqAGBAW0B/wH4AfIB7AH/AYEBhAGCAf8DOAFeAzgBXgM2AVgDgQH/AccCyAH/A4EB/wMDAQQDBQEH - DAADNQFVAcIBxQHEAf8DUgGjA4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8BnQKBAf8BkwGW - AcoB/wKBAcwB/wJuAc0B/wJpAcwB/wJpAcwB/wJpAcwB/wJpAcwB/wJpAcwB/wJpAcwB/wKBAdMB/wJA - AbYB/QQAAzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/ - AdsB4AHeAf8BngGjAaEB/wHfAuEB/wHgAeIB4QH/Ad8B4gHgAf8B3gHhAd8B/wHiAeMB4gH/AeMB5QHj - Af8B4QHjAeIB/wNXAbgDFQEdBAABgQGEAYIB/wPuAf8BgQFzAWEB/wGrAYoBgQH/AZ0CgQH/AY8BgQF0 - Af8BkAGBAXQB/wGQAYEBdAH/AZABgQF0Af8BjwGBAXQB/wGBAXMBYQH/A+4B/wGBAYQBggH/AWsBbQGS - Af8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8CKwGkAf8DRgF+CAABgQGEAYIB/wH4 - AfIB7AH/AagBgQFtAf8B0AGeAYEB/wHEAYsBgQH/AbYBgQFvAf8BtwGBAW8B/wG3AYEBbwH/AbcBgQFv - Af8BtgGBAW8B/wGoAYEBbQH/AfgB8gHsAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AwEBAgwAAzUBVQO9Af0DgQH/AaACgQH/AZ8CgQH/ - AZoCgQH/AZgCgQH/AZcCgQH/AZgCgQH/AZgCgQH/AZgCgQH/A4EB/wG2AbkB1gH/AoEBuwH/AoEBzAH/ - AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/A18B+wNWAbMEAAM1AVUDvQH9A4EB/wGg - AoEB/wGfAoEB/wGaAoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8B3wHiAeEB/wGe - AaIBoAH/AaoCqwH/AbECsgH/Aa4CrwH/AbICswH/AboCuwH/AZwBngGdAf8DZgHnA0ABbgMWAR8EAAGB - AYQBggH/A+4B/wGBAXMBYQH/AakBhwGBAf8BqQGHAYEB/wGoAYcBgQH/AZoCgQH/AZMBgQGAAf8BiwGB - AXEB/wGMAYEBcQH/AYEBcwFhAf8D7wH/AYEBhAGCAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGE - AYIB/wMBAQIMAAGBAYQBggH/AfgB8gHsAf8BqAGBAW0B/wHOAZsBgQH/Ac4BmwGBAf8BzgGbAYEB/wHC - AYYBgQH/AboBgQF4Af8BsgGBAWwB/wG0AYEBbAH/AagBgQFtAf8B+AHzAe0B/wGBAYQBggH/A9kB/wPZ - Af8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8DAQECDAADNQFVA18B+wOBAf8BrAGLAYEB/wGrAYkBgQH/ - AaUCgQH/AZkCgQH/AZUCgQH/AZMCgQH/AZMCgQH/AZQCgQH/A4EB/wHaAt4B/wGCAYYBpQH/AoEBkgH/ - AoEBkQH/AoEBkQH/AoEBkQH/AoEBkQH/AlQBVgGrA0UBfAJXAVgBugM1AVYEAAM1AVUDXwH7A4EB/wGs - AYsBgQH/AasBiQGBAf8BpQKBAf8BmQKBAf8BlQKBAf8BkwKBAf8BkwKBAf8BlAKBAf8DgQH/Ad0B4QHf - Af8BnAGgAZ8B/wGSAZUBlAH/AZIBlQGUAf8BkAGTAZIB/wGbAZ4BnQH/AZQBlwGWAf8DVgGrAzsBZANS - AaEDKwFCBAABgQGEAYIB/wPwAf8BgQFzAWEB/wGmAYMBgQH/AaUBhAGBAf8BpgGEAYEB/wGlAYQBgQH/ - AaYBhAGBAf8BpgGEAYEB/wGQAYEBfAH/AYEBcwFhAf8D8AH/AYEBhAGCAf8D6wH/A+sB/wPIAf8DxQH/ - A8UB/wPoAf8BgQGEAYIB/xAAAYEBhAGCAf8B+AH0Ae4B/wGoAYEBbQH/AcwBlwGBAf8BywGYAYEB/wHM - AZgBgQH/AcsBmAGBAf8BzAGYAYEB/wHMAZgBgQH/AbcBgQF2Af8BqAGBAW0B/wH4AfQB7gH/AYEBhAGC - Af8D6wH/A+sB/wPIAf8DxQH/A8UB/wPoAf8BgQGEAYIB/xAAAzUBVQN9AfgDgQH/AasBiQGBAf8BqQGH - AYEB/wGpAYcBgQH/AagBhQGBAf8BmgKBAf8BkAKBAf8BkAKBAf8BkAKBAf8DgQH/Ab0BwQHAAf8BlwGb - AZkB/wHIAcwBywH/Ab4BwgHBAf8BtQG5AbgB/wNWAbMDYgHVA00BkQNfAdkDUAGbAw0BEQQAAzUBVQN9 - AfgDgQH/AasBiQGBAf8BqQGHAYEB/wGpAYcBgQH/AagBhQGBAf8BmgKBAf8BkAKBAf8BkAKBAf8BkAKB - Af8DgQH/Ab0BwQHAAf8BlwGbAZkB/wHIAcwBywH/Ab4BwgHBAf8BtQG5AbgB/wNWAbMDYgHVA00BkQNf - AdkDUAGbAw0BEQQAAYEBhAGCAf8D8wH/AYEBcwFhAf8BowKBAf8BowKBAf8BowKBAf8BowKBAf8BowKB - Af8BogKBAf8BowKBAf8BgQFzAWEB/wPwAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wNFAX0QAAGBAYQBggH/AfoB9gHyAf8BqAGBAW0B/wHJAZUBgQH/ - AckBlAGBAf8ByQGUAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AagBgQFtAf8B+AH0 - Ae4B/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ - A0UBfRAAAzUBVQNiAfYDgQH/AaoBiAGBAf8BqAGHAYEB/wGnAYYBgQH/AacBhAGBAf8BpgGCAYEB/wGZ - AoEB/wGPAoEB/wGMAoEB/wOBAf8B0wHWAdQB/wGZAZ4BnAH/AaUBqgGoAf8BpQGqAagB/wGlAaoBpwH/ - AaUBqQGnAf8BpQGpAacB/wGcAaABngH/A1QBpgwAAzUBVQNiAfYDgQH/AaoBiAGBAf8BqAGHAYEB/wGn - AYYBgQH/AacBhAGBAf8BpgGCAYEB/wGZAoEB/wGPAoEB/wGMAoEB/wOBAf8B0wHWAdQB/wGZAZ4BnAH/ - AaUBqgGoAf8BpQGqAagB/wGlAaoBpwH/AaUBqQGnAf8BpQGpAacB/wGcAaABngH/A1QBpgwAAYEBhAGC - Af8D7QH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFh - Af8BgQFzAWEB/wGBAXMBYQH/A/AB/wGBAYQBggH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D1AH/AYEBhAGC - Af8QAAGBAYQBggH/AfcB8QHrAf8BpAGBAWcB/wGoAYEBbQH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/ - AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AaQBgQFnAf8B+AH0Ae4B/wGBAYQBggH/AfcB8QHrAf8B9wHx - AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wPUAf8BgQGEAYIB/xAAAzUBVQNmAfQDgQH/AagBhgGB - Af8BpwGFAYEB/wGmAYQBgQH/AaUBggGBAf8BpAKBAf8BowKBAf8BnQKBAf8BlQKBAf8DgQH/AdMB1gHV - Af8BmQGeAZwB/wHFAcoByAH/Ad4B4gHgAf8B3wHjAeEB/wHfAeMB4QH/AdwB4AHeAf8B1gHZAdgB/wGB - AYQBggH/DAADNQFVA2YB9AOBAf8BqAGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGj - AoEB/wGdAoEB/wGVAoEB/wOBAf8B0wHWAdUB/wGZAZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/ - Ad8B4wHhAf8B3AHgAd4B/wHWAdkB2AH/AYEBhAGCAf8MAAGBAYQBggH/AccCyAH/A+0B/wPtAf8D7QH/ - A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/AdkC2gH/AYEBhAGCAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFh - Af8BgQFzAWEB/wGBAXMBYQH/A+0B/wGBAYQBggH/EAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/AfcB8QHr - Af8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wHZ - AtoB/wGBAYQBggH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFtAf8BpAGBAWcB/wH3AfEB6wH/ - AYEBhAGCAf8QAAM1AVUDagHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh - AoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/A4EB/wNZAcIB3AHf - Ad4B/wGBAYQBggH/DAADNQFVA2oB8AOBAf8BpwGFAYEB/wGlAYQBgQH/AaQBgwGBAf8BpAKBAf8BogKB - Af8BoQKBAf8BoAKBAf8BnwKBAf8DgQH/AcABwwHBAf8BlgGZAZcB/wOBAf8DgQH/A4EB/wOBAf8DWQHC - AdwB3wHeAf8BgQGEAYIB/wwAA1QBrAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BhwKBAf8BkwGB - AXcB/wGTAYEBdwH/AZMBgQF4Af8BlAGBAXcB/wGBAXMBYQH/A+4B/wGBAYQBggH/EAADVAGsAYEBhAGC + A1YBtANVAbUDVQG1A1UBtQNbAcgDagH5AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/ + AoEB0wH/AoEB1AH/AmQBbwHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 + A1UBtQNVAbUDVQG1A1YBtAM9AWgDDgETAwMBBBwAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wG6AYEBcQH/AboBgQFxAf8BugGBAXIB/wG7AYEBcQH/ - AagBgQFtAf8B+AHyAewB/wGBAYQBggH/EAADNQFVA2YB9ANUAawBlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGbAZ0BmwH/AZcCgQH/AZgCgQH/ - AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/wwAAzUBVQNmAfQDVAGsAZYCgQH/AZYCgQH/ - AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZ0CgQH/AdQB1gHUAf8BmwGdAZsB/wGX - AoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGbAoEB/wHcAd8B3QH/AYEBhAGCAf8oAAGBAYQBggH/A+4B/wGB - AXMBYQH/AasBigGBAf8BnQKBAf8BjwGBAXQB/wGQAYEBdAH/AZABgQF0Af8BkAGBAXQB/wGPAYEBdAH/ - AYEBcwFhAf8D7gH/AYEBhAGCAf8sAAGBAYQBggH/AfgB8gHsAf8BqAGBAW0B/wHQAZ4BgQH/AcQBiwGB - Af8BtgGBAW8B/wG3AYEBbwH/AbcBgQFvAf8BtwGBAW8B/wG2AYEBbwH/AagBgQFtAf8B+AHyAewB/wGB - AYQBggH/EAADIQEwA18B2QGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe - AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGT - AoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO9Af0BgQGEAYIB/wwAAyEBMANfAdkBmgGeAZ0B/wGaAZ4BnQH/ - AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe - AZ0B/wGbAZ8BnQH/AYkBhwGBAf8BlAKBAf8BkwKBAf8BlAKBAf8BlAKBAf8BmwKBAf8DvQH9AYEBhAGC - Af8oAAGBAYQBggH/A+4B/wGBAXMBYQH/AakBhwGBAf8BqQGHAYEB/wGoAYcBgQH/AZoCgQH/AZMBgQGA - Af8BiwGBAXEB/wGMAYEBcQH/AYEBcwFhAf8D7wH/AYEBhAGCAf8sAAGBAYQBggH/AfgB8gHsAf8BqAGB - AW0B/wHOAZsBgQH/Ac4BmwGBAf8BzgGbAYEB/wHCAYYBgQH/AboBgQF4Af8BsgGBAWwB/wG0AYEBbAH/ - AagBgQFtAf8B+AHzAe0B/wGBAYQBggH/LAADUwGpA28B9QGJAoEB/wGqAYgBgQH/AakBhwGBAf8BqQGG - AYEB/wGoAYUBgQH/AZMCgQH/AZACgQH/AZACgQH/AZACgQH/AY4CgQH/A30B/AGBAYQBggH/KAADUwGp - A28B9QGJAoEB/wGqAYgBgQH/AakBhwGBAf8BqQGGAYEB/wGoAYUBgQH/AZMCgQH/AZACgQH/AZACgQH/ - AZACgQH/AY4CgQH/A30B/AGBAYQBggH/KAABgQGEAYIB/wPwAf8BgQFzAWEB/wGmAYMBgQH/AaUBhAGB - Af8BpgGEAYEB/wGlAYQBgQH/AaYBhAGBAf8BpgGEAYEB/wGQAYEBfAH/AYEBcwFhAf8D8AH/AYEBhAGC - Af8sAAGBAYQBggH/AfgB9AHuAf8BqAGBAW0B/wHMAZcBgQH/AcsBmAGBAf8BzAGYAYEB/wHLAZgBgQH/ - AcwBmAGBAf8BzAGYAYEB/wG3AYEBdgH/AagBgQFtAf8B+AH0Ae4B/wGBAYQBggH/LAADUwGpA3IB8QGJ - AoEB/wGpAYcBgQH/AagBhwGBAf8BpwGGAYEB/wGmAYQBgQH/AaYBggGBAf8BlgKBAf8BjgKBAf8BiwKB - Af8BlwKBAf8DagH5AYEBhAGCAf8oAANTAakDcgHxAYkCgQH/AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/ - AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGOAoEB/wGLAoEB/wGXAoEB/wNqAfkBgQGEAYIB/ygAAYEBhAGC - Af8D8wH/AYEBcwFhAf8BowKBAf8BowKBAf8BowKBAf8BowKBAf8BowKBAf8BogKBAf8BowKBAf8BgQFz - AWEB/wPwAf8BgQGEAYIB/ywAAYEBhAGCAf8B+gH2AfIB/wGoAYEBbQH/AckBlQGBAf8ByQGUAYEB/wHJ - AZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAW0B/wH4AfQB7gH/AYEBhAGC - Af8sAANTAakDbAHuAYgCgQH/AacBhgGBAf8BpwGFAYEB/wGmAYQBgQH/AaQBggGBAf8BpAKBAf8BogKB - Af8BmwKBAf8BkgKBAf8BlwKBAf8DbQH3AYEBhAGCAf8oAANTAakDbAHuAYgCgQH/AacBhgGBAf8BpwGF - AYEB/wGmAYQBgQH/AaQBggGBAf8BpAKBAf8BogKBAf8BmwKBAf8BkgKBAf8BlwKBAf8DbQH3AYEBhAGC - Af8oAAGBAYQBggH/A+0B/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFz - AWEB/wGBAXMBYQH/AYEBcwFhAf8BgQFzAWEB/wPwAf8BgQGEAYIB/ywAAYEBhAGCAf8B9wHxAesB/wGk - AYEBZwH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFtAf8BqAGBAW0B/wGoAYEBbQH/AagBgQFt - Af8BpAGBAWcB/wH4AfQB7gH/AYEBhAGCAf8sAANTAakDZwHpAYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGk - AYIBgQH/AaMCgQH/AaICgQH/AaECgQH/AaACgQH/AZ4CgQH/AZACgQH/A2oB+QGBAYQBggH/KAADUwGp - A2cB6QGIAoEB/wGmAYQBgQH/AaUBhAGBAf8BpAGCAYEB/wGjAoEB/wGiAoEB/wGhAoEB/wGgAoEB/wGe - AoEB/wGQAoEB/wNqAfkBgQGEAYIB/ygAAYEBhAGCAf8BxwLIAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt - Af8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/ywAAYEBhAGCAf8BxwLIAf8B9wHxAesB/wH3AfEB6wH/ - AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B2QLa - Af8BgQGEAYIB/ywAA1MBqQNsAe4DXQHIAZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/ - AZYCgQH/AZYCgQH/A2AB1gNyAfEBgQGEAYIB/ygAA1MBqQNsAe4DXQHIAZYCgQH/AZYCgQH/AZYCgQH/ - AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/A2AB1gNyAfEBgQGEAYIB/ygAA1QBrAGBAYQBggH/ - AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8DTwGXLAADVAGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNP - AZcsAAM4AV4DfwH+AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ - AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8DXwH7A10ByCgAAzgBXgN/Af4BmgGeAZ0B/wGa - AZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGd - Af8BmgGeAZ0B/wNfAfsDXQHI/wCJAAFCAU0BPgcAAT4DAAEoAwABYAMAATADAAEBAQABAQUAAUABAhYA - A///ACIABv8BgAIAAYABAAF/AYABAAEBAYABAAEHAYACAAGAAQABPwGAAQABAQGAAQABBwGAAgABgAEA - AT8BgAEAAQEBgAEAAQcBgAIAAYABAAE/AYABAAEBAYABAAEHAYACAAGAAQABPwGAAQABAQGAAQABBwHA - AgABwAEAAX8BgAEAAQEBgAECAQcBgAIAAYACAAGAAQABAQGAAQABDwGAAgABgAIAAYABAAEBAYABAAED - AYACAAGAAgABgAEAAQEBgAEAAQMBgAIAAYACAAGAAQABAwGAAQABAwGAAgABgAIAAYABAAEHAYABAAEH - AYACAAGAAgABgAEAAQcBgAEAAQcBgAEAAQMBgAEAAQMBgAEAAQcBgAEAAQcBgAEAAQMBgAEAAQMBgAEA - AQcBgAEAAQcBgAEAAQMBgAEAAQMBgAEAAQcBgAEAAQcBgAEAAQMBgAEAAQMB/wEAAQcB/wEAAQcBgAEA - AQMBgAEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEA - AQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEA - AQMB/wEAAQMB/wEAAQcB/wEAAQcB/wEAAQMB/wEAAQMM/ws= + AYQBggH/AYEBhAGCAf8CKQGkAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHM + Af8CKQGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AxkBIwMU + ARsDNQFWA2EB2gF4AnkB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg + Af8B3wLhAf8B3wLhAf8B3wLhAf8B3wLhAf8B4QHiAeEB/wHeAeEB3wH/AeEB4gHhAf8BmAGaAc8B/wKB + AcwB/wJnAcwB/wKBAdEB/wKBAdAB/wJnAcwB/wJnAcwB/wKBAdQB/wJrAc0B/wKBAdMB/wKBAbgB/wQA + AagBqwGqAf8B5ALlAf8B4QHkAeMB/wHfAuEB/wHfAeEB4AH/Ad8C4QH/Ad8C4QH/Ad8C4QH/Ad8C4QH/ + AeEB4gHhAf8B3gHhAd8B/wHhAeIB4QH/AeMB5QHkAf8B4QHjAeIB/wN/Af4DGAEiAwYBCBwAAYEBhAGC + Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AikBpAH/ + AikBzAn/AikBzAn/AikBzAH/AikBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ + Af8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8DgQH/A4EB/wOBAf8BnAKdAf8DtgH/AawCrQH/A1oBwBAA + A0ABbgNjAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv + Af8ByALJAf8CgQGhAf8CgQHMAf8CZwHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT + Af8CgQHTAf8CgQG4Af8EAANAAW4DYwHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr + Af8BqgKrAf8DuAH/Aa4CrwH/AcgCyQH/AZ0BnwGeAf8DYwHvA0kBiAMgAS4DBAEFHAABgQGEAYIB/wPz + Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CKQGkAf8CKQHM + Ff8CKQHMAf8CKQGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ + A8UB/wPoAf8BgQGEAYIB/wOQAf8DswH/A4EB/wO2Af8D6wH/A9UB/wNqAfkQAAMIAQsDOAFdA38B/gGW + AZoBmAH/AZUBmQGYAf8BkgGVAZQB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGRAZQBkwH/AZcBmgGZ + Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJnAcwB/wJ3Ac8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJn + AcwB/wKBAdMB/wKBAbgB/wQAAwgBCwM4AV0DfwH+AZYBmgGYAf8BlQGZAZgB/wGSAZUBlAH/AZIBlQGU + Af8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGaAZkB/wGYAZwBmwH/A18B1QM/AW0DTAGSA0cBgAMA + AQEcAANPAZcBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQIpAaQB/wIpAcwB/wIpAcwN/wIp + AcwB/wIpAcwB/wIpAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9A4EB/wOB + Af8DgQH/AaMCpAH/A9UB/wPXAf8DWQG/FAADAQECAwQBBQGVAZkBmAH/AcsBzwHOAf8B2wHdAdwB/wHY + AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmcBzAH/ + AmcBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmcBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ + AcsBzwHOAf8B2wHdAdwB/wHYAdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/ + A0oBiQNbAcQDXgHOAxkBIyAAAYEBhAGCAf8D1gH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt + Af8D7QH/A9QB/wGBAYQBggH/AikBpAH/AikBzBX/AikBzAH/AikBpAH/CAABgQGEAYIB/wPWAf8B9wHx + AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/ + AfcB8QHrAf8D1AH/AYEBhAGCAf8EAAMFAQcDJgE4A4EB/wOBAf8DgQH/AxoBJRAAAx8BLANfAdUBoQGm + AaQB/wGhAaUBowH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/ + AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJnAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB + AfMB/wJ5Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANfAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl + AaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/AaUBqQGnAf8BpQGpAacB/wGgAaUBowH/ + A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBcQFf + Af8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGB + AXEBXwH/A+0B/wGBAYQBggH/AikBpAH/AikBzAn/AikBzAn/AikBzAH/AikBpAH/CAABgQGEAYIB/wH6 + AfYB8gH/AaQBgQFlAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGoAYEBawH/AagBgQFr + Af8BqAGBAWsB/wGkAYEBZQH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA + AzUBVQG/AcMBwQH/AdcB3AHZAf8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/ + Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJnAcwB/wKBAdcB/wKB + AdsB/wJnAcwB/wJ3Ac8B/wKBAeEB/wJ7AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ + Af8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/Ad0B4gHfAf8B3wHkAeEB/wHf + AeMB4QH/Ad8B4wHhAf8B1gHaAdgB/wGcAaEBnwH/A1YBtANWAbQDVQG1A1UBtQNVAbUDVQG1A1YBswMx + AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFxAV8B/wGTAYEBdQH/AZMBgQF1Af8BlAGBAXUB/wGTAYEBdQH/ + AZMBgQF1Af8BkwGBAXYB/wGUAYEBdQH/AYEBcQFfAf8D7gH/AYEBhAGCAf8CKQGkAf8CKQHMAf8CKQHM + Af8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB + AWsB/wG6AYEBbwH/AboBgQFvAf8BuwGBAW8B/wG6AYEBbwH/AboBgQFvAf8BugGBAXAB/wG7AYEBbwH/ + AagBgQFrAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA + AzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/AZMBlgHK + Af8CgQHMAf8CbAHNAf8CZwHMAf8CZwHMAf8CZwHMAf8CZwHMAf8CZwHMAf8CZwHMAf8CgQHTAf8CQAG0 + Af0EAAM1AVUBwgHFAcQB/wNSAaMDgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wGdAoEB/wHb + AeAB3gH/AZ4BowGhAf8B3wLhAf8B4AHiAeEB/wHfAeIB4AH/Ad4B4QHfAf8B4gHjAeIB/wHjAeUB4wH/ + AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBcQFfAf8BqwGKAYEB/wGdAoEB/wGPAYEBcgH/ + AZABgQFyAf8BkAGBAXIB/wGQAYEBcgH/AY8BgQFyAf8BgQFxAV8B/wPuAf8BgQGEAYIB/wFpAWsBkgH/ + AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/A0YBfggAAYEBhAGCAf8B+AHy + AewB/wGoAYEBawH/AdABngGBAf8BxAGLAYEB/wG2AYEBbQH/AbcBgQFtAf8BtwGBAW0B/wG3AYEBbQH/ + AbYBgQFtAf8BqAGBAWsB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDuwH9A4EB/wGgAoEB/wGfAoEB/wGa + AoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8BtgG5AdYB/wKBAbsB/wKBAcwB/wKB + AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7sB/QOBAf8BoAKB + Af8BnwKBAf8BmgKBAf8BmAKBAf8BlwKBAf8BmAKBAf8BmAKBAf8BmAKBAf8DgQH/Ad8B4gHhAf8BngGi + AaAB/wGqAqsB/wGxArIB/wGuAq8B/wGyArMB/wG6ArsB/wGcAZ4BnQH/A2QB5wNAAW4DFgEfBAABgQGE + AYIB/wPuAf8BgQFxAV8B/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFv + Af8BjAGBAW8B/wGBAXEBXwH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC + Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFrAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG + AYEB/wG6AYEBdgH/AbIBgQFqAf8BtAGBAWoB/wGoAYEBawH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ + A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AwEBAgwAAzUBVQNfAfsDgQH/AawBiwGBAf8BqwGJAYEB/wGl + AoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B2gLeAf8BggGGAaUB/wKBAZIB/wKB + AZEB/wKBAZEB/wKBAZEB/wKBAZEB/wJUAVYBqwNFAXwDVwG6AzUBVgQAAzUBVQNfAfsDgQH/AawBiwGB + Af8BqwGJAYEB/wGlAoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B3QHhAd8B/wGc + AaABnwH/AZIBlQGUAf8BkgGVAZQB/wGQAZMBkgH/AZsBngGdAf8BlAGXAZYB/wNWAasDOwFkA1IBoQMr + AUIEAAGBAYQBggH/A/AB/wGBAXEBXwH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE + AYEB/wGmAYQBgQH/AZABgQF6Af8BgQFxAV8B/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ + A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFrAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB + Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXQB/wGoAYEBawH/AfgB9AHuAf8BgQGEAYIB/wPr + Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3sB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ + AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOBAf8BvQHBAcAB/wGXAZsBmQH/ + AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNfAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3sB+AOB + Af8BqwGJAYEB/wGpAYcBgQH/AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOB + Af8BvQHBAcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNfAdUDTQGRA14B2QNQ + AZsDDQERBAABgQGEAYIB/wPzAf8BgQFxAV8B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi + AoEB/wGjAoEB/wGBAXEBXwH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBawH/AckBlQGBAf8ByQGU + AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWsB/wH4AfQB7gH/ + AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9 + EAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGBAf8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/ + AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGqAagB/wGlAaoBqAH/AaUBqgGnAf8BpQGp + AacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGB + Af8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGq + AagB/wGlAaoBqAH/AaUBqgGnAf8BpQGpAacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAABgQGEAYIB/wPt + Af8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGB + AXEBXwH/AYEBcQFfAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA + AYEBhAGCAf8B9wHxAesB/wGkAYEBZQH/AagBgQFrAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGB + AWsB/wGoAYEBawH/AagBgQFrAf8BpAGBAWUB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ + AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/A9QB/wGBAYQBggH/EAADNQFVA2UB9AOBAf8BqAGGAYEB/wGn + AYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGjAoEB/wGdAoEB/wGVAoEB/wOBAf8B0wHWAdUB/wGZ + AZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/Ad8B4wHhAf8B3AHgAd4B/wHWAdkB2AH/AYEBhAGC + Af8MAAM1AVUDZQH0A4EB/wGoAYYBgQH/AacBhQGBAf8BpgGEAYEB/wGlAYIBgQH/AaQCgQH/AaMCgQH/ + AZ0CgQH/AZUCgQH/A4EB/wHTAdYB1QH/AZkBngGcAf8BxQHKAcgB/wHeAeIB4AH/Ad8B4wHhAf8B3wHj + AeEB/wHcAeAB3gH/AdYB2QHYAf8BgQGEAYIB/wwAAYEBhAGCAf8BxwLIAf8D7QH/A+0B/wPtAf8D7QH/ + A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGB + AXEBXwH/AYEBcQFfAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 + AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AdkC2gH/ + AYEBhAGCAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGkAYEBZQH/AfcB8QHrAf8BgQGE + AYIB/xAAAzUBVQNoAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ + AaACgQH/AZ8CgQH/A4EB/wHAAcMBwQH/AZYBmQGXAf8DgQH/A4EB/wOBAf8DgQH/A1kBwgHcAd8B3gH/ + AYEBhAGCAf8MAAM1AVUDaAHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh + AoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/A4EB/wNZAcIB3AHf + Ad4B/wGBAYQBggH/DAADVAGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBdQH/ + AZMBgQF1Af8BkwGBAXYB/wGUAYEBdQH/AYEBcQFfAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFvAf8BugGBAW8B/wG6AYEBcAH/AbsBgQFvAf8BqAGB + AWsB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW + AoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGdAoEB/wHUAdYB1AH/AZsBnQGbAf8BlwKBAf8BmAKBAf8BlwKB + Af8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/DAADNQFVA2UB9ANUAawBlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGbAZ0BmwH/AZcCgQH/ + AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBcQFf + Af8BqwGKAYEB/wGdAoEB/wGPAYEBcgH/AZABgQFyAf8BkAGBAXIB/wGQAYEBcgH/AY8BgQFyAf8BgQFx + AV8B/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBawH/AdABngGBAf8BxAGLAYEB/wG2 + AYEBbQH/AbcBgQFtAf8BtwGBAW0B/wG3AYEBbQH/AbYBgQFtAf8BqAGBAWsB/wH4AfIB7AH/AYEBhAGC + Af8QAAMhATADXgHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ + AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmwGfAZ0B/wGJAYcBgQH/AZQCgQH/AZMCgQH/ + AZQCgQH/AZQCgQH/AZsCgQH/A7sB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe + AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ + AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO7Af0BgQGEAYIB/ygA + AYEBhAGCAf8D7gH/AYEBcQFfAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL + AYEBbwH/AYwBgQFvAf8BgQFxAV8B/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBawH/ + Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXYB/wGyAYEBagH/AbQBgQFqAf8BqAGB + AWsB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDbQH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ + AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DewH8AYEBhAGCAf8oAANSAakDbQH1 + AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKB + Af8BjgKBAf8DewH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAXEBXwH/AaYBgwGBAf8BpQGEAYEB/wGm + AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF6Af8BgQFxAV8B/wPwAf8BgQGEAYIB/ywA + AYEBhAGCAf8B+AH0Ae4B/wGoAYEBawH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY + AYEB/wHMAZgBgQH/AbcBgQF0Af8BqAGBAWsB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDbwHxAYkCgQH/ + AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGOAoEB/wGLAoEB/wGX + AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNvAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE + AYEB/wGmAYIBgQH/AZYCgQH/AY4CgQH/AYsCgQH/AZcCgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wPz + Af8BgQFxAV8B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAXEBXwH/ + A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFrAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB + Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBawH/AfgB9AHuAf8BgQGEAYIB/ywA + A1IBqQNpAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb + AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNpAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ + AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGbAoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygA + AYEBhAGCAf8D7QH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/ + AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFl + Af8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGk + AYEBZQH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNjAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB + Af8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKBAf8DagH5AYEBhAGCAf8oAANSAakDYwHp + AYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGkAYIBgQH/AaMCgQH/AaICgQH/AaECgQH/AaACgQH/AZ4CgQH/ + AZACgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wHHAsgB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt + Af8D7QH/A+0B/wHZAtoB/wGBAYQBggH/LAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/AfcB8QHrAf8B9wHx + AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wHZAtoB/wGB + AYQBggH/LAADUgGpA2kB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8DYAHWA28B8QGBAYQBggH/KAADUgGpA2kB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA28B8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ + AYEBhAGCAf8BgQGEAYIB/wNPAZcsAANUAawBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A08BlywA + AzgBXgN/Af4BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe + AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wNfAfsDWwHIKAADOAFeA38B/gGaAZ4BnQH/AZoBngGd + Af8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGa + AZ4BnQH/A18B+wNbAcj/AIkAAUIBTQE+BwABPgMAASgDAAFgAwABMAMAAQEBAAEBBQABQAECFgAD//8A + IgAG/wGAAgABgAEAAX8BgAEAAQEBgAEAAQcBgAIAAYABAAE/AYABAAEBAYABAAEHAYACAAGAAQABPwGA + AQABAQGAAQABBwGAAgABgAEAAT8BgAEAAQEBgAEAAQcBgAIAAYABAAE/AYABAAEBAYABAAEHAcACAAHA + AQABfwGAAQABAQGAAQIBBwGAAgABgAIAAYABAAEBAYABAAEPAYACAAGAAgABgAEAAQEBgAEAAQMBgAIA + AYACAAGAAQABAQGAAQABAwGAAgABgAIAAYABAAEDAYABAAEDAYACAAGAAgABgAEAAQcBgAEAAQcBgAIA + AYACAAGAAQABBwGAAQABBwGAAQABAwGAAQABAwGAAQABBwGAAQABBwGAAQABAwGAAQABAwGAAQABBwGA + AQABBwGAAQABAwGAAQABAwGAAQABBwGAAQABBwGAAQABAwGAAQABAwH/AQABBwH/AQABBwGAAQABAwGA + AQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwH/AQABBwH/ + AQABBwH/AQABAwH/AQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwH/AQABBwH/AQABBwH/AQABAwH/ + AQABAwH/AQABBwH/AQABBwH/AQABAwH/AQABAwz/Cw== diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index ed93f70..9593e5c 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -42,8 +42,7 @@ public class SftpDrive : IDisposable, ISerializable private readonly AutoResetEvent _pauseEvent = new AutoResetEvent(false); private readonly CancellationTokenSource _threadCancel = new CancellationTokenSource(); private bool _exeptionThrown; - private SftpFilesystem _filesystem; - public VirtualDrive _virtualDrive { get; set; } + internal SftpFilesystem _filesystem; private Exception _lastExeption; private Thread _mountThread; @@ -150,15 +149,11 @@ private void MountLoop() try { - //if (_virtualDrive.Status == DriveStatus.Mounted) - if (_virtualDrive != null) - _virtualDrive.AddSubFS("VFS-" + Letter, _filesystem); - int threadCount = 8; #if DEBUG threadCount=1; #endif - _filesystem.Mount(String.Format("{0}:\\", Letter), + _filesystem.Mount(String.Format("{0}:\\", Letter), Settings.Default.UseNetworkDrive?DokanOptions.NetworkDrive|DokanOptions.KeepAlive: DokanOptions.RemovableDrive|DokanOptions.KeepAlive, threadCount); } catch (Exception e) @@ -201,36 +196,36 @@ public void Mount() Status = DriveStatus.Unmounted; throw; + } + + if (Letter != ' ') + { + SetupMountThread(); + + var mountEvent = Task.Factory.StartNew(() => + { + while (!_mountCancel.IsCancellationRequested && + Directory.GetLogicalDrives().All( + drive => drive[0] != Letter)) + { + Thread.Sleep(200); + } + }, _mountCancel.Token); + + _pauseEvent.Set(); + + mountEvent.Wait(); + + if (_exeptionThrown) + { + + _exeptionThrown = false; + + throw _lastExeption; + } + if (Settings.Default.UseNetworkDrive) + Utilities.SetNetworkDriveName(_connection, Name); } - - SetupMountThread(); - - - - var mountEvent = Task.Factory.StartNew(() => - { - while (!_mountCancel.IsCancellationRequested && - Directory.GetLogicalDrives().All( - drive => drive[0] != Letter)) - { - Thread.Sleep(200); - } - }, _mountCancel.Token); - - - _pauseEvent.Set(); - - mountEvent.Wait(); - - if (_exeptionThrown) - { - - _exeptionThrown = false; - - throw _lastExeption; - } - if(Settings.Default.UseNetworkDrive) - Utilities.SetNetworkDriveName(_connection, Name); Status= DriveStatus.Mounted; OnStatusChanged(EventArgs.Empty); diff --git a/Sshfs/Sshfs/Utilities.cs b/Sshfs/Sshfs/Utilities.cs index 6896539..78c0aa6 100644 --- a/Sshfs/Sshfs/Utilities.cs +++ b/Sshfs/Sshfs/Utilities.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD #region using System; @@ -140,4 +141,140 @@ public static void SetNetworkDriveName(string connection,string name) } } } +======= +#region + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.IsolatedStorage; +using System.Linq; +using System.Runtime.Serialization; +using System.Security.Cryptography; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using Renci.SshNet; + +#endregion + +namespace Sshfs +{ + internal static class Utilities + { + private static readonly DirectoryInfo datadir = Directory.CreateDirectory( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\WinSshFS" + ); + + public static void Load(this List list, string file) where T : ISerializable + { + string filepath = datadir.FullName+"\\"+file; + if (!File.Exists(filepath)) return; + + var xmlSerializer = new DataContractSerializer(typeof(IEnumerable)); + using ( + var stream = File.Open(filepath, FileMode.OpenOrCreate, + FileAccess.Read)) + { + list.Clear(); + + list.AddRange(xmlSerializer.ReadObject(stream) as IEnumerable); + } + } + + public static void Presist(this List list, string file, bool delete = false) where T : ISerializable + + { + string filepath = datadir.FullName + "\\" + file; + if (delete) + { + File.Delete(filepath); + } + else + { + var xmlSerializer = new DataContractSerializer(typeof (List)); + using ( + var stream = File.Open(filepath, FileMode.Create, + FileAccess.Write)) + { + xmlSerializer.WriteObject(stream, list); + } + } + } + + public static string ProtectString(string stringToProtect) + { + return stringToProtect != null + ? Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(stringToProtect), null, + DataProtectionScope.CurrentUser)) + : null; + } + + public static string UnprotectString(string stringToUnprotect) + { + return stringToUnprotect != null + ? Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(stringToUnprotect), + null, + DataProtectionScope.CurrentUser)) + : null; + } + + public static IEnumerable GetAvailableDrives() + { + return Enumerable.Range('D', 23).Select(value => (char) value).Except( + Directory.GetLogicalDrives().Select(drive => drive[0])); + } + + + public static void RegisterForStartup() + { + Registry.CurrentUser.OpenSubKey + ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true).SetValue(Application.ProductName, + Application.ExecutablePath); + } + + public static void UnregisterForStarup() + { + Registry.CurrentUser.OpenSubKey + ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true).DeleteValue(Application.ProductName); + } + + + public static bool IsAppRegistredForStarup() + { + return (Registry.CurrentUser.OpenSubKey + ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", false).GetValue(Application.ProductName) + as string) == Application.ExecutablePath; + } + + + public static bool IsValidUnixPath(string value) + { + return !string.IsNullOrEmpty(value) && (value[0] == '.' || value[0] == '/') && (value.IndexOf('\\') == -1); + } + + /* public static bool IsValidPrivateKey(string path) + { + try + { + new PrivateKeyFile(path); + return true; + } + catch + { + return false; + } + }*/ + + public static void SetNetworkDriveName(string connection,string name) + { + var drive= Registry.CurrentUser.OpenSubKey + ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MountPoints2", false).OpenSubKey(connection.Replace("\\","#"),true); + if(drive!=null) + { + drive.SetValue("_LabelFromReg", name); + } + } + } +>>>>>>> ce0c99f... automounting by folder and gui update } \ No newline at end of file diff --git a/Sshfs/Sshfs/VirtualDrive.cs b/Sshfs/Sshfs/VirtualDrive.cs index cad2fb4..bf58c4f 100644 --- a/Sshfs/Sshfs/VirtualDrive.cs +++ b/Sshfs/Sshfs/VirtualDrive.cs @@ -41,12 +41,12 @@ public class VirtualDrive : IDisposable public VirtualDrive() { } - internal void AddSubFS(string path, SftpFilesystem fileSystem) + internal void AddSubFS(string path, SftpDrive fileSystem) { _filesystem.AddSubFS(path, fileSystem); } - internal void RemoveSubFS(string path, SftpFilesystem fileSystem) + internal void RemoveSubFS(string path, SftpDrive fileSystem) { _filesystem.RemoveSubFS(path, fileSystem); } diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 92ce880..f1e4eef 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -22,7 +22,7 @@ internal sealed class VirtualFilesystem : IDokanOperations private readonly string _volumeLabel; private bool _debugMode = false; - private readonly Dictionary _subsytems = new Dictionary(); + private readonly Dictionary _subsytems = new Dictionary(); #endregion @@ -37,12 +37,12 @@ public VirtualFilesystem(string label = null) #region Methods - internal void AddSubFS(string path, SftpFilesystem fileSystem) + internal void AddSubFS(string path, SftpDrive fileSystem) { _subsytems.Add(path, fileSystem); } - internal void RemoveSubFS(string path, SftpFilesystem fileSystem) + internal void RemoveSubFS(string path, SftpDrive fileSystem) { _subsytems.Remove(path); } @@ -106,13 +106,32 @@ private string GetSubSystemFileName(string fileName, out string subfs) return fileName; } + private IDokanOperations GetSubSystemOperations(string subid) + { + if (subid == null) + return null; + if (!this._subsytems.ContainsKey(subid)) + { + Log("Drive subid no longer exists?"); + return null; + } + SftpDrive subdrive = this._subsytems[subid]; + + if (subdrive.Status != DriveStatus.Mounted){ + subdrive.Mount(); + } + + SftpFilesystem subfs = subdrive._filesystem; + return ((IDokanOperations)subfs); + } + DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { Log("VFS OpenDir:{0}", fileName); - string subfs; - string subfilename = GetSubSystemFileName(fileName, out subfs); - if (subfs != null) - return ((IDokanOperations)this._subsytems[subfs]).OpenDirectory(subfilename, info); + string subid; + string subfilename = GetSubSystemFileName(fileName, out subid); + if (subid != null) + return GetSubSystemOperations(subid).OpenDirectory(subfilename, info); if (fileName == "\\") { @@ -175,11 +194,11 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList(); From 0a75049de817ae7c5d476ca33c47033d098dfb06 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 23 Jul 2014 01:00:21 +0200 Subject: [PATCH 034/134] depth mounting base still buggy --- Sshfs/Sshfs/MainForm.Designer.cs | 40 +++--- Sshfs/Sshfs/MainForm.cs | 6 +- Sshfs/Sshfs/MainForm.resx | 208 +++++++++++++++---------------- Sshfs/Sshfs/SftpDrive.cs | 17 ++- Sshfs/Sshfs/VirtualDrive.cs | 8 +- Sshfs/Sshfs/VirtualFilesystem.cs | 152 ++++++++++++---------- 6 files changed, 233 insertions(+), 198 deletions(-) diff --git a/Sshfs/Sshfs/MainForm.Designer.cs b/Sshfs/Sshfs/MainForm.Designer.cs index 68f9d05..7a0fff8 100644 --- a/Sshfs/Sshfs/MainForm.Designer.cs +++ b/Sshfs/Sshfs/MainForm.Designer.cs @@ -55,7 +55,7 @@ private void InitializeComponent() this.passwordBox = new System.Windows.Forms.TextBox(); this.authLabel = new System.Windows.Forms.Label(); this.label8 = new System.Windows.Forms.Label(); - this.textBox1 = new System.Windows.Forms.TextBox(); + this.mountPointBox = new System.Windows.Forms.TextBox(); this.driveListView = new System.Windows.Forms.ListView(); this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.imageList = new System.Windows.Forms.ImageList(this.components); @@ -116,7 +116,7 @@ private void InitializeComponent() this.fieldsPanel.ColumnCount = 3; this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 26.10063F)); this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 73.89937F)); - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 9F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 10F)); this.fieldsPanel.Controls.Add(this.nameBox, 1, 0); this.fieldsPanel.Controls.Add(this.label1, 0, 0); this.fieldsPanel.Controls.Add(this.hostBox, 1, 1); @@ -134,7 +134,7 @@ private void InitializeComponent() this.fieldsPanel.Controls.Add(this.panel2, 1, 5); this.fieldsPanel.Controls.Add(this.authLabel, 0, 5); this.fieldsPanel.Controls.Add(this.label8, 0, 8); - this.fieldsPanel.Controls.Add(this.textBox1, 1, 8); + this.fieldsPanel.Controls.Add(this.mountPointBox, 1, 8); this.fieldsPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.fieldsPanel.Location = new System.Drawing.Point(253, 5); this.fieldsPanel.Name = "fieldsPanel"; @@ -156,7 +156,7 @@ private void InitializeComponent() // // nameBox // - this.nameBox.Location = new System.Drawing.Point(86, 3); + this.nameBox.Location = new System.Drawing.Point(85, 3); this.nameBox.Name = "nameBox"; this.nameBox.Size = new System.Drawing.Size(186, 20); this.nameBox.TabIndex = 0; @@ -175,7 +175,7 @@ private void InitializeComponent() // // hostBox // - this.hostBox.Location = new System.Drawing.Point(86, 32); + this.hostBox.Location = new System.Drawing.Point(85, 32); this.hostBox.Name = "hostBox"; this.hostBox.Size = new System.Drawing.Size(186, 20); this.hostBox.TabIndex = 1; @@ -194,7 +194,7 @@ private void InitializeComponent() // // portBox // - this.portBox.Location = new System.Drawing.Point(86, 62); + this.portBox.Location = new System.Drawing.Point(85, 62); this.portBox.Name = "portBox"; this.portBox.Size = new System.Drawing.Size(68, 20); this.portBox.TabIndex = 2; @@ -223,7 +223,7 @@ private void InitializeComponent() // // userBox // - this.userBox.Location = new System.Drawing.Point(86, 90); + this.userBox.Location = new System.Drawing.Point(85, 90); this.userBox.Name = "userBox"; this.userBox.Size = new System.Drawing.Size(186, 20); this.userBox.TabIndex = 3; @@ -237,7 +237,7 @@ private void InitializeComponent() "Password", "PrivateKey", "Pageant"}); - this.authCombo.Location = new System.Drawing.Point(86, 119); + this.authCombo.Location = new System.Drawing.Point(85, 119); this.authCombo.Name = "authCombo"; this.authCombo.Size = new System.Drawing.Size(121, 21); this.authCombo.TabIndex = 4; @@ -269,7 +269,7 @@ private void InitializeComponent() // this.panel1.Controls.Add(this.letterBox); this.panel1.Controls.Add(this.mountCheck); - this.panel1.Location = new System.Drawing.Point(86, 238); + this.panel1.Location = new System.Drawing.Point(85, 238); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(182, 23); this.panel1.TabIndex = 12; @@ -303,7 +303,7 @@ private void InitializeComponent() this.directoryBox.Items.AddRange(new object[] { ".", "/"}); - this.directoryBox.Location = new System.Drawing.Point(86, 211); + this.directoryBox.Location = new System.Drawing.Point(85, 211); this.directoryBox.Name = "directoryBox"; this.directoryBox.Size = new System.Drawing.Size(186, 21); this.directoryBox.TabIndex = 8; @@ -325,7 +325,7 @@ private void InitializeComponent() this.panel2.Controls.Add(this.privateKeyBox); this.panel2.Controls.Add(this.privateKeyButton); this.panel2.Controls.Add(this.passwordBox); - this.panel2.Location = new System.Drawing.Point(86, 148); + this.panel2.Location = new System.Drawing.Point(85, 148); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(186, 57); this.panel2.TabIndex = 15; @@ -383,18 +383,18 @@ private void InitializeComponent() this.label8.Dock = System.Windows.Forms.DockStyle.Left; this.label8.Location = new System.Drawing.Point(3, 264); this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(63, 29); + this.label8.Size = new System.Drawing.Size(69, 29); this.label8.TabIndex = 17; - this.label8.Text = "Virtual path:"; + this.label8.Text = "Mount folder:"; this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // - // textBox1 + // mountPointBox // - this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill; - this.textBox1.Location = new System.Drawing.Point(86, 267); - this.textBox1.Name = "textBox1"; - this.textBox1.Size = new System.Drawing.Size(228, 20); - this.textBox1.TabIndex = 18; + this.mountPointBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.mountPointBox.Location = new System.Drawing.Point(85, 267); + this.mountPointBox.Name = "mountPointBox"; + this.mountPointBox.Size = new System.Drawing.Size(228, 20); + this.mountPointBox.TabIndex = 18; // // driveListView // @@ -736,7 +736,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem unmountMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.Label label8; - private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox mountPointBox; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; private System.Windows.Forms.ComboBox virtualDriveCombo; private System.Windows.Forms.Label label9; diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index d36a070..d99917c 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -93,7 +93,7 @@ protected override void OnLoad(EventArgs e) _drives[i].StatusChanged += drive_StatusChanged; if (_drives[i].Name.StartsWith("New Drive")) _namecount++; - virtualDrive.AddSubFS(_drives[i].Name, _drives[i]); + virtualDrive.AddSubFS(_drives[i]); } @@ -367,6 +367,7 @@ private void listView_ItemSelectionChanged(object sender, ListViewItemSelectionC muButton.Text = drive.Status == DriveStatus.Mounted ? "Unmount" : "Mount"; muButton.Image = drive.Status == DriveStatus.Mounted ? Resources.unmount : Resources.mount; muButton.Enabled = (drive.Status == DriveStatus.Unmounted || drive.Status == DriveStatus.Mounted); + mountPointBox.Text = drive.MountPoint; } } @@ -393,7 +394,7 @@ private void saveButton_Click(object sender, EventArgs e) nameBox.Focus(); return; } - var drive = driveListView.SelectedItems[0].Tag as SftpDrive; + SftpDrive drive = driveListView.SelectedItems[0].Tag as SftpDrive; if ((_regex.IsMatch(nameBox.Text) || nameBox.Text == String.Format("{0}@'{1}'", drive.Username, drive.Host)) && !String.IsNullOrEmpty(userBox.Text) && !String.IsNullOrEmpty(hostBox.Text)) @@ -417,6 +418,7 @@ private void saveButton_Click(object sender, EventArgs e) drive.Password = passwordBox.Text; drive.PrivateKey = privateKeyBox.Text; drive.Passphrase = passphraseBox.Text; + drive.MountPoint = mountPointBox.Text; _dirty = true; } diff --git a/Sshfs/Sshfs/MainForm.resx b/Sshfs/Sshfs/MainForm.resx index 8afbe8c..5fca1a6 100644 --- a/Sshfs/Sshfs/MainForm.resx +++ b/Sshfs/Sshfs/MainForm.resx @@ -125,181 +125,181 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACi - LAAAAk1TRnQBSQFMAgEBBAEAASQBAAEkAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg + LAAAAk1TRnQBSQFMAgEBBAEAASwBAAEsAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ - AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABtAH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ - AoEBuAH/Al8BbgHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK + AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABswH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ + AoEBuAH/Al8BbQHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK AwgBCwMHAQoDBwEJAwUBBwMBAQIgAAMPARQDDwEUAxIBGAMeASsDJgE5AygBPAMoATwDJgE4AyIBMQMY - ASIDEAEWAxABFQMRARcDSAGDAikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/ + ASIDEAEWAxABFQMRARcDSAGDAigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/ A0YBfggAAw8BFAMPARQDEgEYAx4BKwMmATkDKAE8AygBPAMmATgDIgExAxgBIgMQARYDEAEVAxEBFwMH AQkDAgEDAwsBDwMWAR4DFgEfAwsBDwMBAQIQAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGz A1YBtANVAbUDVQG1A1UBtQNbAcgDagH5AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/ - AoEB0wH/AoEB1AH/AmQBbwHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 + AoEB0wH/AoEB1AH/AmQBbgHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 A1UBtQNVAbUDVQG1A1YBtAM9AWgDDgETAwMBBBwAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8CKQGkAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHM - Af8CKQGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8CKAGkAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHM + Af8CKAGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AxkBIwMU - ARsDNQFWA2EB2gF4AnkB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg + ARsDNQFWA2EB2gF3AngB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg Af8B3wLhAf8B3wLhAf8B3wLhAf8B3wLhAf8B4QHiAeEB/wHeAeEB3wH/AeEB4gHhAf8BmAGaAc8B/wKB - AcwB/wJnAcwB/wKBAdEB/wKBAdAB/wJnAcwB/wJnAcwB/wKBAdQB/wJrAc0B/wKBAdMB/wKBAbgB/wQA + AcwB/wJmAcwB/wKBAdEB/wKBAdAB/wJmAcwB/wJmAcwB/wKBAdQB/wJqAc0B/wKBAdMB/wKBAbgB/wQA AagBqwGqAf8B5ALlAf8B4QHkAeMB/wHfAuEB/wHfAeEB4AH/Ad8C4QH/Ad8C4QH/Ad8C4QH/Ad8C4QH/ AeEB4gHhAf8B3gHhAd8B/wHhAeIB4QH/AeMB5QHkAf8B4QHjAeIB/wN/Af4DGAEiAwYBCBwAAYEBhAGC - Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AikBpAH/ - AikBzAn/AikBzAn/AikBzAH/AikBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ + Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AigBpAH/ + AigBzAn/AigBzAn/AigBzAH/AigBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ Af8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8DgQH/A4EB/wOBAf8BnAKdAf8DtgH/AawCrQH/A1oBwBAA - A0ABbgNjAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv - Af8ByALJAf8CgQGhAf8CgQHMAf8CZwHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT - Af8CgQHTAf8CgQG4Af8EAANAAW4DYwHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr - Af8BqgKrAf8DuAH/Aa4CrwH/AcgCyQH/AZ0BnwGeAf8DYwHvA0kBiAMgAS4DBAEFHAABgQGEAYIB/wPz - Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CKQGkAf8CKQHM - Ff8CKQHMAf8CKQGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ + A0ABbgNiAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv + Af8ByALJAf8CgQGhAf8CgQHMAf8CZgHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT + Af8CgQHTAf8CgQG4Af8EAANAAW4DYgHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr + Af8BqgKrAf8DuAH/Aa4CrwH/AcgCyQH/AZ0BnwGeAf8DYgHvA0kBiAMgAS4DBAEFHAABgQGEAYIB/wPz + Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CKAGkAf8CKAHM + Ff8CKAHMAf8CKAGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ A8UB/wPoAf8BgQGEAYIB/wOQAf8DswH/A4EB/wO2Af8D6wH/A9UB/wNqAfkQAAMIAQsDOAFdA38B/gGW AZoBmAH/AZUBmQGYAf8BkgGVAZQB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGRAZQBkwH/AZcBmgGZ - Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJnAcwB/wJ3Ac8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJn + Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJmAcwB/wJ2Ac8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJm AcwB/wKBAdMB/wKBAbgB/wQAAwgBCwM4AV0DfwH+AZYBmgGYAf8BlQGZAZgB/wGSAZUBlAH/AZIBlQGU - Af8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGaAZkB/wGYAZwBmwH/A18B1QM/AW0DTAGSA0cBgAMA + Af8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGaAZkB/wGYAZwBmwH/A14B1QM/AW0DTAGSA0cBgAMA AQEcAANPAZcBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQIpAaQB/wIpAcwB/wIpAcwN/wIp - AcwB/wIpAcwB/wIpAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQIoAaQB/wIoAcwB/wIoAcwN/wIo + AcwB/wIoAcwB/wIoAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9A4EB/wOB Af8DgQH/AaMCpAH/A9UB/wPXAf8DWQG/FAADAQECAwQBBQGVAZkBmAH/AcsBzwHOAf8B2wHdAdwB/wHY - AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmcBzAH/ - AmcBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmcBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ + AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmYBzAH/ + AmYBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmYBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ AcsBzwHOAf8B2wHdAdwB/wHYAdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/ A0oBiQNbAcQDXgHOAxkBIyAAAYEBhAGCAf8D1gH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt - Af8D7QH/A9QB/wGBAYQBggH/AikBpAH/AikBzBX/AikBzAH/AikBpAH/CAABgQGEAYIB/wPWAf8B9wHx + Af8D7QH/A9QB/wGBAYQBggH/AigBpAH/AigBzBX/AigBzAH/AigBpAH/CAABgQGEAYIB/wPWAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/ - AfcB8QHrAf8D1AH/AYEBhAGCAf8EAAMFAQcDJgE4A4EB/wOBAf8DgQH/AxoBJRAAAx8BLANfAdUBoQGm + AfcB8QHrAf8D1AH/AYEBhAGCAf8EAAMFAQcDJgE4A4EB/wOBAf8DgQH/AxoBJRAAAx8BLANeAdUBoQGm AaQB/wGhAaUBowH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/ - AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJnAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB - AfMB/wJ5Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANfAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl + AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJmAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB + AfMB/wJ4Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANeAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl AaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/AaUBqQGnAf8BpQGpAacB/wGgAaUBowH/ - A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBcQFf - Af8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGB - AXEBXwH/A+0B/wGBAYQBggH/AikBpAH/AikBzAn/AikBzAn/AikBzAH/AikBpAH/CAABgQGEAYIB/wH6 - AfYB8gH/AaQBgQFlAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGoAYEBawH/AagBgQFr - Af8BqAGBAWsB/wGkAYEBZQH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA + A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBcAFe + Af8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGB + AXABXgH/A+0B/wGBAYQBggH/AigBpAH/AigBzAn/AigBzAn/AigBzAH/AigBpAH/CAABgQGEAYIB/wH6 + AfYB8gH/AaQBgQFkAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGoAYEBagH/AagBgQFq + Af8BqAGBAWoB/wGkAYEBZAH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA AzUBVQG/AcMBwQH/AdcB3AHZAf8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/ - Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJnAcwB/wKBAdcB/wKB - AdsB/wJnAcwB/wJ3Ac8B/wKBAeEB/wJ7AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ + Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJmAcwB/wKBAdcB/wKB + AdsB/wJmAcwB/wJ2Ac8B/wKBAeEB/wJ6AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ Af8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/Ad0B4gHfAf8B3wHkAeEB/wHf AeMB4QH/Ad8B4wHhAf8B1gHaAdgB/wGcAaEBnwH/A1YBtANWAbQDVQG1A1UBtQNVAbUDVQG1A1YBswMx - AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFxAV8B/wGTAYEBdQH/AZMBgQF1Af8BlAGBAXUB/wGTAYEBdQH/ - AZMBgQF1Af8BkwGBAXYB/wGUAYEBdQH/AYEBcQFfAf8D7gH/AYEBhAGCAf8CKQGkAf8CKQHMAf8CKQHM - Af8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQHMAf8CKQGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB - AWsB/wG6AYEBbwH/AboBgQFvAf8BuwGBAW8B/wG6AYEBbwH/AboBgQFvAf8BugGBAXAB/wG7AYEBbwH/ - AagBgQFrAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA + AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFwAV4B/wGTAYEBdAH/AZMBgQF0Af8BlAGBAXQB/wGTAYEBdAH/ + AZMBgQF0Af8BkwGBAXUB/wGUAYEBdAH/AYEBcAFeAf8D7gH/AYEBhAGCAf8CKAGkAf8CKAHMAf8CKAHM + Af8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB + AWoB/wG6AYEBbgH/AboBgQFuAf8BuwGBAW4B/wG6AYEBbgH/AboBgQFuAf8BugGBAW8B/wG7AYEBbgH/ + AagBgQFqAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA AzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/AZMBlgHK - Af8CgQHMAf8CbAHNAf8CZwHMAf8CZwHMAf8CZwHMAf8CZwHMAf8CZwHMAf8CZwHMAf8CgQHTAf8CQAG0 + Af8CgQHMAf8CawHNAf8CZgHMAf8CZgHMAf8CZgHMAf8CZgHMAf8CZgHMAf8CZgHMAf8CgQHTAf8CQAGz Af0EAAM1AVUBwgHFAcQB/wNSAaMDgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wGdAoEB/wHb AeAB3gH/AZ4BowGhAf8B3wLhAf8B4AHiAeEB/wHfAeIB4AH/Ad4B4QHfAf8B4gHjAeIB/wHjAeUB4wH/ - AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBcQFfAf8BqwGKAYEB/wGdAoEB/wGPAYEBcgH/ - AZABgQFyAf8BkAGBAXIB/wGQAYEBcgH/AY8BgQFyAf8BgQFxAV8B/wPuAf8BgQGEAYIB/wFpAWsBkgH/ - AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/AikBpAH/A0YBfggAAYEBhAGCAf8B+AHy - AewB/wGoAYEBawH/AdABngGBAf8BxAGLAYEB/wG2AYEBbQH/AbcBgQFtAf8BtwGBAW0B/wG3AYEBbQH/ - AbYBgQFtAf8BqAGBAWsB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDuwH9A4EB/wGgAoEB/wGfAoEB/wGa + AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBcAFeAf8BqwGKAYEB/wGdAoEB/wGPAYEBcQH/ + AZABgQFxAf8BkAGBAXEB/wGQAYEBcQH/AY8BgQFxAf8BgQFwAV4B/wPuAf8BgQGEAYIB/wFoAWoBkgH/ + AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/A0YBfggAAYEBhAGCAf8B+AHy + AewB/wGoAYEBagH/AdABngGBAf8BxAGLAYEB/wG2AYEBbAH/AbcBgQFsAf8BtwGBAWwB/wG3AYEBbAH/ + AbYBgQFsAf8BqAGBAWoB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDugH9A4EB/wGgAoEB/wGfAoEB/wGa AoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8BtgG5AdYB/wKBAbsB/wKBAcwB/wKB - AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7sB/QOBAf8BoAKB + AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7oB/QOBAf8BoAKB Af8BnwKBAf8BmgKBAf8BmAKBAf8BlwKBAf8BmAKBAf8BmAKBAf8BmAKBAf8DgQH/Ad8B4gHhAf8BngGi AaAB/wGqAqsB/wGxArIB/wGuAq8B/wGyArMB/wG6ArsB/wGcAZ4BnQH/A2QB5wNAAW4DFgEfBAABgQGE - AYIB/wPuAf8BgQFxAV8B/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFv - Af8BjAGBAW8B/wGBAXEBXwH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC - Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFrAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG - AYEB/wG6AYEBdgH/AbIBgQFqAf8BtAGBAWoB/wGoAYEBawH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ + AYIB/wPuAf8BgQFwAV4B/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFu + Af8BjAGBAW4B/wGBAXABXgH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC + Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFqAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG + AYEB/wG6AYEBdQH/AbIBgQFpAf8BtAGBAWkB/wGoAYEBagH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AwEBAgwAAzUBVQNfAfsDgQH/AawBiwGBAf8BqwGJAYEB/wGl AoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B2gLeAf8BggGGAaUB/wKBAZIB/wKB AZEB/wKBAZEB/wKBAZEB/wKBAZEB/wJUAVYBqwNFAXwDVwG6AzUBVgQAAzUBVQNfAfsDgQH/AawBiwGB Af8BqwGJAYEB/wGlAoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B3QHhAd8B/wGc AaABnwH/AZIBlQGUAf8BkgGVAZQB/wGQAZMBkgH/AZsBngGdAf8BlAGXAZYB/wNWAasDOwFkA1IBoQMr - AUIEAAGBAYQBggH/A/AB/wGBAXEBXwH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE - AYEB/wGmAYQBgQH/AZABgQF6Af8BgQFxAV8B/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ - A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFrAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB - Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXQB/wGoAYEBawH/AfgB9AHuAf8BgQGEAYIB/wPr - Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3sB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ + AUIEAAGBAYQBggH/A/AB/wGBAXABXgH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE + AYEB/wGmAYQBgQH/AZABgQF5Af8BgQFwAV4B/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ + A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFqAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB + Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXMB/wGoAYEBagH/AfgB9AHuAf8BgQGEAYIB/wPr + Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3oB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOBAf8BvQHBAcAB/wGXAZsBmQH/ - AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNfAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3sB+AOB + AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3oB+AOB Af8BqwGJAYEB/wGpAYcBgQH/AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOB - Af8BvQHBAcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNfAdUDTQGRA14B2QNQ - AZsDDQERBAABgQGEAYIB/wPzAf8BgQFxAV8B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi - AoEB/wGjAoEB/wGBAXEBXwH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBawH/AckBlQGBAf8ByQGU - AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWsB/wH4AfQB7gH/ + Af8BvQHBAcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQ + AZsDDQERBAABgQGEAYIB/wPzAf8BgQFwAV4B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi + AoEB/wGjAoEB/wGBAXABXgH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBagH/AckBlQGBAf8ByQGU + AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWoB/wH4AfQB7gH/ AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9 EAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGBAf8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/ AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGqAagB/wGlAaoBqAH/AaUBqgGnAf8BpQGp AacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGB Af8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGq AagB/wGlAaoBqAH/AaUBqgGnAf8BpQGpAacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAABgQGEAYIB/wPt - Af8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGB - AXEBXwH/AYEBcQFfAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA - AYEBhAGCAf8B9wHxAesB/wGkAYEBZQH/AagBgQFrAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGB - AWsB/wGoAYEBawH/AagBgQFrAf8BpAGBAWUB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ + Af8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGB + AXABXgH/AYEBcAFeAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA + AYEBhAGCAf8B9wHxAesB/wGkAYEBZAH/AagBgQFqAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGB + AWoB/wGoAYEBagH/AagBgQFqAf8BpAGBAWQB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/A9QB/wGBAYQBggH/EAADNQFVA2UB9AOBAf8BqAGGAYEB/wGn AYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGjAoEB/wGdAoEB/wGVAoEB/wOBAf8B0wHWAdUB/wGZ AZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/Ad8B4wHhAf8B3AHgAd4B/wHWAdkB2AH/AYEBhAGC Af8MAAM1AVUDZQH0A4EB/wGoAYYBgQH/AacBhQGBAf8BpgGEAYEB/wGlAYIBgQH/AaQCgQH/AaMCgQH/ AZ0CgQH/AZUCgQH/A4EB/wHTAdYB1QH/AZkBngGcAf8BxQHKAcgB/wHeAeIB4AH/Ad8B4wHhAf8B3wHj AeEB/wHcAeAB3gH/AdYB2QHYAf8BgQGEAYIB/wwAAYEBhAGCAf8BxwLIAf8D7QH/A+0B/wPtAf8D7QH/ - A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGB - AXEBXwH/AYEBcQFfAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 + A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGB + AXABXgH/AYEBcAFeAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AdkC2gH/ - AYEBhAGCAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGkAYEBZQH/AfcB8QHrAf8BgQGE - AYIB/xAAAzUBVQNoAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ + AYEBhAGCAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGkAYEBZAH/AfcB8QHrAf8BgQGE + AYIB/xAAAzUBVQNnAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ AaACgQH/AZ8CgQH/A4EB/wHAAcMBwQH/AZYBmQGXAf8DgQH/A4EB/wOBAf8DgQH/A1kBwgHcAd8B3gH/ - AYEBhAGCAf8MAAM1AVUDaAHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh + AYEBhAGCAf8MAAM1AVUDZwHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh AoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/A4EB/wNZAcIB3AHf Ad4B/wGBAYQBggH/DAADVAGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBdQH/ - AZMBgQF1Af8BkwGBAXYB/wGUAYEBdQH/AYEBcQFfAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBdAH/ + AZMBgQF0Af8BkwGBAXUB/wGUAYEBdAH/AYEBcAFeAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFvAf8BugGBAW8B/wG6AYEBcAH/AbsBgQFvAf8BqAGB - AWsB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW + Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFuAf8BugGBAW4B/wG6AYEBbwH/AbsBgQFuAf8BqAGB + AWoB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW AoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGdAoEB/wHUAdYB1AH/AZsBnQGbAf8BlwKBAf8BmAKBAf8BlwKB Af8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/DAADNQFVA2UB9ANUAawBlgKBAf8BlgKBAf8BlgKB Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGbAZ0BmwH/AZcCgQH/ - AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBcQFf - Af8BqwGKAYEB/wGdAoEB/wGPAYEBcgH/AZABgQFyAf8BkAGBAXIB/wGQAYEBcgH/AY8BgQFyAf8BgQFx - AV8B/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBawH/AdABngGBAf8BxAGLAYEB/wG2 - AYEBbQH/AbcBgQFtAf8BtwGBAW0B/wG3AYEBbQH/AbYBgQFtAf8BqAGBAWsB/wH4AfIB7AH/AYEBhAGC + AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBcAFe + Af8BqwGKAYEB/wGdAoEB/wGPAYEBcQH/AZABgQFxAf8BkAGBAXEB/wGQAYEBcQH/AY8BgQFxAf8BgQFw + AV4B/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBagH/AdABngGBAf8BxAGLAYEB/wG2 + AYEBbAH/AbcBgQFsAf8BtwGBAWwB/wG3AYEBbAH/AbYBgQFsAf8BqAGBAWoB/wH4AfIB7AH/AYEBhAGC Af8QAAMhATADXgHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmwGfAZ0B/wGJAYcBgQH/AZQCgQH/AZMCgQH/ - AZQCgQH/AZQCgQH/AZsCgQH/A7sB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe + AZQCgQH/AZQCgQH/AZsCgQH/A7oB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ - AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO7Af0BgQGEAYIB/ygA - AYEBhAGCAf8D7gH/AYEBcQFfAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL - AYEBbwH/AYwBgQFvAf8BgQFxAV8B/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBawH/ - Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXYB/wGyAYEBagH/AbQBgQFqAf8BqAGB - AWsB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDbQH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ - AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DewH8AYEBhAGCAf8oAANSAakDbQH1 + AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO6Af0BgQGEAYIB/ygA + AYEBhAGCAf8D7gH/AYEBcAFeAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL + AYEBbgH/AYwBgQFuAf8BgQFwAV4B/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBagH/ + Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXUB/wGyAYEBaQH/AbQBgQFpAf8BqAGB + AWoB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDbAH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ + AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DegH8AYEBhAGCAf8oAANSAakDbAH1 AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKB - Af8BjgKBAf8DewH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAXEBXwH/AaYBgwGBAf8BpQGEAYEB/wGm - AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF6Af8BgQFxAV8B/wPwAf8BgQGEAYIB/ywA - AYEBhAGCAf8B+AH0Ae4B/wGoAYEBawH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY - AYEB/wHMAZgBgQH/AbcBgQF0Af8BqAGBAWsB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDbwHxAYkCgQH/ + Af8BjgKBAf8DegH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAXABXgH/AaYBgwGBAf8BpQGEAYEB/wGm + AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF5Af8BgQFwAV4B/wPwAf8BgQGEAYIB/ywA + AYEBhAGCAf8B+AH0Ae4B/wGoAYEBagH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY + AYEB/wHMAZgBgQH/AbcBgQFzAf8BqAGBAWoB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDbgHxAYkCgQH/ AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGOAoEB/wGLAoEB/wGX - AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNvAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE + AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNuAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE AYEB/wGmAYIBgQH/AZYCgQH/AY4CgQH/AYsCgQH/AZcCgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wPz - Af8BgQFxAV8B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAXEBXwH/ - A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFrAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB - Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBawH/AfgB9AHuAf8BgQGEAYIB/ywA - A1IBqQNpAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb - AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNpAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ + Af8BgQFwAV4B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAXABXgH/ + A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFqAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB + Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBagH/AfgB9AHuAf8BgQGEAYIB/ywA + A1IBqQNnAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb + AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNnAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGbAoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygA - AYEBhAGCAf8D7QH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/ - AYEBcQFfAf8BgQFxAV8B/wGBAXEBXwH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFl - Af8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGoAYEBawH/AagBgQFrAf8BqAGBAWsB/wGk - AYEBZQH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNjAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB - Af8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKBAf8DagH5AYEBhAGCAf8oAANSAakDYwHp + AYEBhAGCAf8D7QH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/ + AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFk + Af8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGk + AYEBZAH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNiAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB + Af8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKBAf8DagH5AYEBhAGCAf8oAANSAakDYgHp AYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGkAYIBgQH/AaMCgQH/AaICgQH/AaECgQH/AaACgQH/AZ4CgQH/ AZACgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wHHAsgB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt Af8D7QH/A+0B/wHZAtoB/wGBAYQBggH/LAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/AfcB8QHrAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wHZAtoB/wGB - AYQBggH/LAADUgGpA2kB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8DYAHWA28B8QGBAYQBggH/KAADUgGpA2kB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA28B8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE + AYQBggH/LAADUgGpA2cB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8DYAHWA24B8QGBAYQBggH/KAADUgGpA2cB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA24B8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ AYEBhAGCAf8BgQGEAYIB/wNPAZcsAANUAawBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A08BlywA diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index 9593e5c..75a3199 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -76,9 +76,9 @@ public class SftpDrive : IDisposable, ISerializable public object Tag { get; set; } - public DriveStatus Status { get; private set; } - - + public DriveStatus Status { get; private set; } + + public string MountPoint { get; set; } public SftpDrive(){} @@ -326,6 +326,14 @@ public SftpDrive(SerializationInfo info, { Passphrase = Utilities.UnprotectString(info.GetString("p")); PrivateKey = info.GetString("k"); + } + try + { + MountPoint = info.GetString("mountpoint"); + } + catch + { + MountPoint = Name;//default is name after version update } } @@ -340,7 +348,8 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("path",Root); info.AddValue("mount", Automount); info.AddValue("user", Username); - info.AddValue("c", (byte)ConnectionType); + info.AddValue("c", (byte)ConnectionType); + info.AddValue("mountpoint", MountPoint); if (ConnectionType == ConnectionType.Password) { info.AddValue("p", Utilities.ProtectString(Password)); diff --git a/Sshfs/Sshfs/VirtualDrive.cs b/Sshfs/Sshfs/VirtualDrive.cs index bf58c4f..8735955 100644 --- a/Sshfs/Sshfs/VirtualDrive.cs +++ b/Sshfs/Sshfs/VirtualDrive.cs @@ -41,14 +41,14 @@ public class VirtualDrive : IDisposable public VirtualDrive() { } - internal void AddSubFS(string path, SftpDrive fileSystem) + internal void AddSubFS(SftpDrive sftpDrive) { - _filesystem.AddSubFS(path, fileSystem); + _filesystem.AddSubFS(sftpDrive); } - internal void RemoveSubFS(string path, SftpDrive fileSystem) + internal void RemoveSubFS(SftpDrive sftpDrive) { - _filesystem.RemoveSubFS(path, fileSystem); + _filesystem.RemoveSubFS(sftpDrive); } diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index f1e4eef..ecaead3 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -22,7 +22,7 @@ internal sealed class VirtualFilesystem : IDokanOperations private readonly string _volumeLabel; private bool _debugMode = false; - private readonly Dictionary _subsytems = new Dictionary(); + private readonly List _subsytems = new List(); #endregion @@ -37,14 +37,14 @@ public VirtualFilesystem(string label = null) #region Methods - internal void AddSubFS(string path, SftpDrive fileSystem) + internal void AddSubFS(SftpDrive sftpDrive) { - _subsytems.Add(path, fileSystem); + _subsytems.Add(sftpDrive); } - internal void RemoveSubFS(string path, SftpDrive fileSystem) + internal void RemoveSubFS(SftpDrive sftpDrive) { - _subsytems.Remove(path); + _subsytems.Remove(sftpDrive); } @@ -75,68 +75,65 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS return DokanError.ErrorAccessDenied; } - private string GetSubSystemFileName(string fileName, out string subfs) + private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) { - string[] parts = fileName.Split(new char[] { '\\'}, 3); - if (parts.Count() > 1) + if (fileName.Length>1) { - if (parts[0] != "") + string path = fileName.Substring(1); + foreach (SftpDrive drive in this._subsytems) { - subfs = null; - return null; - } - subfs = parts[1]; - - if (!this._subsytems.ContainsKey(subfs)) - { - subfs = null; - return "\\"; - } - - if (parts.Count()==3){ - return "\\"+parts[2]; - } - else - { - return "\\"; + if (drive.MountPoint.Length > 0) + { + if (path == drive.MountPoint) // path contains leading \ + { + subfspath = path.Substring(drive.MountPoint.Length + 1); + return drive; + } + } } } - - subfs = null; - return fileName; + subfspath = fileName; + return null; } - private IDokanOperations GetSubSystemOperations(string subid) + private IDokanOperations GetSubSystemOperations(SftpDrive drive) { - if (subid == null) + if (drive == null) return null; - if (!this._subsytems.ContainsKey(subid)) + + if (drive.Status != DriveStatus.Mounted) { - Log("Drive subid no longer exists?"); - return null; - } - SftpDrive subdrive = this._subsytems[subid]; - - if (subdrive.Status != DriveStatus.Mounted){ - subdrive.Mount(); + drive.Mount(); } + if (drive == null) + return null; - SftpFilesystem subfs = subdrive._filesystem; - return ((IDokanOperations)subfs); + return ((IDokanOperations)drive._filesystem); } DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { Log("VFS OpenDir:{0}", fileName); - string subid; - string subfilename = GetSubSystemFileName(fileName, out subid); - if (subid != null) - return GetSubSystemOperations(subid).OpenDirectory(subfilename, info); - if (fileName == "\\") - { - info.IsDirectory = true; + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).OpenDirectory(fileName, info); + + info.IsDirectory = true; + + if (fileName.Length == 1) //root dir return DokanError.ErrorSuccess; + + string path = fileName.Substring(1);//cut leading \ + + foreach (SftpDrive subdrive in _subsytems) + { + string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... + if (path == mp) + return DokanError.ErrorSuccess; + + if (mp.IndexOf(path + '\\') == 0) //path is part of mount point + return DokanError.ErrorSuccess; } return DokanError.ErrorPathNotFound; @@ -192,27 +189,54 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { - Log("FindFiles:{0}", fileName); + Log("VFS FindFiles:{0}", fileName); - string subid; - string subfilename = GetSubSystemFileName(fileName, out subid); - if (subid != null) - { - return GetSubSystemOperations(subid).FindFiles(subfilename, out files, info); - } + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).FindFiles(fileName, out files, info); + files = new List(); - foreach (string dir in _subsytems.Keys) + string path = fileName.Substring(1);//cut leading \ + foreach(SftpDrive subdrive in _subsytems) { - //SftpFilesystem fs = _subsytems[path]; - FileInformation fi = new FileInformation(); - fi.FileName = dir; - fi.Attributes = FileAttributes.Directory | FileAttributes.Offline; - fi.CreationTime = DateTime.Now; - fi.LastWriteTime = DateTime.Now; - fi.LastAccessTime = DateTime.Now; - files.Add(fi); + string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... + + if (path.Length > 0) //not root dir + { + if (path == mp) //this shoud not happend, because is managed by drive + { + Log("Error, mountpoint not in drives?"); + break; + } + + if (mp.IndexOf(path + '\\') == 0) //path is part of mount point =>implies=> length of path>mp + { + mp = mp.Substring(path.Length + 1); //cut the path + } + else + { + continue; + } + } + + int cuttmp = mp.IndexOf('\\'); + if (cuttmp>0) // have submountpoint like mp1\mp2 + { + mp = mp.Substring(0, cuttmp); + } + + if (!files.Select(file => file.FileName).Contains(mp)) + { + FileInformation fi = new FileInformation(); + fi.FileName = mp; + fi.Attributes = FileAttributes.Directory | FileAttributes.Offline; + fi.CreationTime = DateTime.Now; + fi.LastWriteTime = DateTime.Now; + fi.LastAccessTime = DateTime.Now; + files.Add(fi); + } } return DokanError.ErrorSuccess; From ced25ba1fd936e24bff09672c061d997bcdfe449 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 23 Jul 2014 11:48:26 +0200 Subject: [PATCH 035/134] Dokan implementation extend --- DokanNet/Dokan.cs | 2 +- Sshfs/Sshfs/SftpFilesystem.cs | 32 +++-- Sshfs/Sshfs/Utilities.cs | 137 -------------------- Sshfs/Sshfs/VirtualFilesystem.cs | 212 ++++++++++++++++++++++++++++--- 4 files changed, 216 insertions(+), 167 deletions(-) diff --git a/DokanNet/Dokan.cs b/DokanNet/Dokan.cs index f35c1f7..838d7da 100644 --- a/DokanNet/Dokan.cs +++ b/DokanNet/Dokan.cs @@ -74,7 +74,7 @@ public static void Mount(this IDokanOperations operations, string mountPoint,Dok OpenDirectory = dokanOperationProxy.OpenDirectoryProxy, CreateDirectory = dokanOperationProxy.CreateDirectoryProxy, Cleanup = dokanOperationProxy.CleanupProxy, - // CloseFile = dokanOperationProxy.CloseFileProxy, + CloseFile = dokanOperationProxy.CloseFileProxy, ReadFile = dokanOperationProxy.ReadFileProxy, WriteFile = dokanOperationProxy.WriteFileProxy, FlushFileBuffers = dokanOperationProxy.FlushFileBuffersProxy, diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index b681704..11c0013 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -464,6 +464,10 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) LogFSActionInit("CloseFile", fileName, (SftpContext)info.Context, ""); //???flush? + (info.Context as SftpContext).Stream.Flush(); + (info.Context as SftpContext).Stream.Dispose(); + _cache.Remove(fileName); + return DokanError.ErrorSuccess; } @@ -525,6 +529,11 @@ DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int by } else { + if (fileName == "\\public\\1\\test\\.git\\config.lock") + { + Log("Data: {0}", Encoding.ASCII.GetString(buffer)); + } + var stream = (info.Context as SftpContext).Stream; lock (stream) { @@ -547,7 +556,8 @@ DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info //Log("FLUSH:{0}", fileName); LogFSActionInit("FlushFile", fileName, (SftpContext)info.Context,""); - (info.Context as SftpContext).Stream.Flush(); //I newer saw it get called ,but .. + (info.Context as SftpContext).Stream.Flush(); //git use this + _cache.Remove(fileName); LogFSActionSuccess("FlushFile", fileName, (SftpContext)info.Context, ""); return DokanError.ErrorSuccess; @@ -760,13 +770,17 @@ var file in _sftpSession.RequestClose(handle); - - _cache.Add(fileName, new Tuple>( - (info.Context as SftpContext).Attributes.LastWriteTime, - files), - DateTimeOffset.UtcNow.AddSeconds(Math.Max(_attributeCacheTimeout, - Math.Min(files.Count, _directoryCacheTimeout)))); - + try + { + _cache.Add(fileName, new Tuple>( + (info.Context as SftpContext).Attributes.LastWriteTime, + files), + DateTimeOffset.UtcNow.AddSeconds(Math.Max(_attributeCacheTimeout, + Math.Min(files.Count, _directoryCacheTimeout)))); + } + catch + { + } LogFSActionSuccess("FindFiles", fileName, (SftpContext)info.Context, "Count:{0}", files.Count); return DokanError.ErrorSuccess; } @@ -801,7 +815,7 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName, string searchP //apply pattern List filteredfiles = new List(); - Regex repattern = new Regex(Regex.Escape(searchPattern).Replace("\\*", ".*")); + Regex repattern = new Regex("^"+Regex.Escape(searchPattern).Replace("\\*", ".*")+"$"); foreach(FileInformation fi in files){ if (repattern.IsMatch(fi.FileName)) filteredfiles.Add(fi); diff --git a/Sshfs/Sshfs/Utilities.cs b/Sshfs/Sshfs/Utilities.cs index 78c0aa6..6896539 100644 --- a/Sshfs/Sshfs/Utilities.cs +++ b/Sshfs/Sshfs/Utilities.cs @@ -1,4 +1,3 @@ -<<<<<<< HEAD #region using System; @@ -141,140 +140,4 @@ public static void SetNetworkDriveName(string connection,string name) } } } -======= -#region - -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.IsolatedStorage; -using System.Linq; -using System.Runtime.Serialization; -using System.Security.Cryptography; -using System.Text; -using System.Windows.Forms; -using Microsoft.Win32; -using Renci.SshNet; - -#endregion - -namespace Sshfs -{ - internal static class Utilities - { - private static readonly DirectoryInfo datadir = Directory.CreateDirectory( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\WinSshFS" - ); - - public static void Load(this List list, string file) where T : ISerializable - { - string filepath = datadir.FullName+"\\"+file; - if (!File.Exists(filepath)) return; - - var xmlSerializer = new DataContractSerializer(typeof(IEnumerable)); - using ( - var stream = File.Open(filepath, FileMode.OpenOrCreate, - FileAccess.Read)) - { - list.Clear(); - - list.AddRange(xmlSerializer.ReadObject(stream) as IEnumerable); - } - } - - public static void Presist(this List list, string file, bool delete = false) where T : ISerializable - - { - string filepath = datadir.FullName + "\\" + file; - if (delete) - { - File.Delete(filepath); - } - else - { - var xmlSerializer = new DataContractSerializer(typeof (List)); - using ( - var stream = File.Open(filepath, FileMode.Create, - FileAccess.Write)) - { - xmlSerializer.WriteObject(stream, list); - } - } - } - - public static string ProtectString(string stringToProtect) - { - return stringToProtect != null - ? Convert.ToBase64String(ProtectedData.Protect(Encoding.UTF8.GetBytes(stringToProtect), null, - DataProtectionScope.CurrentUser)) - : null; - } - - public static string UnprotectString(string stringToUnprotect) - { - return stringToUnprotect != null - ? Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(stringToUnprotect), - null, - DataProtectionScope.CurrentUser)) - : null; - } - - public static IEnumerable GetAvailableDrives() - { - return Enumerable.Range('D', 23).Select(value => (char) value).Except( - Directory.GetLogicalDrives().Select(drive => drive[0])); - } - - - public static void RegisterForStartup() - { - Registry.CurrentUser.OpenSubKey - ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true).SetValue(Application.ProductName, - Application.ExecutablePath); - } - - public static void UnregisterForStarup() - { - Registry.CurrentUser.OpenSubKey - ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true).DeleteValue(Application.ProductName); - } - - - public static bool IsAppRegistredForStarup() - { - return (Registry.CurrentUser.OpenSubKey - ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", false).GetValue(Application.ProductName) - as string) == Application.ExecutablePath; - } - - - public static bool IsValidUnixPath(string value) - { - return !string.IsNullOrEmpty(value) && (value[0] == '.' || value[0] == '/') && (value.IndexOf('\\') == -1); - } - - /* public static bool IsValidPrivateKey(string path) - { - try - { - new PrivateKeyFile(path); - return true; - } - catch - { - return false; - } - }*/ - - public static void SetNetworkDriveName(string connection,string name) - { - var drive= Registry.CurrentUser.OpenSubKey - ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MountPoints2", false).OpenSubKey(connection.Replace("\\","#"),true); - if(drive!=null) - { - drive.SetValue("_LabelFromReg", name); - } - } - } ->>>>>>> ce0c99f... automounting by folder and gui update } \ No newline at end of file diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index ecaead3..ce545b4 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -72,7 +72,16 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS { return DokanError.ErrorFileNotFound; } - return DokanError.ErrorAccessDenied; + + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).CreateFile(fileName, access, share, mode, options, attributes, info); + + //Todo: check against mountpoints + info.IsDirectory = true; + info.Context = this; + + return DokanError.ErrorSuccess; } private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) @@ -84,9 +93,9 @@ private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) { if (drive.MountPoint.Length > 0) { - if (path == drive.MountPoint) // path contains leading \ + if (path.IndexOf(drive.MountPoint)==0) { - subfspath = path.Substring(drive.MountPoint.Length + 1); + subfspath = path.Substring(drive.MountPoint.Length); return drive; } } @@ -103,7 +112,30 @@ private IDokanOperations GetSubSystemOperations(SftpDrive drive) if (drive.Status != DriveStatus.Mounted) { - drive.Mount(); + try + { + drive.Mount(); + } + catch (Exception e) + { + Log("VFS: Mount error: {0}", e.Message); + + //maybe failed because of letter blocked: + char l = drive.Letter; + drive.Letter = ' '; + try + { + drive.Mount(); + drive.Letter = l; + } + catch + { + //connection error + drive.Letter = l; + Log("VFS: Mount error: {0}", e.Message); + return null; + } + } } if (drive == null) return null; @@ -117,7 +149,14 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); if (drive != null) - return GetSubSystemOperations(drive).OpenDirectory(fileName, info); + { + IDokanOperations ops = GetSubSystemOperations(drive); + if (ops == null) + { + return DokanError.ErrorError; + } + return ops.OpenDirectory(fileName, info); + } info.IsDirectory = true; @@ -141,18 +180,31 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).CreateDirectory(fileName, info); + return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { - Log("Cleanup:{0},Delete:{1}", info.Context, info.DeleteOnClose); + Log("VFS Cleanup:{0},Delete:{1}", info.Context, info.DeleteOnClose); + + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).Cleanup(fileName, info); + return DokanError.ErrorSuccess; } DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) { - Log("Close:{0}", info.Context); + Log("VFS Close:{0}", info.Context); + + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).CloseFile(fileName, info); return DokanError.ErrorSuccess; } @@ -161,6 +213,10 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).ReadFile(fileName, buffer, out bytesRead, offset, info); + bytesRead = 0; return DokanError.ErrorAccessDenied; } @@ -168,6 +224,10 @@ DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int byt DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).WriteFile(fileName, buffer, out bytesWritten, offset, info); + bytesWritten = 0; return DokanError.ErrorAccessDenied; } @@ -175,15 +235,25 @@ DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int by DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).FlushFileBuffers(fileName, info); + return DokanError.ErrorSuccess; } DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, DokanFileInfo info) { - Log("GetInfo:{0}:{1}", fileName, info.Context); + Log("VFS GetInfo:{0}:{1}", fileName, info.Context); + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).GetFileInformation(fileName, out fileInfo, info); + + fileInfo = new FileInformation(); fileInfo.FileName = fileName; + fileInfo.CreationTime = fileInfo.LastAccessTime = fileInfo.LastWriteTime = DateTime.Now; return DokanError.ErrorSuccess; } @@ -242,57 +312,153 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { - Log("TrySetAttributes:{0}\n{1};", fileName, attributes); + Log("VFS FindFiles:{0}", fileName); + + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).FindFilesWithPattern(fileName, pattern, out files, info); + + + files = new List(); + + string path = fileName.Substring(1);//cut leading \ + foreach (SftpDrive subdrive in _subsytems) + { + string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... + + if (path.Length > 0) //not root dir + { + if (path == mp) //this shoud not happend, because is managed by drive + { + Log("Error, mountpoint not in drives?"); + break; + } + + if (mp.IndexOf(path + '\\') == 0) //path is part of mount point =>implies=> length of path>mp + { + mp = mp.Substring(path.Length + 1); //cut the path + } + else + { + continue; + } + } + + int cuttmp = mp.IndexOf('\\'); + if (cuttmp > 0) // have submountpoint like mp1\mp2 + { + mp = mp.Substring(0, cuttmp); + } + + if (!files.Select(file => file.FileName).Contains(mp)) + { + FileInformation fi = new FileInformation(); + fi.FileName = mp; + fi.Attributes = FileAttributes.Directory | FileAttributes.Offline; + fi.CreationTime = DateTime.Now; + fi.LastWriteTime = DateTime.Now; + fi.LastAccessTime = DateTime.Now; + files.Add(fi); + } + } return DokanError.ErrorSuccess; } - DokanError IDokanOperations.SetFileTime(string filename, DateTime? creationTime, DateTime? lastAccessTime, + DokanError IDokanOperations.SetFileAttributes(string fileName, FileAttributes attributes, DokanFileInfo info) + { + Log("VFS TrySetAttributes:{0}\n{1};", fileName, attributes); + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).SetFileAttributes(fileName, attributes, info); + + return DokanError.ErrorAccessDenied; + } + + DokanError IDokanOperations.SetFileTime(string fileName, DateTime? creationTime, DateTime? lastAccessTime, DateTime? lastWriteTime, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).SetFileTime(fileName, creationTime, lastAccessTime, lastWriteTime, info); - return DokanError.ErrorSuccess; + return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).DeleteFile(fileName, info); + return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) { - Log("DeleteDirectory:{0}", fileName); + Log("VFS DeleteDirectory:{0}", fileName); + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).DeleteDirectory(fileName, info); + return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replace, DokanFileInfo info) { - Log("MoveFile |Name:{0} ,NewName:{3},Reaplace{4},IsDirectory:{1} ,Context:{2}", + Log("VFS MoveFile |Name:{0} ,NewName:{3},Reaplace{4},IsDirectory:{1} ,Context:{2}", oldName, info.IsDirectory, info.Context, newName, replace); + //todo: check newname? + SftpDrive drive = this.GetDriveByMountPoint(oldName, out oldName); + if (drive != null) + { + SftpDrive drive2 = this.GetDriveByMountPoint(newName, out newName); + if (drive2 != drive) + { + return DokanError.ErrorNotImplemented; + } + + return GetSubSystemOperations(drive).MoveFile(oldName, newName, replace, info); + } + return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.SetEndOfFile(string fileName, long length, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).SetEndOfFile(fileName,length, info); return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.SetAllocationSize(string fileName, long length, DokanFileInfo info) { - Log("SetSize"); + Log("VFS SetSize"); + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).SetAllocationSize(fileName, length, info); + return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.LockFile(string fileName, long offset, long length, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).LockFile(fileName, offset, length, info); + return DokanError.ErrorAccessDenied; } DokanError IDokanOperations.UnlockFile(string fileName, long offset, long length, DokanFileInfo info) { + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).UnlockFile(fileName, offset, length, info); return DokanError.ErrorAccessDenied; } @@ -326,20 +492,26 @@ DokanError IDokanOperations.GetVolumeInformation(out string volumeLabel, out Fil return DokanError.ErrorSuccess; } - DokanError IDokanOperations.GetFileSecurity(string filename, out FileSystemSecurity security, + DokanError IDokanOperations.GetFileSecurity(string fileName, out FileSystemSecurity security, AccessControlSections sections, DokanFileInfo info) { - Log("GetSecurrityInfo:{0}:{1}", filename, sections); + Log("VFS GetSecurrityInfo:{0}:{1}", fileName, sections); - security = null; + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).GetFileSecurity(fileName, out security, sections, info); + security = null; return DokanError.ErrorAccessDenied; } - DokanError IDokanOperations.SetFileSecurity(string filename, FileSystemSecurity security, + DokanError IDokanOperations.SetFileSecurity(string fileName, FileSystemSecurity security, AccessControlSections sections, DokanFileInfo info) { - Log("TrySetSecurity:{0}", filename); + Log("VFS TrySetSecurity:{0}", fileName); + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + if (drive != null) + return GetSubSystemOperations(drive).SetFileSecurity(fileName, security, sections, info); return DokanError.ErrorAccessDenied; } From e0f52330244ea4ca1fd4ac35d1464930e8c17e4f Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Fri, 25 Jul 2014 00:47:44 +0200 Subject: [PATCH 036/134] CloseFile repair --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 4 ++-- Sshfs/Sshfs/SftpFilesystem.cs | 12 +++++++++--- Sshfs/Sshfs/Sshfs.csproj | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 1cf922e..b7f3acc 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.5.8")] -[assembly: AssemblyFileVersion("0.1.5.8")] +[assembly: AssemblyVersion("0.1.5.10")] +[assembly: AssemblyFileVersion("0.1.5.10")] diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 11c0013..483dde4 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -463,9 +463,15 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) //Log("Close:{0}", info.Context); LogFSActionInit("CloseFile", fileName, (SftpContext)info.Context, ""); //???flush? - - (info.Context as SftpContext).Stream.Flush(); - (info.Context as SftpContext).Stream.Dispose(); + if (info.Context != null) + { + SftpContext context = (SftpContext) info.Context; + if (context.Stream != null) + { + (info.Context as SftpContext).Stream.Flush(); + (info.Context as SftpContext).Stream.Dispose(); + } + } _cache.Remove(fileName); return DokanError.ErrorSuccess; diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index b163f6a..a0cad58 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -34,8 +34,8 @@ true index.html false - 8 - 0.1.5.8 + 10 + 0.1.5.10 true true true From 7114f4b5a39571587f9e5e02ab3d62897d9dbcf1 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Fri, 25 Jul 2014 12:55:18 +0200 Subject: [PATCH 037/134] VFS log/error tweak --- Sshfs/Sshfs/SftpFilesystem.cs | 15 +++++++++++++-- Sshfs/Sshfs/VirtualFilesystem.cs | 4 +++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 483dde4..ea109b5 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -578,6 +578,7 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat var context = info.Context as SftpContext; SftpFileAttributes sftpFileAttributes; + string path = GetUnixPath(fileName); if (context != null) { @@ -585,11 +586,11 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat * Attributtes in streams are causing trouble with git. GetInfo returns wrong length if other context is writing. */ //sftpFileAttributes = context.Attributes; - sftpFileAttributes = GetAttributes(GetUnixPath(fileName)); + sftpFileAttributes = GetAttributes(path); } else { - string path = GetUnixPath(fileName); + sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; if (sftpFileAttributes == null) @@ -599,6 +600,16 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } } + if (sftpFileAttributes == null) + { + //try again? + //sftpFileAttributes = GetAttributes(path); + + LogFSActionError("FileInfo", fileName, (SftpContext)info.Context, "No such file - unable to get info"); + fileInfo = new FileInformation(); + return DokanError.ErrorFileNotFound; + + } fileInfo = new FileInformation diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index ce545b4..07cedc4 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -96,6 +96,8 @@ private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) if (path.IndexOf(drive.MountPoint)==0) { subfspath = path.Substring(drive.MountPoint.Length); + if (subfspath == "") + subfspath = "\\"; return drive; } } @@ -110,7 +112,7 @@ private IDokanOperations GetSubSystemOperations(SftpDrive drive) if (drive == null) return null; - if (drive.Status != DriveStatus.Mounted) + if ((drive.Status != DriveStatus.Mounted)&&(drive.Status != DriveStatus.Mounting)) { try { From 6286d9cd0c6008bff35fea247bdf96c88b0d57e0 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 26 Jul 2014 00:41:25 +0200 Subject: [PATCH 038/134] VFS FindfilePattern bug fix --- Sshfs/Sshfs/VirtualFilesystem.cs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 07cedc4..e4f3149 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -8,6 +8,7 @@ using System.Security.Principal; using System.Text; using DokanNet; +using System.Text.RegularExpressions; using FileAccess = DokanNet.FileAccess; @@ -73,6 +74,8 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS return DokanError.ErrorFileNotFound; } + Log("VFS CreateFile:{0} Mode:{1}", fileName, mode); + SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); if (drive != null) return GetSubSystemOperations(drive).CreateFile(fileName, access, share, mode, options, attributes, info); @@ -86,6 +89,7 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) { + Log("VFS Get Drive{0}", fileName); if (fileName.Length>1) { string path = fileName.Substring(1); @@ -98,12 +102,14 @@ private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) subfspath = path.Substring(drive.MountPoint.Length); if (subfspath == "") subfspath = "\\"; + Log("VFS Drive:{1} file:{0}", fileName,drive.Name); return drive; } } } } subfspath = fileName; + Log("VFS Drive:none file:{0}", fileName); return null; } @@ -166,15 +172,21 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) return DokanError.ErrorSuccess; string path = fileName.Substring(1);//cut leading \ - + foreach (SftpDrive subdrive in _subsytems) { string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... if (path == mp) + { + info.Context = mp; return DokanError.ErrorSuccess; + } - if (mp.IndexOf(path + '\\') == 0) //path is part of mount point + if (mp.IndexOf(path + '\\') == 0) + { //path is part of mount point + info.Context = mp; return DokanError.ErrorSuccess; + } } return DokanError.ErrorPathNotFound; @@ -358,7 +370,7 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName,string pattern, { FileInformation fi = new FileInformation(); fi.FileName = mp; - fi.Attributes = FileAttributes.Directory | FileAttributes.Offline; + fi.Attributes = FileAttributes.Directory /*| FileAttributes.Offline*/; fi.CreationTime = DateTime.Now; fi.LastWriteTime = DateTime.Now; fi.LastAccessTime = DateTime.Now; @@ -366,6 +378,16 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName,string pattern, } } + //apply pattern + List filteredfiles = new List(); + Regex repattern = new Regex("^" + Regex.Escape(pattern).Replace("\\*", ".*") + "$"); + foreach (FileInformation fi in files) + { + if (repattern.IsMatch(fi.FileName)) + filteredfiles.Add(fi); + } + files = filteredfiles; + return DokanError.ErrorSuccess; } From da2cd75347c32079c56ef1ce1019e0b718c530e7 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 26 Jul 2014 00:42:37 +0200 Subject: [PATCH 039/134] Caching improvements *Unified caching points *Unique cache key for unique systems --- Sshfs/Sshfs/SftpFilesystem.cs | 160 +++++++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 42 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index ea109b5..f647017 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -30,10 +30,6 @@ using Renci.SshNet.Common; using Renci.SshNet.Sftp; using FileAccess = DokanNet.FileAccess; - -//using System.Runtime.CompilerServices; -//[assembly: InternalsVisibleTo("WinSshFS, PublicKey=0024000004800000940000000602000000240000525341310004000001000100bb1df492d3d63bb4aedb73672b0c0694ad7838ea17c6d49685ef1a03301ae6de5a4b4b1795844de0ddab49254d05bd533b5a02c24a580346274b204b8975536ffe36b4e7bb8c82e419264c335682ad44861e99eef16e95ee978742064c9335283ecb6e2fd325ea97942a51a3fc1a7d9948dd919b0d4ad25148d5490cc37f85b4")] - using System.Text.RegularExpressions; @@ -143,12 +139,63 @@ protected override void Dispose(bool disposing) #endregion + #region Cache + + private void CacheAddAttr(string path, SftpFileAttributes attributes, DateTimeOffset expiration) + { + LogFSActionSuccess("CacheSetAttr", path, null, "Expir:{1} Size:{0}", attributes.Size, expiration); + _cache.Add(_volumeLabel+"A:"+path, attributes, expiration); + } + + private void CacheAddDir(string path, Tuple> dir, DateTimeOffset expiration) + { + LogFSActionSuccess("CacheSetDir", path, null, "Expir:{1} Count:{0}", dir.Item2.Count, expiration); + _cache.Add(_volumeLabel + "D:" + path, dir, expiration); + } + + private void CacheAddDiskInfo(Tuple info, DateTimeOffset expiration) + { + LogFSActionSuccess("CacheSetDInfo", _volumeLabel, null, "Expir:{0}", expiration); + _cache.Add(_volumeLabel + "I:", info, expiration); + } + + + private SftpFileAttributes CacheGetAttr(string path) + { + SftpFileAttributes attributes = _cache.Get(_volumeLabel + "A:" + path) as SftpFileAttributes; + LogFSActionSuccess("CacheGetAttr", path, null, "Size:{0}", (attributes == null) ? "miss" : attributes.Size.ToString()); + return attributes; + } + + private Tuple> CacheGetDir(string path) + { + Tuple> dir = _cache.Get(_volumeLabel + "D:" + path) as Tuple>; + LogFSActionSuccess("CacheGetDir", path, null, "Count:{0}", (dir==null) ? "miss" : dir.Item2.Count.ToString()); + return dir; + } + + private Tuple CacheGetDiskInfo() + { + Tuple info = _cache.Get(_volumeLabel + "I:") as Tuple; + LogFSActionSuccess("CacheGetDInfo", _volumeLabel, null, ""); + return info; + } + + private void CacheReset(string path) + { + LogFSActionSuccess("CacheReset", path, null, ""); + _cache.Remove(_volumeLabel + "A:" + path); + _cache.Remove(_volumeLabel + "D:" + path); + } + + #endregion + #region Methods private string GetUnixPath(string path) { // return String.Concat(_rootpath, path.Replace('\\', '/')); - return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/')); + return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/').Replace("//","/")); } @@ -166,7 +213,7 @@ private void Log(string format, params object[] arg) [Conditional("DEBUG")] private void LogFSAction(String action, String path, SftpContext context, string format, params object[] arg) { - Debug.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")+"\t"+context+"\t"+action+"\t"+path+"\t"); + Debug.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + context + "\t" + action + "\t" + _volumeLabel +"\t" +path + "\t"); Debug.WriteLine(format, arg); } @@ -244,7 +291,8 @@ private SftpFileAttributes GetAttributes(string path) private void InvalidateParentCache(string fileName) { int index = fileName.LastIndexOf('\\'); - _cache.Remove(index != 0 ? fileName.Substring(0, index) : "\\"); + //_cache.Remove(index != 0 ? fileName.Substring(0, index) : "\\"); + this.CacheReset( GetUnixPath(index != 0 ? fileName.Substring(0, index) : "\\")); } #endregion @@ -261,9 +309,12 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS return DokanError.ErrorFileNotFound; } + LogFSActionInit("OpenFile", fileName, (SftpContext)info.Context, "Mode:{0}", mode); + string path = GetUnixPath(fileName); // var sftpFileAttributes = GetAttributes(path); - var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + //var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + var sftpFileAttributes = this.CacheGetAttr(path); if (sftpFileAttributes == null) { @@ -271,10 +322,13 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS sftpFileAttributes = GetAttributes(path); if (sftpFileAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + else + { + LogFSActionOther("OpenFile", fileName, (SftpContext)info.Context, "get attributes failed"); + } } - - LogFSActionInit("OpenFile", fileName, (SftpContext)info.Context, "Mode:{0}", mode); /*Log("Open| Name:{0},\n Mode:{1},\n Share{2},\n Disp:{3},\n Flags{4},\n Attr:{5},\nPagingIO:{6} NoCache:{7} SynIO:{8}\n", fileName, access, share, mode, options, attributes, info.PagingIo, info.NoCache, info.SynchronousIo);*/ @@ -283,8 +337,8 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS case FileMode.Open: if (sftpFileAttributes != null) { - if (((uint) access & 0xe0000027) == 0 || sftpFileAttributes.IsDirectory) - //check if only wants to read attributes,security info or open directory + if (((uint)access & 0xe0000027) == 0 || sftpFileAttributes.IsDirectory) + //check if only wants to read attributes,security info or open directory { //Log("JustInfo:{0},{1}", fileName, sftpFileAttributes.IsDirectory); info.IsDirectory = sftpFileAttributes.IsDirectory; @@ -293,7 +347,11 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS return DokanError.ErrorSuccess; } } - else return DokanError.ErrorFileNotFound; + else + { + LogFSActionError("OpenFile", fileName, (SftpContext)info.Context, "File not found"); + return DokanError.ErrorFileNotFound; + } break; case FileMode.CreateNew: if (sftpFileAttributes != null) @@ -305,7 +363,8 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (sftpFileAttributes == null) return DokanError.ErrorFileNotFound; InvalidateParentCache(fileName); - _cache.Remove(path); + //_cache.Remove(path); + this.CacheReset(path); break; default: @@ -323,7 +382,8 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS catch (SshException) // Don't have access rights or try to read broken symlink { var ownerpath = path.Substring(0, path.LastIndexOf('/')); - var sftpPathAttributes = _cache.Get(ownerpath) as SftpFileAttributes; + //var sftpPathAttributes = _cache.Get(ownerpath) as SftpFileAttributes; + var sftpPathAttributes = CacheGetAttr(ownerpath); if (sftpPathAttributes == null) { @@ -331,7 +391,8 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS sftpPathAttributes = GetAttributes(ownerpath); if (sftpPathAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); else { //Log("Up directory must be created"); @@ -357,7 +418,8 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) string path = GetUnixPath(fileName); // var sftpFileAttributes = GetAttributes(GetUnixPath(fileName)); - var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + //var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + var sftpFileAttributes = CacheGetAttr(path); if (sftpFileAttributes == null) { @@ -365,7 +427,8 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) sftpFileAttributes = GetAttributes(path); if (sftpFileAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } @@ -383,10 +446,12 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) info.IsDirectory = true; info.Context = new SftpContext(sftpFileAttributes); - var dircahe = _cache.Get(fileName) as Tuple>; + //var dircahe = _cache.Get(fileName) as Tuple>; + var dircahe = CacheGetDir(path); if (dircahe != null && dircahe.Item1 != sftpFileAttributes.LastWriteTime) { - _cache.Remove(fileName); + //_cache.Remove(fileName); + CacheReset(path); } LogFSActionSuccess("OpenDir", fileName, (SftpContext)info.Context,""); return DokanError.ErrorSuccess; @@ -451,7 +516,8 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) _sftpSession.RequestRemove(path); } InvalidateParentCache(fileName); - _cache.Remove(path); + //_cache.Remove(path); + CacheReset(path); } LogFSActionSuccess("Cleanup", fileName, (SftpContext)info.Context, ""); @@ -472,7 +538,8 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) (info.Context as SftpContext).Stream.Dispose(); } } - _cache.Remove(fileName); + //_cache.Remove(fileName); + CacheReset(GetUnixPath(fileName)); return DokanError.ErrorSuccess; } @@ -563,7 +630,8 @@ DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info LogFSActionInit("FlushFile", fileName, (SftpContext)info.Context,""); (info.Context as SftpContext).Stream.Flush(); //git use this - _cache.Remove(fileName); + //_cache.Remove(fileName); + CacheReset(GetUnixPath(fileName)); LogFSActionSuccess("FlushFile", fileName, (SftpContext)info.Context, ""); return DokanError.ErrorSuccess; @@ -586,18 +654,24 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat * Attributtes in streams are causing trouble with git. GetInfo returns wrong length if other context is writing. */ //sftpFileAttributes = context.Attributes; - sftpFileAttributes = GetAttributes(path); + //test: + if (context.Stream != null) + sftpFileAttributes = GetAttributes(path); + else + sftpFileAttributes = context.Attributes; } else { - sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + //sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; + sftpFileAttributes = CacheGetAttr(path); if (sftpFileAttributes == null) { sftpFileAttributes = GetAttributes(path); if (sftpFileAttributes != null) - _cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } } if (sftpFileAttributes == null) @@ -779,8 +853,10 @@ var file in sftpFiles.Where( pair => !pair.Value.IsSymbolicLink)) { - _cache.Set(GetUnixPath(String.Format("{0}{1}", fileName, file.Key)), file.Value, - DateTimeOffset.UtcNow.AddSeconds(timeout)); + /*_cache.Set(GetUnixPath(String.Format("{0}{1}", fileName, file.Key)), file.Value, + DateTimeOffset.UtcNow.AddSeconds(timeout));*/ + CacheAddAttr(GetUnixPath(String.Format("{0}\\{1}", fileName , file.Key)), file.Value, + DateTimeOffset.UtcNow.AddSeconds(timeout)); } } @@ -789,7 +865,7 @@ var file in try { - _cache.Add(fileName, new Tuple>( + CacheAddDir( GetUnixPath(fileName), new Tuple>( (info.Context as SftpContext).Attributes.LastWriteTime, files), DateTimeOffset.UtcNow.AddSeconds(Math.Max(_attributeCacheTimeout, @@ -816,7 +892,7 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName, string searchP } //get files from cache || load them - var dircache = _cache.Get(fileName) as Tuple>; + var dircache = CacheGetDir(GetUnixPath(fileName)); if (dircache != null) { files = (dircache).Item2; @@ -831,7 +907,6 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName, string searchP //apply pattern List filteredfiles = new List(); - Regex repattern = new Regex("^"+Regex.Escape(searchPattern).Replace("\\*", ".*")+"$"); foreach(FileInformation fi in files){ if (repattern.IsMatch(fi.FileName)) @@ -878,13 +953,14 @@ DokanError IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) string parentPath = GetUnixPath(fileName.Substring(0, fileName.LastIndexOf('\\'))); - var sftpFileAttributes = _cache.Get(parentPath) as SftpFileAttributes; + var sftpFileAttributes = CacheGetAttr(parentPath); if (sftpFileAttributes == null) { sftpFileAttributes = GetAttributes(parentPath); if (sftpFileAttributes != null) - _cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + //_cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + CacheAddAttr(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } @@ -904,13 +980,13 @@ DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) string parentPath = GetUnixPath(fileName.Substring(0, fileName.LastIndexOf('\\'))); - var sftpFileAttributes = _cache.Get(parentPath) as SftpFileAttributes; + var sftpFileAttributes = CacheGetAttr(parentPath); if (sftpFileAttributes == null) { sftpFileAttributes = GetAttributes(parentPath); if (sftpFileAttributes != null) - _cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); + CacheAddAttr(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } @@ -921,7 +997,7 @@ DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) LogFSActionError("DeleteDir", fileName, (SftpContext)info.Context, "Access denied"); return DokanError.ErrorAccessDenied; } - var dircache = _cache.Get(fileName) as Tuple>; + var dircache = CacheGetDir(GetUnixPath(fileName)); if (dircache != null) { //Log("DelateCacheHit:{0}", fileName); @@ -980,7 +1056,7 @@ DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replac _sftpSession.RequestRename(oldpath, newpath); InvalidateParentCache(oldName); InvalidateParentCache(newName); - _cache.Remove(oldpath); + CacheReset(oldpath); } catch (SftpPermissionDeniedException) { @@ -1013,7 +1089,7 @@ DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replac InvalidateParentCache(oldName); InvalidateParentCache(newName); - _cache.Remove(oldpath); + CacheReset(oldpath); } catch (SftpPermissionDeniedException) { @@ -1069,7 +1145,7 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, Log("GetDiskFreeSpace"); - var diskSpaceInfo = _cache.Get(_volumeLabel) as Tuple; + var diskSpaceInfo = CacheGetDiskInfo(); if (diskSpaceInfo != null) { @@ -1107,8 +1183,8 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, } } - _cache.Add(_volumeLabel, new Tuple(free, total, used), - DateTimeOffset.UtcNow.AddMinutes(3)); + CacheAddDiskInfo(new Tuple(free, total, used), + DateTimeOffset.UtcNow.AddMinutes(3)); } LogFSActionSuccess("GetDiskFreeSpace", this._volumeLabel, (SftpContext)info.Context, "Free:{0} Total:{1} Used:{2}", free, total, used); return DokanError.ErrorSuccess; From 5a62ecf7b8a1287a354c108a8104b4454deee9ec Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 26 Jul 2014 01:11:10 +0200 Subject: [PATCH 040/134] Mountpoint VFS CreateFile fix --- Sshfs/Sshfs/MainForm.cs | 2 +- Sshfs/Sshfs/VirtualFilesystem.cs | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index d99917c..915a181 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -367,7 +367,7 @@ private void listView_ItemSelectionChanged(object sender, ListViewItemSelectionC muButton.Text = drive.Status == DriveStatus.Mounted ? "Unmount" : "Mount"; muButton.Image = drive.Status == DriveStatus.Mounted ? Resources.unmount : Resources.mount; muButton.Enabled = (drive.Status == DriveStatus.Unmounted || drive.Status == DriveStatus.Mounted); - mountPointBox.Text = drive.MountPoint; + mountPointBox.Text = drive.MountPoint.Replace("/", "\\");//fix unix / to Windows standard } } diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index e4f3149..ca42739 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -80,11 +80,27 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (drive != null) return GetSubSystemOperations(drive).CreateFile(fileName, access, share, mode, options, attributes, info); - //Todo: check against mountpoints - info.IsDirectory = true; - info.Context = this; + //check against mountpoints if virtual dir exists - return DokanError.ErrorSuccess; + string path = fileName.Substring(1); + if (path == "") + { + info.IsDirectory = true; + return DokanError.ErrorSuccess; + } + foreach (SftpDrive drive2 in this._subsytems) + { + if (drive2.MountPoint.Length > 0) + { + if (drive2.MountPoint.IndexOf(path) == 0) + { + info.IsDirectory = true; + return DokanError.ErrorSuccess; + } + } + } + + return DokanError.ErrorFileNotFound; } private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) From 6da78a85d716672ad377704ed267293fcea5bc15 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 26 Jul 2014 03:30:07 +0200 Subject: [PATCH 041/134] VFS dir attribute bug + logging improve --- Sshfs/Sshfs/SftpFilesystem.cs | 11 +- Sshfs/Sshfs/VirtualFilesystem.cs | 179 ++++++++++++++++++++++++++----- 2 files changed, 162 insertions(+), 28 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index f647017..b7d7467 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -198,7 +198,7 @@ private string GetUnixPath(string path) return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/').Replace("//","/")); } - + #region Logging [Conditional("DEBUG")] private void Log(string format, params object[] arg) { @@ -206,14 +206,16 @@ private void Log(string format, params object[] arg) { Console.WriteLine(format, arg); } + Debug.AutoFlush = false; Debug.Write(DateTime.Now.ToLongTimeString() + " "); Debug.WriteLine(format, arg); + Debug.Flush(); } [Conditional("DEBUG")] private void LogFSAction(String action, String path, SftpContext context, string format, params object[] arg) { - Debug.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + context + "\t" + action + "\t" + _volumeLabel +"\t" +path + "\t"); + Debug.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + (context == null ? "[-------]" : context.ToString()) + "\t" + action + "\t" + _volumeLabel +"\t" +path + "\t"); Debug.WriteLine(format, arg); } @@ -237,6 +239,8 @@ private void LogFSActionOther(String action, String path, SftpContext context, s { LogFSAction(action + "|", path, context, format, arg); } + + #endregion private IEnumerable GetUserGroupsIds() { using (var cmd = new SshCommand(Session, "id -G ")) @@ -910,7 +914,10 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName, string searchP Regex repattern = new Regex("^"+Regex.Escape(searchPattern).Replace("\\*", ".*")+"$"); foreach(FileInformation fi in files){ if (repattern.IsMatch(fi.FileName)) + { filteredfiles.Add(fi); + LogFSActionOther("FindFilesPat", fileName, (SftpContext)info.Context, "Result:{0}", fi.FileName); + } } files = filteredfiles; /*not sure, whats right... if (files.Count == 0) diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index ca42739..ae96768 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -48,7 +48,10 @@ internal void RemoveSubFS(SftpDrive sftpDrive) _subsytems.Remove(sftpDrive); } + #endregion + + #region Logging [Conditional("DEBUG")] private void Log(string format, params object[] arg) { @@ -56,12 +59,44 @@ private void Log(string format, params object[] arg) { Console.WriteLine(format, arg); } + + Debug.AutoFlush = false; Debug.Write(DateTime.Now.ToLongTimeString() + " "); Debug.WriteLine(format, arg); + Debug.Flush(); + } + + [Conditional("DEBUG")] + private void LogFSAction(String action, String path, SftpDrive subsystem, string format, params object[] arg) + { + Debug.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + "[--VFS--]" + "\t" + action + "\t" + ( subsystem!=null? subsystem.Name : "- ") + "\t" + path + "\t"); + Debug.WriteLine(format, arg); + } + + [Conditional("DEBUG")] + private void LogFSActionInit(String action, String path, SftpDrive subsystem, string format, params object[] arg) + { + LogFSAction(action + "^", path, subsystem, format, arg); + } + [Conditional("DEBUG")] + private void LogFSActionSuccess(String action, String path, SftpDrive subsystem, string format, params object[] arg) + { + LogFSAction(action + "$", path, subsystem, format, arg); + } + [Conditional("DEBUG")] + private void LogFSActionError(String action, String path, SftpDrive subsystem, string format, params object[] arg) + { + LogFSAction(action + "!", path, subsystem, format, arg); + } + [Conditional("DEBUG")] + private void LogFSActionOther(String action, String path, SftpDrive subsystem, string format, params object[] arg) + { + LogFSAction(action + "|", path, subsystem, format, arg); } #endregion + #region DokanOperations DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, @@ -74,11 +109,13 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS return DokanError.ErrorFileNotFound; } - Log("VFS CreateFile:{0} Mode:{1}", fileName, mode); - SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + LogFSActionInit("OpenFile", fileName, drive, "Mode:{0}", mode); if (drive != null) + { + LogFSActionSuccess("OpenFile", fileName, drive, "Mode:{0} NonVFS", mode); return GetSubSystemOperations(drive).CreateFile(fileName, access, share, mode, options, attributes, info); + } //check against mountpoints if virtual dir exists @@ -86,6 +123,8 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (path == "") { info.IsDirectory = true; + info.Context = null; + LogFSActionSuccess("OpenFile", fileName, null, "VFS root"); return DokanError.ErrorSuccess; } foreach (SftpDrive drive2 in this._subsytems) @@ -95,17 +134,23 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (drive2.MountPoint.IndexOf(path) == 0) { info.IsDirectory = true; + info.Context = drive2; + LogFSActionSuccess("OpenFile", fileName, drive2, "VFS (sub)mountpoint"); return DokanError.ErrorSuccess; } } } + //pathnotfound detection? + + LogFSActionError("OpenFile", fileName, null, "File not found"); return DokanError.ErrorFileNotFound; } private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) { - Log("VFS Get Drive{0}", fileName); + LogFSActionInit("LookupMP", fileName, null, ""); + if (fileName.Length>1) { string path = fileName.Substring(1); @@ -118,14 +163,15 @@ private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) subfspath = path.Substring(drive.MountPoint.Length); if (subfspath == "") subfspath = "\\"; - Log("VFS Drive:{1} file:{0}", fileName,drive.Name); + LogFSActionSuccess("LookupMP", fileName, drive, "Subsystem path: {0}",subfspath); return drive; } } } } subfspath = fileName; - Log("VFS Drive:none file:{0}", fileName); + + LogFSActionSuccess("LookupMP", fileName, null, "VFS path"); return null; } @@ -169,23 +215,28 @@ private IDokanOperations GetSubSystemOperations(SftpDrive drive) DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { - Log("VFS OpenDir:{0}", fileName); - SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + LogFSActionInit("OpenDir", fileName, drive, ""); + if (drive != null) { IDokanOperations ops = GetSubSystemOperations(drive); if (ops == null) { + LogFSActionError("OpenDir", fileName, drive, "Strange error, ops not found"); return DokanError.ErrorError; } + LogFSActionSuccess("OpenDir", fileName, drive, "Found, subsytem"); return ops.OpenDirectory(fileName, info); } info.IsDirectory = true; if (fileName.Length == 1) //root dir + { + LogFSActionSuccess("OpenDir", fileName, drive, "Found, VFS root"); return DokanError.ErrorSuccess; + } string path = fileName.Substring(1);//cut leading \ @@ -194,17 +245,19 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... if (path == mp) { - info.Context = mp; + info.Context = subdrive; + LogFSActionSuccess("OpenDir", fileName, drive, "Found, final mountpoint"); return DokanError.ErrorSuccess; } if (mp.IndexOf(path + '\\') == 0) { //path is part of mount point - info.Context = mp; + info.Context = subdrive; + LogFSActionSuccess("OpenDir", fileName, drive, "Found, part of mountpoint"); return DokanError.ErrorSuccess; } } - + LogFSActionError("OpenDir", fileName, drive, "Path not found"); return DokanError.ErrorPathNotFound; } @@ -219,23 +272,41 @@ DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { - Log("VFS Cleanup:{0},Delete:{1}", info.Context, info.DeleteOnClose); - SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + LogFSActionInit("Cleanup", fileName, drive, ""); if (drive != null) + { + LogFSActionSuccess("Cleanup", fileName, drive, "nonVFS clean"); return GetSubSystemOperations(drive).Cleanup(fileName, info); + } + if (info.Context != null) + { + drive = info.Context as SftpDrive; + info.Context = null; + } + + LogFSActionSuccess("Cleanup", fileName, drive, "VFS clean"); return DokanError.ErrorSuccess; } DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) { - Log("VFS Close:{0}", info.Context); - SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + LogFSActionInit("CloseFile", fileName, drive, ""); if (drive != null) + { + LogFSActionSuccess("CloseFile", fileName, drive, "NonVFS close"); return GetSubSystemOperations(drive).CloseFile(fileName, info); + } + if (info.Context != null) + { + drive = info.Context as SftpDrive; + info.Context = null; + } + + LogFSActionSuccess("CloseFile", fileName, drive, "VFS close"); return DokanError.ErrorSuccess; } @@ -275,26 +346,75 @@ DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, DokanFileInfo info) { - Log("VFS GetInfo:{0}:{1}", fileName, info.Context); SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + LogFSActionInit("FileInfo", fileName, drive, ""); if (drive != null) + { + LogFSActionSuccess("FileInfo", fileName, drive, "NonVFS"); return GetSubSystemOperations(drive).GetFileInformation(fileName, out fileInfo, info); + } + + fileInfo = new FileInformation + { + Attributes = + FileAttributes.NotContentIndexed | FileAttributes.Directory, + FileName = Path.GetFileName(fileName), //String.Empty, + // GetInfo info doesn't use it maybe for sorting . + CreationTime = DateTime.Now, + LastAccessTime = DateTime.Now, + LastWriteTime = DateTime.Now, + Length = 4096 + }; + + if (fileName.Length == 1) + { //root dir + LogFSActionSuccess("FileInfo", fileName, drive, "root info"); + return DokanError.ErrorSuccess; + } + + string path = fileName.Substring(1);//cut leading \ + + if (info.Context != null) + { + drive = info.Context as SftpDrive; + LogFSActionSuccess("FileInfo", fileName, drive, "from context"); + return DokanError.ErrorSuccess; + } + + foreach (SftpDrive subdrive in _subsytems) + { + string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... + if (path == mp) + { + info.Context = mp; + //fileInfo.FileName = path.Substring(path.LastIndexOf("\\")+1); + LogFSActionSuccess("FileInfo", fileName, drive, "final mountpoint"); + return DokanError.ErrorSuccess; + } + + if (mp.IndexOf(path + '\\') == 0) + { //path is part of mount point + //fileInfo.FileName = path.Substring(path.LastIndexOf("\\") + 1); + LogFSActionSuccess("FileInfo", fileName, drive, "part of mountpoint"); + return DokanError.ErrorSuccess; + } + } + + LogFSActionError("FileInfo", fileName, drive, "path not found"); + return DokanError.ErrorPathNotFound; - fileInfo = new FileInformation(); - fileInfo.FileName = fileName; - fileInfo.CreationTime = fileInfo.LastAccessTime = fileInfo.LastWriteTime = DateTime.Now; - return DokanError.ErrorSuccess; } DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { - Log("VFS FindFiles:{0}", fileName); - SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + LogFSActionError("FindFiles", fileName, drive, "!? not using FindFilesWithPattern !?"); + if (drive != null) return GetSubSystemOperations(drive).FindFiles(fileName, out files, info); + //this shoud be never called files = new List(); @@ -344,11 +464,14 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { - Log("VFS FindFiles:{0}", fileName); - SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); + LogFSActionInit("FindFilesPat", fileName, drive, ""); + if (drive != null) + { + LogFSActionSuccess("FindFilesPat", fileName, drive, "NonVFS"); return GetSubSystemOperations(drive).FindFilesWithPattern(fileName, pattern, out files, info); + } files = new List(); @@ -362,7 +485,7 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName,string pattern, { if (path == mp) //this shoud not happend, because is managed by drive { - Log("Error, mountpoint not in drives?"); + LogFSActionError("FindFilesPat", fileName, drive, "mountpoint not in drives?"); break; } @@ -386,7 +509,7 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName,string pattern, { FileInformation fi = new FileInformation(); fi.FileName = mp; - fi.Attributes = FileAttributes.Directory /*| FileAttributes.Offline*/; + fi.Attributes = FileAttributes.Directory | FileAttributes.Offline; fi.CreationTime = DateTime.Now; fi.LastWriteTime = DateTime.Now; fi.LastAccessTime = DateTime.Now; @@ -400,10 +523,14 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName,string pattern, foreach (FileInformation fi in files) { if (repattern.IsMatch(fi.FileName)) + { filteredfiles.Add(fi); + LogFSActionOther("FindFilesPat", fileName, drive, "Result:{0}", fi.FileName); + } } files = filteredfiles; + LogFSActionError("FindFilesPat", fileName, drive, "Pattern:{0} Count:{1}", pattern, files.Count); return DokanError.ErrorSuccess; } @@ -518,7 +645,7 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, DokanError IDokanOperations.GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features, out string filesystemName, DokanFileInfo info) { - Log("GetVolumeInformation"); + LogFSActionSuccess("DiskInfo", _volumeLabel, null, ""); volumeLabel = _volumeLabel; From 20aba44013c63ed6d601b131612d79596f4ad6fc Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 26 Jul 2014 03:31:06 +0200 Subject: [PATCH 042/134] 1.5.11 --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index b7f3acc..04cc8ad 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.5.10")] -[assembly: AssemblyFileVersion("0.1.5.10")] +[assembly: AssemblyVersion("0.1.5.11")] +[assembly: AssemblyFileVersion("0.1.5.11")] From b31a2c7f243adfb20b06070a022eff821c215779 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 27 Jul 2014 01:24:24 +0200 Subject: [PATCH 043/134] dir caching speed repair --- Sshfs/Sshfs/SftpFilesystem.cs | 4 ++++ Sshfs/Sshfs/app.config | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index b7d7467..5d40ccf 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -543,7 +543,11 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) } } //_cache.Remove(fileName); + + /* cache reset for dir close is not good idea, for files its probably also nto problem CacheReset(GetUnixPath(fileName)); + */ + return DokanError.ErrorSuccess; } diff --git a/Sshfs/Sshfs/app.config b/Sshfs/Sshfs/app.config index af3c6a6..8ddde00 100644 --- a/Sshfs/Sshfs/app.config +++ b/Sshfs/Sshfs/app.config @@ -5,6 +5,18 @@
+ + + + + + + + + From 9d5f16c7c55cfcb5e245444b626f73b9380d9d21 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 27 Jul 2014 01:46:54 +0200 Subject: [PATCH 044/134] VFS free disk space info support --- Sshfs/Sshfs/VirtualFilesystem.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index ae96768..930b67c 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -24,6 +24,7 @@ internal sealed class VirtualFilesystem : IDokanOperations private bool _debugMode = false; private readonly List _subsytems = new List(); + private SftpDrive lastActiveSubsytem; #endregion @@ -220,6 +221,8 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) if (drive != null) { + lastActiveSubsytem = drive; + IDokanOperations ops = GetSubSystemOperations(drive); if (ops == null) { @@ -230,6 +233,7 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) return ops.OpenDirectory(fileName, info); } + lastActiveSubsytem = null; info.IsDirectory = true; if (fileName.Length == 1) //root dir @@ -632,7 +636,16 @@ DokanError IDokanOperations.UnlockFile(string fileName, long offset, long length DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, out long used, DokanFileInfo info) { - Log("GetDiskFreeSpace"); + + if (lastActiveSubsytem != null) + { + IDokanOperations ops = GetSubSystemOperations(lastActiveSubsytem); + if (ops != null) + { + return ops.GetDiskFreeSpace(out free, out total, out used, info); + } + } + free = 0; total = 1024; From cf25f56d14682eaf9448a5f2e5f69e2212b7d392 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 27 Jul 2014 02:42:44 +0200 Subject: [PATCH 045/134] Readonly flag to group rights mapping Bug in total commander, state not up to date after change, but only in properties window.. --- Sshfs/Sshfs/SftpFilesystem.cs | 44 +++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 5d40ccf..2a36e5a 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -281,6 +281,11 @@ private bool UserCanExecute(SftpFileAttributes attributes) attributes.OthersCanExecute)); } + private bool GroupCanControl(SftpFileAttributes attributes) + { + return attributes.GroupCanWrite || attributes.OthersCanWrite; + } + private SftpFileAttributes GetAttributes(string path) { var sftpLStatAttributes = _sftpSession.RequestLStat(path, true); @@ -720,7 +725,7 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat fileInfo.Attributes |= FileAttributes.Hidden; } - if (!UserCanWrite(sftpFileAttributes)) + if (!GroupCanControl(sftpFileAttributes)) { fileInfo.Attributes |= FileAttributes.ReadOnly; } @@ -834,7 +839,7 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList Date: Sun, 27 Jul 2014 21:29:50 +0200 Subject: [PATCH 046/134] ARCHIVE = group right Archive is ON if group have same rights as owner (mask AAB) If set archive ON, group get same rights as owner (goal AAB) If set archive OF, group get same rights as everyone (goal ABB) --- Sshfs/Sshfs/Program.cs | 13 +- Sshfs/Sshfs/SftpDrive.cs | 4 +- Sshfs/Sshfs/SftpFilesystem.cs | 222 +++++++++++++++++-------------- Sshfs/Sshfs/VirtualFilesystem.cs | 61 ++++++--- 4 files changed, 178 insertions(+), 122 deletions(-) diff --git a/Sshfs/Sshfs/Program.cs b/Sshfs/Sshfs/Program.cs index 25d55b4..6fb645e 100644 --- a/Sshfs/Sshfs/Program.cs +++ b/Sshfs/Sshfs/Program.cs @@ -9,7 +9,9 @@ namespace Sshfs { internal static class Program - { + { + static SftpManagerApplication app; + /// /// The main entry point for the application. /// @@ -18,10 +20,13 @@ private static void Main(params string[] args ) { #if DEBUG - Debug.AutoFlush = true; - Debug.Listeners.Add(new DelimitedListTraceListener(String.Format("{0}\\log{1:yyyy-MM-dd-HH-mm-ss}.txt",Environment.CurrentDirectory,DateTime.Now), "debug")); + Debug.AutoFlush = true; + Debug.Listeners.Clear(); + //Debug.Listeners.Add(new DelimitedListTraceListener(String.Format("{0}\\log{1:yyyy-MM-dd-HH-mm-ss}.txt",Environment.CurrentDirectory,DateTime.Now), "debug")); + Debug.Listeners.Add(new DelimitedListTraceListener(Environment.CurrentDirectory+"\\last.log", "debug")); #endif - new SftpManagerApplication().Run(args); + SftpManagerApplication app = new SftpManagerApplication(); + app.Run(args); } } } \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index 75a3199..94e33ad 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -97,7 +97,7 @@ private void OnStatusChanged(EventArgs args) private void SetupFilesystem() { - Debug.WriteLine("SetupFilesystem"); + Debug.WriteLine("SetupFilesystem {0},{1},{2},{3}",Host,Port,Username,ConnectionType.ToString()); ConnectionInfo info; switch (ConnectionType) @@ -176,7 +176,7 @@ private void MountLoop() [MethodImpl(MethodImplOptions.Synchronized)] public void Mount() { - Debug.WriteLine("Mount"); + //Debug.WriteLine("Mount"); if (Directory.GetLogicalDrives().Any(drive=>drive[0]==Letter)) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 2a36e5a..01d7813 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -139,6 +139,50 @@ protected override void Dispose(bool disposing) #endregion + #region Logging + [Conditional("DEBUG")] + private void Log(string format, params object[] arg) + { + if (_debugMode) + { + Console.WriteLine(format, arg); + } + Debug.AutoFlush = false; + Debug.Write(DateTime.Now.ToLongTimeString() + " "); + Debug.WriteLine(format, arg); + Debug.Flush(); + } + + [Conditional("DEBUG")] + private void LogFSAction(String action, String path, SftpContext context, string format, params object[] arg) + { + Debug.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + (context == null ? "[-------]" : context.ToString()) + "\t" + action + "\t" + _volumeLabel + "\t" + path + "\t"); + Debug.WriteLine(format, arg); + } + + [Conditional("DEBUG")] + private void LogFSActionInit(String action, String path, SftpContext context, string format, params object[] arg) + { + LogFSAction(action + "^", path, context, format, arg); + } + [Conditional("DEBUG")] + private void LogFSActionSuccess(String action, String path, SftpContext context, string format, params object[] arg) + { + LogFSAction(action + "$", path, context, format, arg); + } + [Conditional("DEBUG")] + private void LogFSActionError(String action, String path, SftpContext context, string format, params object[] arg) + { + LogFSAction(action + "!", path, context, format, arg); + } + [Conditional("DEBUG")] + private void LogFSActionOther(String action, String path, SftpContext context, string format, params object[] arg) + { + LogFSAction(action + "|", path, context, format, arg); + } + + #endregion + #region Cache private void CacheAddAttr(string path, SftpFileAttributes attributes, DateTimeOffset expiration) @@ -163,7 +207,7 @@ private void CacheAddDiskInfo(Tuple info, DateTimeOffset expir private SftpFileAttributes CacheGetAttr(string path) { SftpFileAttributes attributes = _cache.Get(_volumeLabel + "A:" + path) as SftpFileAttributes; - LogFSActionSuccess("CacheGetAttr", path, null, "Size:{0}", (attributes == null) ? "miss" : attributes.Size.ToString()); + LogFSActionSuccess("CacheGetAttr", path, null, "Size:{0} Group write:{1} ", (attributes == null) ? "miss" : attributes.Size.ToString(), (attributes == null ? "miss" : attributes.GroupCanWrite.ToString()) ); return attributes; } @@ -188,6 +232,21 @@ private void CacheReset(string path) _cache.Remove(_volumeLabel + "D:" + path); } + private void CacheResetParent(string path) + { + int index = path.LastIndexOf('/'); + if (index > 0) + { + //_cache.Remove(index != 0 ? fileName.Substring(0, index) : "\\"); + this.CacheReset(path.Substring(0, index)); + } + else + { + this.CacheReset("/"); + } + } + + #endregion #region Methods @@ -198,49 +257,6 @@ private string GetUnixPath(string path) return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/').Replace("//","/")); } - #region Logging - [Conditional("DEBUG")] - private void Log(string format, params object[] arg) - { - if (_debugMode) - { - Console.WriteLine(format, arg); - } - Debug.AutoFlush = false; - Debug.Write(DateTime.Now.ToLongTimeString() + " "); - Debug.WriteLine(format, arg); - Debug.Flush(); - } - - [Conditional("DEBUG")] - private void LogFSAction(String action, String path, SftpContext context, string format, params object[] arg) - { - Debug.Write(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + (context == null ? "[-------]" : context.ToString()) + "\t" + action + "\t" + _volumeLabel +"\t" +path + "\t"); - Debug.WriteLine(format, arg); - } - - [Conditional("DEBUG")] - private void LogFSActionInit(String action, String path, SftpContext context, string format, params object[] arg) - { - LogFSAction(action+"^", path, context, format, arg); - } - [Conditional("DEBUG")] - private void LogFSActionSuccess(String action, String path, SftpContext context, string format, params object[] arg) - { - LogFSAction(action + "$", path, context, format, arg); - } - [Conditional("DEBUG")] - private void LogFSActionError(String action, String path, SftpContext context, string format, params object[] arg) - { - LogFSAction(action + "!", path, context, format, arg); - } - [Conditional("DEBUG")] - private void LogFSActionOther(String action, String path, SftpContext context, string format, params object[] arg) - { - LogFSAction(action + "|", path, context, format, arg); - } - - #endregion private IEnumerable GetUserGroupsIds() { using (var cmd = new SshCommand(Session, "id -G ")) @@ -281,9 +297,11 @@ private bool UserCanExecute(SftpFileAttributes attributes) attributes.OthersCanExecute)); } - private bool GroupCanControl(SftpFileAttributes attributes) + private bool GroupRightsSameAsOwner(SftpFileAttributes attributes) { - return attributes.GroupCanWrite || attributes.OthersCanWrite; + return (attributes.GroupCanWrite == attributes.OwnerCanWrite) + && (attributes.GroupCanRead == attributes.OwnerCanRead) + && (attributes.GroupCanExecute == attributes.OwnerCanExecute); } private SftpFileAttributes GetAttributes(string path) @@ -297,13 +315,6 @@ private SftpFileAttributes GetAttributes(string path) return sftpStatAttributes ?? sftpLStatAttributes; } - private void InvalidateParentCache(string fileName) - { - int index = fileName.LastIndexOf('\\'); - //_cache.Remove(index != 0 ? fileName.Substring(0, index) : "\\"); - this.CacheReset( GetUnixPath(index != 0 ? fileName.Substring(0, index) : "\\")); - } - #endregion #region DokanOperations @@ -366,18 +377,18 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (sftpFileAttributes != null) return DokanError.ErrorAlreadyExists; - InvalidateParentCache(fileName); // cache invalidate + CacheResetParent(path); break; case FileMode.Truncate: if (sftpFileAttributes == null) return DokanError.ErrorFileNotFound; - InvalidateParentCache(fileName); + CacheResetParent(path); //_cache.Remove(path); this.CacheReset(path); break; default: - InvalidateParentCache(fileName); + CacheResetParent(path); break; } //Log("NotJustInfo:{0}-{1}", info.Context, mode); @@ -474,11 +485,11 @@ DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) //Log("CreateDir:{0}", fileName); LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context, ""); - + string path = GetUnixPath(fileName); try { - _sftpSession.RequestMkDir(GetUnixPath(fileName)); - InvalidateParentCache(fileName); //invalidate dircahe of the parent + _sftpSession.RequestMkDir(path); + CacheResetParent(path); } catch (SftpPermissionDeniedException) { @@ -524,9 +535,8 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { _sftpSession.RequestRemove(path); } - InvalidateParentCache(fileName); - //_cache.Remove(path); CacheReset(path); + CacheResetParent(path); } LogFSActionSuccess("Cleanup", fileName, (SftpContext)info.Context, ""); @@ -725,9 +735,9 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat fileInfo.Attributes |= FileAttributes.Hidden; } - if (!GroupCanControl(sftpFileAttributes)) + if (GroupRightsSameAsOwner(sftpFileAttributes)) { - fileInfo.Attributes |= FileAttributes.ReadOnly; + fileInfo.Attributes |= FileAttributes.Archive; } if (_useOfflineAttribute) { @@ -839,13 +849,12 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList Date: Sun, 27 Jul 2014 22:59:48 +0200 Subject: [PATCH 047/134] VFS GUI and config saving --- Sshfs/Sshfs/MainForm.Designer.cs | 51 +++++-- Sshfs/Sshfs/MainForm.cs | 76 +++++++--- Sshfs/Sshfs/MainForm.resx | 198 ++++++++++++------------- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 4 +- Sshfs/Sshfs/Sshfs.csproj | 4 +- Sshfs/Sshfs/Utilities.cs | 36 +++++ Sshfs/Sshfs/VirtualDrive.cs | 34 ++++- Sshfs/Sshfs/VirtualFilesystem.cs | 4 + 8 files changed, 265 insertions(+), 142 deletions(-) diff --git a/Sshfs/Sshfs/MainForm.Designer.cs b/Sshfs/Sshfs/MainForm.Designer.cs index 7a0fff8..076ffe1 100644 --- a/Sshfs/Sshfs/MainForm.Designer.cs +++ b/Sshfs/Sshfs/MainForm.Designer.cs @@ -66,6 +66,7 @@ private void InitializeComponent() this.muButton = new System.Windows.Forms.Button(); this.saveButton = new System.Windows.Forms.Button(); this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.buttonVFSMount = new System.Windows.Forms.Button(); this.virtualDriveCombo = new System.Windows.Forms.ComboBox(); this.label9 = new System.Windows.Forms.Label(); this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); @@ -107,8 +108,8 @@ private void InitializeComponent() this.tableLayoutPanel1.RowCount = 3; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 41F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(583, 418); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 41F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(583, 461); this.tableLayoutPanel1.TabIndex = 0; // // fieldsPanel @@ -116,7 +117,7 @@ private void InitializeComponent() this.fieldsPanel.ColumnCount = 3; this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 26.10063F)); this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 73.89937F)); - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 10F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 12F)); this.fieldsPanel.Controls.Add(this.nameBox, 1, 0); this.fieldsPanel.Controls.Add(this.label1, 0, 0); this.fieldsPanel.Controls.Add(this.hostBox, 1, 1); @@ -151,7 +152,7 @@ private void InitializeComponent() this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 45F)); this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.fieldsPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.fieldsPanel.Size = new System.Drawing.Size(327, 339); + this.fieldsPanel.Size = new System.Drawing.Size(327, 371); this.fieldsPanel.TabIndex = 3; // // nameBox @@ -393,7 +394,7 @@ private void InitializeComponent() this.mountPointBox.Dock = System.Windows.Forms.DockStyle.Fill; this.mountPointBox.Location = new System.Drawing.Point(85, 267); this.mountPointBox.Name = "mountPointBox"; - this.mountPointBox.Size = new System.Drawing.Size(228, 20); + this.mountPointBox.Size = new System.Drawing.Size(226, 20); this.mountPointBox.TabIndex = 18; // // driveListView @@ -408,7 +409,7 @@ private void InitializeComponent() this.driveListView.Location = new System.Drawing.Point(3, 5); this.driveListView.MultiSelect = false; this.driveListView.Name = "driveListView"; - this.driveListView.Size = new System.Drawing.Size(244, 339); + this.driveListView.Size = new System.Drawing.Size(244, 371); this.driveListView.SmallImageList = this.imageList; this.driveListView.TabIndex = 0; this.driveListView.UseCompatibleStateImageBehavior = false; @@ -438,7 +439,7 @@ private void InitializeComponent() this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel2.Controls.Add(this.removeButton, 1, 0); this.tableLayoutPanel2.Controls.Add(this.addButton, 0, 0); - this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 350); + this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 382); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; this.tableLayoutPanel2.RowCount = 1; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); @@ -482,7 +483,7 @@ private void InitializeComponent() this.buttonPanel.Controls.Add(this.muButton, 1, 0); this.buttonPanel.Controls.Add(this.saveButton, 1, 0); this.buttonPanel.Dock = System.Windows.Forms.DockStyle.Fill; - this.buttonPanel.Location = new System.Drawing.Point(253, 350); + this.buttonPanel.Location = new System.Drawing.Point(253, 382); this.buttonPanel.Name = "buttonPanel"; this.buttonPanel.Padding = new System.Windows.Forms.Padding(0, 0, 15, 0); this.buttonPanel.RowCount = 1; @@ -520,21 +521,38 @@ private void InitializeComponent() // // tableLayoutPanel3 // - this.tableLayoutPanel3.ColumnCount = 2; - this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33F)); - this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67F)); + this.tableLayoutPanel3.ColumnCount = 3; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 70F)); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 94F)); + this.tableLayoutPanel3.Controls.Add(this.buttonVFSMount, 2, 0); this.tableLayoutPanel3.Controls.Add(this.virtualDriveCombo, 1, 0); this.tableLayoutPanel3.Controls.Add(this.label9, 0, 0); this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 391); + this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 423); this.tableLayoutPanel3.Name = "tableLayoutPanel3"; this.tableLayoutPanel3.RowCount = 1; this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel3.Size = new System.Drawing.Size(244, 24); + this.tableLayoutPanel3.Size = new System.Drawing.Size(244, 35); this.tableLayoutPanel3.TabIndex = 6; // + // buttonVFSMount + // + this.buttonVFSMount.Dock = System.Windows.Forms.DockStyle.Fill; + this.buttonVFSMount.Image = global::Sshfs.Properties.Resources.mount; + this.buttonVFSMount.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.buttonVFSMount.Location = new System.Drawing.Point(153, 3); + this.buttonVFSMount.Name = "buttonVFSMount"; + this.buttonVFSMount.Size = new System.Drawing.Size(88, 29); + this.buttonVFSMount.TabIndex = 7; + this.buttonVFSMount.Text = "Mount"; + this.buttonVFSMount.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.buttonVFSMount.UseVisualStyleBackColor = true; + this.buttonVFSMount.Click += new System.EventHandler(this.buttonVFSMount_Click); + // // virtualDriveCombo // + this.virtualDriveCombo.Dock = System.Windows.Forms.DockStyle.Fill; this.virtualDriveCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.virtualDriveCombo.DropDownWidth = 43; this.virtualDriveCombo.FormattingEnabled = true; @@ -542,7 +560,7 @@ private void InitializeComponent() "off"}); this.virtualDriveCombo.Location = new System.Drawing.Point(83, 3); this.virtualDriveCombo.Name = "virtualDriveCombo"; - this.virtualDriveCombo.Size = new System.Drawing.Size(63, 21); + this.virtualDriveCombo.Size = new System.Drawing.Size(64, 21); this.virtualDriveCombo.Sorted = true; this.virtualDriveCombo.TabIndex = 5; this.virtualDriveCombo.SelectedIndexChanged += new System.EventHandler(this.virtualDriveCombo_SelectedIndexChanged); @@ -553,7 +571,7 @@ private void InitializeComponent() this.label9.Dock = System.Windows.Forms.DockStyle.Left; this.label9.Location = new System.Drawing.Point(3, 0); this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(65, 24); + this.label9.Size = new System.Drawing.Size(65, 35); this.label9.TabIndex = 6; this.label9.Text = "Virtual drive:"; this.label9.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -661,7 +679,7 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(583, 418); + this.ClientSize = new System.Drawing.Size(583, 461); this.Controls.Add(this.tableLayoutPanel1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); @@ -740,6 +758,7 @@ private void InitializeComponent() private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; private System.Windows.Forms.ComboBox virtualDriveCombo; private System.Windows.Forms.Label label9; + private System.Windows.Forms.Button buttonVFSMount; diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index 915a181..bed9583 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -25,6 +25,7 @@ public partial class MainForm : Form { private readonly StringBuilder _balloonText = new StringBuilder(255); private readonly List _drives = new List(); + private readonly List _configVars = new List(); private readonly Regex _regex = new Regex(@"^New Drive\s\d{1,2}$", RegexOptions.Compiled); private readonly Queue _suspendedDrives = new Queue(); @@ -74,12 +75,20 @@ protected override void OnLoad(EventArgs e) // _drives.Presist("config.xml",true); - virtualDrive = new VirtualDrive + + virtualDrive = virtualDrive.Load("vfs.xml"); + if (virtualDrive == null) { - Letter = 'Z' - }; + virtualDrive = new VirtualDrive + { + Letter = 'Z' + }; + } + virtualDrive.StatusChanged += drive_VFSStatusChanged; + updateVirtualDriveCombo(); virtualDrive.Mount(); + buttonVFSupdate(); _drives.Load("config.xml"); @@ -226,6 +235,7 @@ protected override void OnFormClosing(FormClosingEventArgs e) if (_dirty) { _drives.Presist("config.xml"); + //virtualDrive.per } notifyIcon.Visible = false; } @@ -251,7 +261,8 @@ private void addButton_Click(object sender, EventArgs e) Name = String.Format("New Drive {0}", ++_namecount), Port = 22, Root = ".", - Letter = letter + Letter = letter, + MountPoint = "" }; drive.StatusChanged += drive_StatusChanged; _drives.Add(drive); @@ -605,7 +616,7 @@ protected override void OnFormClosed(FormClosedEventArgs e) { SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; - // _drives.Presist("config.xml"); + //_drives.Presist("config.xml"); ; Parallel.ForEach(_drives.Where(d => d.Status != DriveStatus.Unmounted), d => @@ -614,6 +625,7 @@ protected override void OnFormClosed(FormClosedEventArgs e) drive_StatusChanged; d.Unmount(); }); + virtualDrive.Unmount(); base.OnFormClosed(e); } @@ -677,22 +689,11 @@ private void virtualDriveCombo_SelectedIndexChanged(object sender, EventArgs e) { if (_updateLockvirtualDriveBox) return; - _updateLockvirtualDriveBox = true; ; - if (virtualDrive.Letter != virtualDriveCombo.Text[0]) - { - virtualDrive.Letter = virtualDriveCombo.Text[0]; + virtualDrive.Letter = virtualDriveCombo.Text[0]; + virtualDrive.Presist("vfs.xml"); - //TODO: this shoud be in thread - blocks gui: - if (virtualDrive != null && (virtualDrive.Status == DriveStatus.Mounted)) - virtualDrive.Unmount(); - - if (virtualDrive != null && virtualDrive.Letter != ' ') - { - virtualDrive.Letter = virtualDrive.Letter; - virtualDrive.Mount(); - } - } + _updateLockvirtualDriveBox = true; ; updateLetterBoxCombo(null); @@ -709,5 +710,42 @@ private void letterBox_SelectedIndexChanged(object sender, EventArgs e) this.updateVirtualDriveCombo(); _updateLockLetterBox = false; } + + private void buttonVFSMount_Click(object sender, EventArgs e) + { + if (virtualDrive == null) return;//hmm + + if (virtualDrive.Status == DriveStatus.Unmounted) + { + virtualDrive.Mount(); + }else if (virtualDrive.Status == DriveStatus.Mounted) + { + virtualDrive.Unmount(); + } + + buttonVFSMount.Enabled = false; + } + + private void buttonVFSupdate() + { + BeginInvoke(new MethodInvoker(() => + { + buttonVFSMount.Text = virtualDrive.Status == DriveStatus.Mounted + ? "Unmount" + : "Mount"; + buttonVFSMount.Image = virtualDrive.Status == DriveStatus.Mounted + ? Resources.unmount + : Resources.mount; + buttonVFSMount.Enabled = true; + })); + } + + private void drive_VFSStatusChanged(object sender, EventArgs e) + { + var drive = sender as SftpDrive; + buttonVFSupdate(); + } + + } } \ No newline at end of file diff --git a/Sshfs/Sshfs/MainForm.resx b/Sshfs/Sshfs/MainForm.resx index 5fca1a6..f6b6aed 100644 --- a/Sshfs/Sshfs/MainForm.resx +++ b/Sshfs/Sshfs/MainForm.resx @@ -125,181 +125,181 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACi - LAAAAk1TRnQBSQFMAgEBBAEAASwBAAEsAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg + LAAAAk1TRnQBSQFMAgEBBAEAATwBAAE8AQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ - AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABswH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ - AoEBuAH/Al8BbQHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK + AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABsQH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ + AoEBuAH/Al8BawHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK AwgBCwMHAQoDBwEJAwUBBwMBAQIgAAMPARQDDwEUAxIBGAMeASsDJgE5AygBPAMoATwDJgE4AyIBMQMY - ASIDEAEWAxABFQMRARcDSAGDAigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/ + ASIDEAEWAxABFQMRARcDSAGDAiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/ A0YBfggAAw8BFAMPARQDEgEYAx4BKwMmATkDKAE8AygBPAMmATgDIgExAxgBIgMQARYDEAEVAxEBFwMH AQkDAgEDAwsBDwMWAR4DFgEfAwsBDwMBAQIQAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGz A1YBtANVAbUDVQG1A1UBtQNbAcgDagH5AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/ - AoEB0wH/AoEB1AH/AmQBbgHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 + AoEB0wH/AoEB1AH/AmQBawHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 A1UBtQNVAbUDVQG1A1YBtAM9AWgDDgETAwMBBBwAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8CKAGkAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHM - Af8CKAGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8CJgGkAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHM + Af8CJgGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AxkBIwMU - ARsDNQFWA2EB2gF3AngB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg + ARsDNQFWA2EB2gF1AnYB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg Af8B3wLhAf8B3wLhAf8B3wLhAf8B3wLhAf8B4QHiAeEB/wHeAeEB3wH/AeEB4gHhAf8BmAGaAc8B/wKB - AcwB/wJmAcwB/wKBAdEB/wKBAdAB/wJmAcwB/wJmAcwB/wKBAdQB/wJqAc0B/wKBAdMB/wKBAbgB/wQA + AcwB/wJkAcwB/wKBAdEB/wKBAdAB/wJkAcwB/wJkAcwB/wKBAdQB/wJoAc0B/wKBAdMB/wKBAbgB/wQA AagBqwGqAf8B5ALlAf8B4QHkAeMB/wHfAuEB/wHfAeEB4AH/Ad8C4QH/Ad8C4QH/Ad8C4QH/Ad8C4QH/ AeEB4gHhAf8B3gHhAd8B/wHhAeIB4QH/AeMB5QHkAf8B4QHjAeIB/wN/Af4DGAEiAwYBCBwAAYEBhAGC - Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AigBpAH/ - AigBzAn/AigBzAn/AigBzAH/AigBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ + Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AiYBpAH/ + AiYBzAn/AiYBzAn/AiYBzAH/AiYBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ Af8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8DgQH/A4EB/wOBAf8BnAKdAf8DtgH/AawCrQH/A1oBwBAA - A0ABbgNiAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv - Af8ByALJAf8CgQGhAf8CgQHMAf8CZgHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT - Af8CgQHTAf8CgQG4Af8EAANAAW4DYgHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr + A0ABbgNfAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv + Af8ByALJAf8CgQGhAf8CgQHMAf8CZAHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT + Af8CgQHTAf8CgQG4Af8EAANAAW4DXwHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr Af8BqgKrAf8DuAH/Aa4CrwH/AcgCyQH/AZ0BnwGeAf8DYgHvA0kBiAMgAS4DBAEFHAABgQGEAYIB/wPz - Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CKAGkAf8CKAHM - Ff8CKAHMAf8CKAGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ + Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CJgGkAf8CJgHM + Ff8CJgHMAf8CJgGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ A8UB/wPoAf8BgQGEAYIB/wOQAf8DswH/A4EB/wO2Af8D6wH/A9UB/wNqAfkQAAMIAQsDOAFdA38B/gGW AZoBmAH/AZUBmQGYAf8BkgGVAZQB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGRAZQBkwH/AZcBmgGZ - Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJmAcwB/wJ2Ac8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJm + Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJkAcwB/wJ0Ac8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJk AcwB/wKBAdMB/wKBAbgB/wQAAwgBCwM4AV0DfwH+AZYBmgGYAf8BlQGZAZgB/wGSAZUBlAH/AZIBlQGU Af8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGaAZkB/wGYAZwBmwH/A14B1QM/AW0DTAGSA0cBgAMA AQEcAANPAZcBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQIoAaQB/wIoAcwB/wIoAcwN/wIo - AcwB/wIoAcwB/wIoAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQImAaQB/wImAcwB/wImAcwN/wIm + AcwB/wImAcwB/wImAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9A4EB/wOB Af8DgQH/AaMCpAH/A9UB/wPXAf8DWQG/FAADAQECAwQBBQGVAZkBmAH/AcsBzwHOAf8B2wHdAdwB/wHY - AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmYBzAH/ - AmYBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmYBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ + AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmQBzAH/ + AmQBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmQBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ AcsBzwHOAf8B2wHdAdwB/wHYAdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/ A0oBiQNbAcQDXgHOAxkBIyAAAYEBhAGCAf8D1gH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt - Af8D7QH/A9QB/wGBAYQBggH/AigBpAH/AigBzBX/AigBzAH/AigBpAH/CAABgQGEAYIB/wPWAf8B9wHx + Af8D7QH/A9QB/wGBAYQBggH/AiYBpAH/AiYBzBX/AiYBzAH/AiYBpAH/CAABgQGEAYIB/wPWAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/ AfcB8QHrAf8D1AH/AYEBhAGCAf8EAAMFAQcDJgE4A4EB/wOBAf8DgQH/AxoBJRAAAx8BLANeAdUBoQGm AaQB/wGhAaUBowH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/ - AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJmAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB - AfMB/wJ4Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANeAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl + AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJkAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB + AfMB/wJ2Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANeAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl AaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/AaUBqQGnAf8BpQGpAacB/wGgAaUBowH/ - A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBcAFe - Af8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGB - AXABXgH/A+0B/wGBAYQBggH/AigBpAH/AigBzAn/AigBzAn/AigBzAH/AigBpAH/CAABgQGEAYIB/wH6 - AfYB8gH/AaQBgQFkAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGoAYEBagH/AagBgQFq - Af8BqAGBAWoB/wGkAYEBZAH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA + A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBbgFc + Af8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGB + AW4BXAH/A+0B/wGBAYQBggH/AiYBpAH/AiYBzAn/AiYBzAn/AiYBzAH/AiYBpAH/CAABgQGEAYIB/wH6 + AfYB8gH/AaQBgQFiAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFo + Af8BqAGBAWgB/wGkAYEBYgH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA AzUBVQG/AcMBwQH/AdcB3AHZAf8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/ - Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJmAcwB/wKBAdcB/wKB - AdsB/wJmAcwB/wJ2Ac8B/wKBAeEB/wJ6AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ + Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJkAcwB/wKBAdcB/wKB + AdsB/wJkAcwB/wJ0Ac8B/wKBAeEB/wJ4AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ Af8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/Ad0B4gHfAf8B3wHkAeEB/wHf AeMB4QH/Ad8B4wHhAf8B1gHaAdgB/wGcAaEBnwH/A1YBtANWAbQDVQG1A1UBtQNVAbUDVQG1A1YBswMx - AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFwAV4B/wGTAYEBdAH/AZMBgQF0Af8BlAGBAXQB/wGTAYEBdAH/ - AZMBgQF0Af8BkwGBAXUB/wGUAYEBdAH/AYEBcAFeAf8D7gH/AYEBhAGCAf8CKAGkAf8CKAHMAf8CKAHM - Af8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAHMAf8CKAGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB - AWoB/wG6AYEBbgH/AboBgQFuAf8BuwGBAW4B/wG6AYEBbgH/AboBgQFuAf8BugGBAW8B/wG7AYEBbgH/ - AagBgQFqAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA + AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFuAVwB/wGTAYEBcgH/AZMBgQFyAf8BlAGBAXIB/wGTAYEBcgH/ + AZMBgQFyAf8BkwGBAXMB/wGUAYEBcgH/AYEBbgFcAf8D7gH/AYEBhAGCAf8CJgGkAf8CJgHMAf8CJgHM + Af8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB + AWgB/wG6AYEBbAH/AboBgQFsAf8BuwGBAWwB/wG6AYEBbAH/AboBgQFsAf8BugGBAW0B/wG7AYEBbAH/ + AagBgQFoAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA AzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/AZMBlgHK - Af8CgQHMAf8CawHNAf8CZgHMAf8CZgHMAf8CZgHMAf8CZgHMAf8CZgHMAf8CZgHMAf8CgQHTAf8CQAGz + Af8CgQHMAf8CaQHNAf8CZAHMAf8CZAHMAf8CZAHMAf8CZAHMAf8CZAHMAf8CZAHMAf8CgQHTAf8CQAGx Af0EAAM1AVUBwgHFAcQB/wNSAaMDgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wGdAoEB/wHb AeAB3gH/AZ4BowGhAf8B3wLhAf8B4AHiAeEB/wHfAeIB4AH/Ad4B4QHfAf8B4gHjAeIB/wHjAeUB4wH/ - AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBcAFeAf8BqwGKAYEB/wGdAoEB/wGPAYEBcQH/ - AZABgQFxAf8BkAGBAXEB/wGQAYEBcQH/AY8BgQFxAf8BgQFwAV4B/wPuAf8BgQGEAYIB/wFoAWoBkgH/ - AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/AigBpAH/A0YBfggAAYEBhAGCAf8B+AHy - AewB/wGoAYEBagH/AdABngGBAf8BxAGLAYEB/wG2AYEBbAH/AbcBgQFsAf8BtwGBAWwB/wG3AYEBbAH/ - AbYBgQFsAf8BqAGBAWoB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDugH9A4EB/wGgAoEB/wGfAoEB/wGa + AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBbgFcAf8BqwGKAYEB/wGdAoEB/wGPAYEBbwH/ + AZABgQFvAf8BkAGBAW8B/wGQAYEBbwH/AY8BgQFvAf8BgQFuAVwB/wPuAf8BgQGEAYIB/wFmAWgBkgH/ + AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/A0YBfggAAYEBhAGCAf8B+AHy + AewB/wGoAYEBaAH/AdABngGBAf8BxAGLAYEB/wG2AYEBagH/AbcBgQFqAf8BtwGBAWoB/wG3AYEBagH/ + AbYBgQFqAf8BqAGBAWgB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDuAH9A4EB/wGgAoEB/wGfAoEB/wGa AoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8BtgG5AdYB/wKBAbsB/wKBAcwB/wKB - AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7oB/QOBAf8BoAKB + AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7gB/QOBAf8BoAKB Af8BnwKBAf8BmgKBAf8BmAKBAf8BlwKBAf8BmAKBAf8BmAKBAf8BmAKBAf8DgQH/Ad8B4gHhAf8BngGi AaAB/wGqAqsB/wGxArIB/wGuAq8B/wGyArMB/wG6ArsB/wGcAZ4BnQH/A2QB5wNAAW4DFgEfBAABgQGE - AYIB/wPuAf8BgQFwAV4B/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFu - Af8BjAGBAW4B/wGBAXABXgH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC - Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFqAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG - AYEB/wG6AYEBdQH/AbIBgQFpAf8BtAGBAWkB/wGoAYEBagH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ + AYIB/wPuAf8BgQFuAVwB/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFs + Af8BjAGBAWwB/wGBAW4BXAH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC + Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFoAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG + AYEB/wG6AYEBcwH/AbIBgQFnAf8BtAGBAWcB/wGoAYEBaAH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AwEBAgwAAzUBVQNfAfsDgQH/AawBiwGBAf8BqwGJAYEB/wGl AoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B2gLeAf8BggGGAaUB/wKBAZIB/wKB AZEB/wKBAZEB/wKBAZEB/wKBAZEB/wJUAVYBqwNFAXwDVwG6AzUBVgQAAzUBVQNfAfsDgQH/AawBiwGB Af8BqwGJAYEB/wGlAoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B3QHhAd8B/wGc AaABnwH/AZIBlQGUAf8BkgGVAZQB/wGQAZMBkgH/AZsBngGdAf8BlAGXAZYB/wNWAasDOwFkA1IBoQMr - AUIEAAGBAYQBggH/A/AB/wGBAXABXgH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE - AYEB/wGmAYQBgQH/AZABgQF5Af8BgQFwAV4B/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ - A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFqAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB - Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXMB/wGoAYEBagH/AfgB9AHuAf8BgQGEAYIB/wPr - Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3oB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ + AUIEAAGBAYQBggH/A/AB/wGBAW4BXAH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE + AYEB/wGmAYQBgQH/AZABgQF3Af8BgQFuAVwB/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ + A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFoAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB + Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXEB/wGoAYEBaAH/AfgB9AHuAf8BgQGEAYIB/wPr + Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3gB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOBAf8BvQHBAcAB/wGXAZsBmQH/ - AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3oB+AOB + AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3gB+AOB Af8BqwGJAYEB/wGpAYcBgQH/AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOB Af8BvQHBAcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQ - AZsDDQERBAABgQGEAYIB/wPzAf8BgQFwAV4B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi - AoEB/wGjAoEB/wGBAXABXgH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBagH/AckBlQGBAf8ByQGU - AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWoB/wH4AfQB7gH/ + AZsDDQERBAABgQGEAYIB/wPzAf8BgQFuAVwB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi + AoEB/wGjAoEB/wGBAW4BXAH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBaAH/AckBlQGBAf8ByQGU + AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWgB/wH4AfQB7gH/ AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9 EAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGBAf8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/ AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGqAagB/wGlAaoBqAH/AaUBqgGnAf8BpQGp AacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGB Af8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGq AagB/wGlAaoBqAH/AaUBqgGnAf8BpQGpAacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAABgQGEAYIB/wPt - Af8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGB - AXABXgH/AYEBcAFeAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA - AYEBhAGCAf8B9wHxAesB/wGkAYEBZAH/AagBgQFqAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGB - AWoB/wGoAYEBagH/AagBgQFqAf8BpAGBAWQB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ + Af8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGB + AW4BXAH/AYEBbgFcAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA + AYEBhAGCAf8B9wHxAesB/wGkAYEBYgH/AagBgQFoAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGB + AWgB/wGoAYEBaAH/AagBgQFoAf8BpAGBAWIB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/A9QB/wGBAYQBggH/EAADNQFVA2UB9AOBAf8BqAGGAYEB/wGn AYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGjAoEB/wGdAoEB/wGVAoEB/wOBAf8B0wHWAdUB/wGZ AZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/Ad8B4wHhAf8B3AHgAd4B/wHWAdkB2AH/AYEBhAGC Af8MAAM1AVUDZQH0A4EB/wGoAYYBgQH/AacBhQGBAf8BpgGEAYEB/wGlAYIBgQH/AaQCgQH/AaMCgQH/ AZ0CgQH/AZUCgQH/A4EB/wHTAdYB1QH/AZkBngGcAf8BxQHKAcgB/wHeAeIB4AH/Ad8B4wHhAf8B3wHj AeEB/wHcAeAB3gH/AdYB2QHYAf8BgQGEAYIB/wwAAYEBhAGCAf8BxwLIAf8D7QH/A+0B/wPtAf8D7QH/ - A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGB - AXABXgH/AYEBcAFeAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 + A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGB + AW4BXAH/AYEBbgFcAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AdkC2gH/ - AYEBhAGCAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGkAYEBZAH/AfcB8QHrAf8BgQGE - AYIB/xAAAzUBVQNnAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ + AYEBhAGCAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGkAYEBYgH/AfcB8QHrAf8BgQGE + AYIB/xAAAzUBVQNlAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ AaACgQH/AZ8CgQH/A4EB/wHAAcMBwQH/AZYBmQGXAf8DgQH/A4EB/wOBAf8DgQH/A1kBwgHcAd8B3gH/ - AYEBhAGCAf8MAAM1AVUDZwHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh + AYEBhAGCAf8MAAM1AVUDZQHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh AoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/A4EB/wNZAcIB3AHf Ad4B/wGBAYQBggH/DAADVAGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBdAH/ - AZMBgQF0Af8BkwGBAXUB/wGUAYEBdAH/AYEBcAFeAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBcgH/ + AZMBgQFyAf8BkwGBAXMB/wGUAYEBcgH/AYEBbgFcAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFuAf8BugGBAW4B/wG6AYEBbwH/AbsBgQFuAf8BqAGB - AWoB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW + Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFsAf8BugGBAWwB/wG6AYEBbQH/AbsBgQFsAf8BqAGB + AWgB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW AoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGdAoEB/wHUAdYB1AH/AZsBnQGbAf8BlwKBAf8BmAKBAf8BlwKB Af8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/DAADNQFVA2UB9ANUAawBlgKBAf8BlgKBAf8BlgKB Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGbAZ0BmwH/AZcCgQH/ - AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBcAFe - Af8BqwGKAYEB/wGdAoEB/wGPAYEBcQH/AZABgQFxAf8BkAGBAXEB/wGQAYEBcQH/AY8BgQFxAf8BgQFw - AV4B/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBagH/AdABngGBAf8BxAGLAYEB/wG2 - AYEBbAH/AbcBgQFsAf8BtwGBAWwB/wG3AYEBbAH/AbYBgQFsAf8BqAGBAWoB/wH4AfIB7AH/AYEBhAGC + AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBbgFc + Af8BqwGKAYEB/wGdAoEB/wGPAYEBbwH/AZABgQFvAf8BkAGBAW8B/wGQAYEBbwH/AY8BgQFvAf8BgQFu + AVwB/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBaAH/AdABngGBAf8BxAGLAYEB/wG2 + AYEBagH/AbcBgQFqAf8BtwGBAWoB/wG3AYEBagH/AbYBgQFqAf8BqAGBAWgB/wH4AfIB7AH/AYEBhAGC Af8QAAMhATADXgHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmwGfAZ0B/wGJAYcBgQH/AZQCgQH/AZMCgQH/ - AZQCgQH/AZQCgQH/AZsCgQH/A7oB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe + AZQCgQH/AZQCgQH/AZsCgQH/A7gB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ - AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO6Af0BgQGEAYIB/ygA - AYEBhAGCAf8D7gH/AYEBcAFeAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL - AYEBbgH/AYwBgQFuAf8BgQFwAV4B/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBagH/ - Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXUB/wGyAYEBaQH/AbQBgQFpAf8BqAGB - AWoB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDbAH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ - AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DegH8AYEBhAGCAf8oAANSAakDbAH1 + AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO4Af0BgQGEAYIB/ygA + AYEBhAGCAf8D7gH/AYEBbgFcAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL + AYEBbAH/AYwBgQFsAf8BgQFuAVwB/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBaAH/ + Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXMB/wGyAYEBZwH/AbQBgQFnAf8BqAGB + AWgB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDagH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ + AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DeAH8AYEBhAGCAf8oAANSAakDagH1 AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKB - Af8BjgKBAf8DegH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAXABXgH/AaYBgwGBAf8BpQGEAYEB/wGm - AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF5Af8BgQFwAV4B/wPwAf8BgQGEAYIB/ywA - AYEBhAGCAf8B+AH0Ae4B/wGoAYEBagH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY - AYEB/wHMAZgBgQH/AbcBgQFzAf8BqAGBAWoB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDbgHxAYkCgQH/ + Af8BjgKBAf8DeAH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAW4BXAH/AaYBgwGBAf8BpQGEAYEB/wGm + AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF3Af8BgQFuAVwB/wPwAf8BgQGEAYIB/ywA + AYEBhAGCAf8B+AH0Ae4B/wGoAYEBaAH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY + AYEB/wHMAZgBgQH/AbcBgQFxAf8BqAGBAWgB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDawHxAYkCgQH/ AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGOAoEB/wGLAoEB/wGX - AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNuAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE + AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNrAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE AYEB/wGmAYIBgQH/AZYCgQH/AY4CgQH/AYsCgQH/AZcCgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wPz - Af8BgQFwAV4B/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAXABXgH/ - A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFqAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB - Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBagH/AfgB9AHuAf8BgQGEAYIB/ywA - A1IBqQNnAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb - AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNnAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ + Af8BgQFuAVwB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAW4BXAH/ + A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFoAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB + Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBaAH/AfgB9AHuAf8BgQGEAYIB/ywA + A1IBqQNjAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb + AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNjAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGbAoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygA - AYEBhAGCAf8D7QH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/ - AYEBcAFeAf8BgQFwAV4B/wGBAXABXgH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFk - Af8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGoAYEBagH/AagBgQFqAf8BqAGBAWoB/wGk - AYEBZAH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNiAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB + AYEBhAGCAf8D7QH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/ + AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFi + Af8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGk + AYEBYgH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNiAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB Af8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKBAf8DagH5AYEBhAGCAf8oAANSAakDYgHp AYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGkAYIBgQH/AaMCgQH/AaICgQH/AaECgQH/AaACgQH/AZ4CgQH/ AZACgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wHHAsgB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt Af8D7QH/A+0B/wHZAtoB/wGBAYQBggH/LAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/AfcB8QHrAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wHZAtoB/wGB - AYQBggH/LAADUgGpA2cB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8DYAHWA24B8QGBAYQBggH/KAADUgGpA2cB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA24B8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE + AYQBggH/LAADUgGpA2MB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8DYAHWA2sB8QGBAYQBggH/KAADUgGpA2MB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA2sB8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ AYEBhAGCAf8BgQGEAYIB/wNPAZcsAANUAawBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A08BlywA diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 04cc8ad..ed43730 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.5.11")] -[assembly: AssemblyFileVersion("0.1.5.11")] +[assembly: AssemblyVersion("0.1.5.12")] +[assembly: AssemblyFileVersion("0.1.5.12")] diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index a0cad58..1ae0456 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -34,8 +34,8 @@ true index.html false - 10 - 0.1.5.10 + 12 + 0.1.5.12 true true true diff --git a/Sshfs/Sshfs/Utilities.cs b/Sshfs/Sshfs/Utilities.cs index 6896539..45589d7 100644 --- a/Sshfs/Sshfs/Utilities.cs +++ b/Sshfs/Sshfs/Utilities.cs @@ -36,6 +36,21 @@ public static void Load(this List list, string file) where T : ISerializab list.AddRange(xmlSerializer.ReadObject(stream) as IEnumerable); } + } + + public static T Load(this T obj, string file) where T : ISerializable + { + string filepath = datadir.FullName + "\\" + file; + if (!File.Exists(filepath)) return default(T); + + var xmlSerializer = new DataContractSerializer(typeof(IEnumerable)); + + using ( + var stream = File.Open(filepath, FileMode.OpenOrCreate, + FileAccess.Read)) + { + return (T)xmlSerializer.ReadObject(stream); + } } public static void Presist(this List list, string file, bool delete = false) where T : ISerializable @@ -56,8 +71,29 @@ public static void Presist(this List list, string file, bool delete = fals xmlSerializer.WriteObject(stream, list); } } + } + + + public static void Presist(this T obj, string file, bool delete = false) where T : ISerializable + { + string filepath = datadir.FullName + "\\" + file; + if (delete) + { + File.Delete(filepath); + } + else + { + var xmlSerializer = new DataContractSerializer(typeof(List)); + using ( + var stream = File.Open(filepath, FileMode.Create, + FileAccess.Write)) + { + xmlSerializer.WriteObject(stream, obj); + } + } } + public static string ProtectString(string stringToProtect) { return stringToProtect != null diff --git a/Sshfs/Sshfs/VirtualDrive.cs b/Sshfs/Sshfs/VirtualDrive.cs index 8735955..6bb280e 100644 --- a/Sshfs/Sshfs/VirtualDrive.cs +++ b/Sshfs/Sshfs/VirtualDrive.cs @@ -17,7 +17,7 @@ namespace Sshfs { [Serializable] - public class VirtualDrive : IDisposable + public class VirtualDrive : IDisposable, ISerializable { private readonly CancellationTokenSource _mountCancel = new CancellationTokenSource(); private readonly AutoResetEvent _pauseEvent = new AutoResetEvent(false); @@ -27,6 +27,7 @@ public class VirtualDrive : IDisposable private bool _exeptionThrown; private VirtualFilesystem _filesystem; + private List _drives = new List(); public string Name { get; set; } @@ -43,12 +44,16 @@ public VirtualDrive() { } internal void AddSubFS(SftpDrive sftpDrive) { - _filesystem.AddSubFS(sftpDrive); + _drives.Add(sftpDrive); + if (_filesystem!=null) + _filesystem.AddSubFS(sftpDrive); } internal void RemoveSubFS(SftpDrive sftpDrive) { - _filesystem.RemoveSubFS(sftpDrive); + _drives.Remove(sftpDrive); + if (_filesystem!=null) + _filesystem.RemoveSubFS(sftpDrive); } @@ -100,8 +105,12 @@ private void MountLoop() try { _filesystem = new VirtualFilesystem("WinSshFS spool"); - _filesystem.Mount(String.Format("{0}:\\", Letter), Settings.Default.UseNetworkDrive ? DokanOptions.NetworkDrive | DokanOptions.KeepAlive : DokanOptions.RemovableDrive | DokanOptions.KeepAlive); + foreach (SftpDrive drive in _drives) + { + _filesystem.AddSubFS(drive); + } + _filesystem.Mount(String.Format("{0}:\\", Letter), Settings.Default.UseNetworkDrive ? DokanOptions.NetworkDrive | DokanOptions.KeepAlive : DokanOptions.RemovableDrive | DokanOptions.KeepAlive); } catch (Exception e) { @@ -226,5 +235,22 @@ public void Dispose() #endregion + #region Implementation of ISerializable + + public VirtualDrive(SerializationInfo info, + StreamingContext context) + { + Letter = info.GetChar("letter"); + } + + + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("letter", Letter); + } + + #endregion + } } \ No newline at end of file diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 1913185..a8de75e 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -507,6 +507,8 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName,string pattern, foreach (SftpDrive subdrive in _subsytems) { string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... + if (mp.Length == 0) + continue; if (path.Length > 0) //not root dir { @@ -612,6 +614,8 @@ DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replac SftpDrive drive2 = this.GetDriveByMountPoint(newName, out newName); if (drive2 != drive) { + //This is server2server move - Total commander handles this by copy&delete, explorer ends with error + //background direct copy between 2 sftp is nice but not real return DokanError.ErrorNotImplemented; } From 93b7e42c7a6714ad8a6459c076733babdd132d55 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 28 Jul 2014 19:08:26 +0200 Subject: [PATCH 048/134] file caching bug --- Sshfs/Sshfs/SftpFilesystem.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 01d7813..a71c12a 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -559,9 +559,13 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) } //_cache.Remove(fileName); - /* cache reset for dir close is not good idea, for files its probably also nto problem + /* cache reset for dir close is not good idea, will read it verz soon probablz again CacheReset(GetUnixPath(fileName)); */ + if (!info.IsDirectory) + { + CacheReset(GetUnixPath(fileName)); + } return DokanError.ErrorSuccess; From 50d3de730e5d0d8965eb1e247b4d6eea238a4110 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 29 Jul 2014 01:30:10 +0200 Subject: [PATCH 049/134] DeleteOnClose Fileoptins Solved with workaround --- DokanNet/DokanFileInfo.cs | 6 +- Sshfs/Sshfs/MainForm.Designer.cs | 20 ++-- Sshfs/Sshfs/MainForm.resx | 198 +++++++++++++++---------------- Sshfs/Sshfs/SftpContext.cs | 8 ++ Sshfs/Sshfs/SftpFilesystem.cs | 27 +++-- create-mirror.bat | 2 + 6 files changed, 140 insertions(+), 121 deletions(-) create mode 100644 create-mirror.bat diff --git a/DokanNet/DokanFileInfo.cs b/DokanNet/DokanFileInfo.cs index 8593e95..8cc091a 100644 --- a/DokanNet/DokanFileInfo.cs +++ b/DokanNet/DokanFileInfo.cs @@ -15,7 +15,7 @@ public sealed class DokanFileInfo private readonly IntPtr _dokanOptions; private readonly uint _processId; [MarshalAs(UnmanagedType.U1)] private bool _isDirectory; - [MarshalAs(UnmanagedType.U1)] private bool _deleteOnClose; + [MarshalAs(UnmanagedType.U1)] private byte _deleteOnClose; [MarshalAs(UnmanagedType.U1)] private bool _pagingIo; [MarshalAs(UnmanagedType.U1)] private bool _synchronousIo; [MarshalAs(UnmanagedType.U1)] private bool _nocache; @@ -54,8 +54,8 @@ public bool IsDirectory public bool DeleteOnClose { - get { return _deleteOnClose; } - set { _deleteOnClose = value; } + get { return _deleteOnClose>0; } + set { _deleteOnClose = value ? byte.MaxValue : byte.MinValue ; } } public bool PagingIo diff --git a/Sshfs/Sshfs/MainForm.Designer.cs b/Sshfs/Sshfs/MainForm.Designer.cs index 076ffe1..255fa4b 100644 --- a/Sshfs/Sshfs/MainForm.Designer.cs +++ b/Sshfs/Sshfs/MainForm.Designer.cs @@ -117,7 +117,7 @@ private void InitializeComponent() this.fieldsPanel.ColumnCount = 3; this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 26.10063F)); this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 73.89937F)); - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 12F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 13F)); this.fieldsPanel.Controls.Add(this.nameBox, 1, 0); this.fieldsPanel.Controls.Add(this.label1, 0, 0); this.fieldsPanel.Controls.Add(this.hostBox, 1, 1); @@ -157,7 +157,7 @@ private void InitializeComponent() // // nameBox // - this.nameBox.Location = new System.Drawing.Point(85, 3); + this.nameBox.Location = new System.Drawing.Point(84, 3); this.nameBox.Name = "nameBox"; this.nameBox.Size = new System.Drawing.Size(186, 20); this.nameBox.TabIndex = 0; @@ -176,7 +176,7 @@ private void InitializeComponent() // // hostBox // - this.hostBox.Location = new System.Drawing.Point(85, 32); + this.hostBox.Location = new System.Drawing.Point(84, 32); this.hostBox.Name = "hostBox"; this.hostBox.Size = new System.Drawing.Size(186, 20); this.hostBox.TabIndex = 1; @@ -195,7 +195,7 @@ private void InitializeComponent() // // portBox // - this.portBox.Location = new System.Drawing.Point(85, 62); + this.portBox.Location = new System.Drawing.Point(84, 62); this.portBox.Name = "portBox"; this.portBox.Size = new System.Drawing.Size(68, 20); this.portBox.TabIndex = 2; @@ -224,7 +224,7 @@ private void InitializeComponent() // // userBox // - this.userBox.Location = new System.Drawing.Point(85, 90); + this.userBox.Location = new System.Drawing.Point(84, 90); this.userBox.Name = "userBox"; this.userBox.Size = new System.Drawing.Size(186, 20); this.userBox.TabIndex = 3; @@ -238,7 +238,7 @@ private void InitializeComponent() "Password", "PrivateKey", "Pageant"}); - this.authCombo.Location = new System.Drawing.Point(85, 119); + this.authCombo.Location = new System.Drawing.Point(84, 119); this.authCombo.Name = "authCombo"; this.authCombo.Size = new System.Drawing.Size(121, 21); this.authCombo.TabIndex = 4; @@ -270,7 +270,7 @@ private void InitializeComponent() // this.panel1.Controls.Add(this.letterBox); this.panel1.Controls.Add(this.mountCheck); - this.panel1.Location = new System.Drawing.Point(85, 238); + this.panel1.Location = new System.Drawing.Point(84, 238); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(182, 23); this.panel1.TabIndex = 12; @@ -304,7 +304,7 @@ private void InitializeComponent() this.directoryBox.Items.AddRange(new object[] { ".", "/"}); - this.directoryBox.Location = new System.Drawing.Point(85, 211); + this.directoryBox.Location = new System.Drawing.Point(84, 211); this.directoryBox.Name = "directoryBox"; this.directoryBox.Size = new System.Drawing.Size(186, 21); this.directoryBox.TabIndex = 8; @@ -326,7 +326,7 @@ private void InitializeComponent() this.panel2.Controls.Add(this.privateKeyBox); this.panel2.Controls.Add(this.privateKeyButton); this.panel2.Controls.Add(this.passwordBox); - this.panel2.Location = new System.Drawing.Point(85, 148); + this.panel2.Location = new System.Drawing.Point(84, 148); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(186, 57); this.panel2.TabIndex = 15; @@ -392,7 +392,7 @@ private void InitializeComponent() // mountPointBox // this.mountPointBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.mountPointBox.Location = new System.Drawing.Point(85, 267); + this.mountPointBox.Location = new System.Drawing.Point(84, 267); this.mountPointBox.Name = "mountPointBox"; this.mountPointBox.Size = new System.Drawing.Size(226, 20); this.mountPointBox.TabIndex = 18; diff --git a/Sshfs/Sshfs/MainForm.resx b/Sshfs/Sshfs/MainForm.resx index f6b6aed..86fa340 100644 --- a/Sshfs/Sshfs/MainForm.resx +++ b/Sshfs/Sshfs/MainForm.resx @@ -125,181 +125,181 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACi - LAAAAk1TRnQBSQFMAgEBBAEAATwBAAE8AQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg + LAAAAk1TRnQBSQFMAgEBBAEAAUQBAAFEAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ - AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABsQH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ - AoEBuAH/Al8BawHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK + AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABsAH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ + AoEBuAH/Al8BagHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK AwgBCwMHAQoDBwEJAwUBBwMBAQIgAAMPARQDDwEUAxIBGAMeASsDJgE5AygBPAMoATwDJgE4AyIBMQMY - ASIDEAEWAxABFQMRARcDSAGDAiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/ + ASIDEAEWAxABFQMRARcDSAGDAiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/ A0YBfggAAw8BFAMPARQDEgEYAx4BKwMmATkDKAE8AygBPAMmATgDIgExAxgBIgMQARYDEAEVAxEBFwMH AQkDAgEDAwsBDwMWAR4DFgEfAwsBDwMBAQIQAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGz A1YBtANVAbUDVQG1A1UBtQNbAcgDagH5AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/ - AoEB0wH/AoEB1AH/AmQBawHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 + AoEB0wH/AoEB1AH/AmQBaQHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 A1UBtQNVAbUDVQG1A1YBtAM9AWgDDgETAwMBBBwAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8CJgGkAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHM - Af8CJgGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8CJQGkAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHM + Af8CJQGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AxkBIwMU - ARsDNQFWA2EB2gF1AnYB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg + ARsDNQFWA2EB2gF0AnUB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg Af8B3wLhAf8B3wLhAf8B3wLhAf8B3wLhAf8B4QHiAeEB/wHeAeEB3wH/AeEB4gHhAf8BmAGaAc8B/wKB - AcwB/wJkAcwB/wKBAdEB/wKBAdAB/wJkAcwB/wJkAcwB/wKBAdQB/wJoAc0B/wKBAdMB/wKBAbgB/wQA + AcwB/wJjAcwB/wKBAdEB/wKBAdAB/wJjAcwB/wJjAcwB/wKBAdQB/wJnAc0B/wKBAdMB/wKBAbgB/wQA AagBqwGqAf8B5ALlAf8B4QHkAeMB/wHfAuEB/wHfAeEB4AH/Ad8C4QH/Ad8C4QH/Ad8C4QH/Ad8C4QH/ AeEB4gHhAf8B3gHhAd8B/wHhAeIB4QH/AeMB5QHkAf8B4QHjAeIB/wN/Af4DGAEiAwYBCBwAAYEBhAGC - Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AiYBpAH/ - AiYBzAn/AiYBzAn/AiYBzAH/AiYBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ + Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AiUBpAH/ + AiUBzAn/AiUBzAn/AiUBzAH/AiUBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ Af8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8DgQH/A4EB/wOBAf8BnAKdAf8DtgH/AawCrQH/A1oBwBAA - A0ABbgNfAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv - Af8ByALJAf8CgQGhAf8CgQHMAf8CZAHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT - Af8CgQHTAf8CgQG4Af8EAANAAW4DXwHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr + A0ABbgNeAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv + Af8ByALJAf8CgQGhAf8CgQHMAf8CYwHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT + Af8CgQHTAf8CgQG4Af8EAANAAW4DXgHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr Af8BqgKrAf8DuAH/Aa4CrwH/AcgCyQH/AZ0BnwGeAf8DYgHvA0kBiAMgAS4DBAEFHAABgQGEAYIB/wPz - Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CJgGkAf8CJgHM - Ff8CJgHMAf8CJgGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ + Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CJQGkAf8CJQHM + Ff8CJQHMAf8CJQGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ A8UB/wPoAf8BgQGEAYIB/wOQAf8DswH/A4EB/wO2Af8D6wH/A9UB/wNqAfkQAAMIAQsDOAFdA38B/gGW AZoBmAH/AZUBmQGYAf8BkgGVAZQB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGRAZQBkwH/AZcBmgGZ - Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJkAcwB/wJ0Ac8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJk + Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJjAcwB/wJzAc8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJj AcwB/wKBAdMB/wKBAbgB/wQAAwgBCwM4AV0DfwH+AZYBmgGYAf8BlQGZAZgB/wGSAZUBlAH/AZIBlQGU Af8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGaAZkB/wGYAZwBmwH/A14B1QM/AW0DTAGSA0cBgAMA AQEcAANPAZcBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQImAaQB/wImAcwB/wImAcwN/wIm - AcwB/wImAcwB/wImAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQIlAaQB/wIlAcwB/wIlAcwN/wIl + AcwB/wIlAcwB/wIlAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9A4EB/wOB Af8DgQH/AaMCpAH/A9UB/wPXAf8DWQG/FAADAQECAwQBBQGVAZkBmAH/AcsBzwHOAf8B2wHdAdwB/wHY - AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmQBzAH/ - AmQBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmQBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ + AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmMBzAH/ + AmMBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmMBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ AcsBzwHOAf8B2wHdAdwB/wHYAdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/ A0oBiQNbAcQDXgHOAxkBIyAAAYEBhAGCAf8D1gH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt - Af8D7QH/A9QB/wGBAYQBggH/AiYBpAH/AiYBzBX/AiYBzAH/AiYBpAH/CAABgQGEAYIB/wPWAf8B9wHx + Af8D7QH/A9QB/wGBAYQBggH/AiUBpAH/AiUBzBX/AiUBzAH/AiUBpAH/CAABgQGEAYIB/wPWAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/ AfcB8QHrAf8D1AH/AYEBhAGCAf8EAAMFAQcDJgE4A4EB/wOBAf8DgQH/AxoBJRAAAx8BLANeAdUBoQGm AaQB/wGhAaUBowH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/ - AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJkAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB - AfMB/wJ2Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANeAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl + AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJjAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB + AfMB/wJ1Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANeAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl AaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/AaUBqQGnAf8BpQGpAacB/wGgAaUBowH/ - A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBbgFc - Af8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGB - AW4BXAH/A+0B/wGBAYQBggH/AiYBpAH/AiYBzAn/AiYBzAn/AiYBzAH/AiYBpAH/CAABgQGEAYIB/wH6 - AfYB8gH/AaQBgQFiAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFo - Af8BqAGBAWgB/wGkAYEBYgH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA + A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBbQFb + Af8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGB + AW0BWwH/A+0B/wGBAYQBggH/AiUBpAH/AiUBzAn/AiUBzAn/AiUBzAH/AiUBpAH/CAABgQGEAYIB/wH6 + AfYB8gH/AaQBgQFhAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFn + Af8BqAGBAWcB/wGkAYEBYQH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA AzUBVQG/AcMBwQH/AdcB3AHZAf8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/ - Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJkAcwB/wKBAdcB/wKB - AdsB/wJkAcwB/wJ0Ac8B/wKBAeEB/wJ4AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ + Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJjAcwB/wKBAdcB/wKB + AdsB/wJjAcwB/wJzAc8B/wKBAeEB/wJ3AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ Af8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/Ad0B4gHfAf8B3wHkAeEB/wHf AeMB4QH/Ad8B4wHhAf8B1gHaAdgB/wGcAaEBnwH/A1YBtANWAbQDVQG1A1UBtQNVAbUDVQG1A1YBswMx - AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFuAVwB/wGTAYEBcgH/AZMBgQFyAf8BlAGBAXIB/wGTAYEBcgH/ - AZMBgQFyAf8BkwGBAXMB/wGUAYEBcgH/AYEBbgFcAf8D7gH/AYEBhAGCAf8CJgGkAf8CJgHMAf8CJgHM - Af8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgHMAf8CJgGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB - AWgB/wG6AYEBbAH/AboBgQFsAf8BuwGBAWwB/wG6AYEBbAH/AboBgQFsAf8BugGBAW0B/wG7AYEBbAH/ - AagBgQFoAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA + AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFtAVsB/wGTAYEBcQH/AZMBgQFxAf8BlAGBAXEB/wGTAYEBcQH/ + AZMBgQFxAf8BkwGBAXIB/wGUAYEBcQH/AYEBbQFbAf8D7gH/AYEBhAGCAf8CJQGkAf8CJQHMAf8CJQHM + Af8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB + AWcB/wG6AYEBawH/AboBgQFrAf8BuwGBAWsB/wG6AYEBawH/AboBgQFrAf8BugGBAWwB/wG7AYEBawH/ + AagBgQFnAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA AzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/AZMBlgHK - Af8CgQHMAf8CaQHNAf8CZAHMAf8CZAHMAf8CZAHMAf8CZAHMAf8CZAHMAf8CZAHMAf8CgQHTAf8CQAGx + Af8CgQHMAf8CaAHNAf8CYwHMAf8CYwHMAf8CYwHMAf8CYwHMAf8CYwHMAf8CYwHMAf8CgQHTAf8CQAGw Af0EAAM1AVUBwgHFAcQB/wNSAaMDgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wGdAoEB/wHb AeAB3gH/AZ4BowGhAf8B3wLhAf8B4AHiAeEB/wHfAeIB4AH/Ad4B4QHfAf8B4gHjAeIB/wHjAeUB4wH/ - AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBbgFcAf8BqwGKAYEB/wGdAoEB/wGPAYEBbwH/ - AZABgQFvAf8BkAGBAW8B/wGQAYEBbwH/AY8BgQFvAf8BgQFuAVwB/wPuAf8BgQGEAYIB/wFmAWgBkgH/ - AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/AiYBpAH/A0YBfggAAYEBhAGCAf8B+AHy - AewB/wGoAYEBaAH/AdABngGBAf8BxAGLAYEB/wG2AYEBagH/AbcBgQFqAf8BtwGBAWoB/wG3AYEBagH/ - AbYBgQFqAf8BqAGBAWgB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDuAH9A4EB/wGgAoEB/wGfAoEB/wGa + AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBbQFbAf8BqwGKAYEB/wGdAoEB/wGPAYEBbgH/ + AZABgQFuAf8BkAGBAW4B/wGQAYEBbgH/AY8BgQFuAf8BgQFtAVsB/wPuAf8BgQGEAYIB/wFlAWcBkgH/ + AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/A0YBfggAAYEBhAGCAf8B+AHy + AewB/wGoAYEBZwH/AdABngGBAf8BxAGLAYEB/wG2AYEBaQH/AbcBgQFpAf8BtwGBAWkB/wG3AYEBaQH/ + AbYBgQFpAf8BqAGBAWcB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDtwH9A4EB/wGgAoEB/wGfAoEB/wGa AoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8BtgG5AdYB/wKBAbsB/wKBAcwB/wKB - AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7gB/QOBAf8BoAKB + AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7cB/QOBAf8BoAKB Af8BnwKBAf8BmgKBAf8BmAKBAf8BlwKBAf8BmAKBAf8BmAKBAf8BmAKBAf8DgQH/Ad8B4gHhAf8BngGi AaAB/wGqAqsB/wGxArIB/wGuAq8B/wGyArMB/wG6ArsB/wGcAZ4BnQH/A2QB5wNAAW4DFgEfBAABgQGE - AYIB/wPuAf8BgQFuAVwB/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFs - Af8BjAGBAWwB/wGBAW4BXAH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC - Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFoAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG - AYEB/wG6AYEBcwH/AbIBgQFnAf8BtAGBAWcB/wGoAYEBaAH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ + AYIB/wPuAf8BgQFtAVsB/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFr + Af8BjAGBAWsB/wGBAW0BWwH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC + Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFnAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG + AYEB/wG6AYEBcgH/AbIBgQFmAf8BtAGBAWYB/wGoAYEBZwH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AwEBAgwAAzUBVQNfAfsDgQH/AawBiwGBAf8BqwGJAYEB/wGl AoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B2gLeAf8BggGGAaUB/wKBAZIB/wKB AZEB/wKBAZEB/wKBAZEB/wKBAZEB/wJUAVYBqwNFAXwDVwG6AzUBVgQAAzUBVQNfAfsDgQH/AawBiwGB Af8BqwGJAYEB/wGlAoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B3QHhAd8B/wGc AaABnwH/AZIBlQGUAf8BkgGVAZQB/wGQAZMBkgH/AZsBngGdAf8BlAGXAZYB/wNWAasDOwFkA1IBoQMr - AUIEAAGBAYQBggH/A/AB/wGBAW4BXAH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE - AYEB/wGmAYQBgQH/AZABgQF3Af8BgQFuAVwB/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ - A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFoAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB - Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXEB/wGoAYEBaAH/AfgB9AHuAf8BgQGEAYIB/wPr - Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3gB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ + AUIEAAGBAYQBggH/A/AB/wGBAW0BWwH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE + AYEB/wGmAYQBgQH/AZABgQF2Af8BgQFtAVsB/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ + A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFnAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB + Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXAB/wGoAYEBZwH/AfgB9AHuAf8BgQGEAYIB/wPr + Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3cB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOBAf8BvQHBAcAB/wGXAZsBmQH/ - AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3gB+AOB + AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3cB+AOB Af8BqwGJAYEB/wGpAYcBgQH/AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOB Af8BvQHBAcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQ - AZsDDQERBAABgQGEAYIB/wPzAf8BgQFuAVwB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi - AoEB/wGjAoEB/wGBAW4BXAH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBaAH/AckBlQGBAf8ByQGU - AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWgB/wH4AfQB7gH/ + AZsDDQERBAABgQGEAYIB/wPzAf8BgQFtAVsB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi + AoEB/wGjAoEB/wGBAW0BWwH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBZwH/AckBlQGBAf8ByQGU + AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWcB/wH4AfQB7gH/ AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9 EAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGBAf8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/ AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGqAagB/wGlAaoBqAH/AaUBqgGnAf8BpQGp AacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGB Af8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGq AagB/wGlAaoBqAH/AaUBqgGnAf8BpQGpAacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAABgQGEAYIB/wPt - Af8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGB - AW4BXAH/AYEBbgFcAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA - AYEBhAGCAf8B9wHxAesB/wGkAYEBYgH/AagBgQFoAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGB - AWgB/wGoAYEBaAH/AagBgQFoAf8BpAGBAWIB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ + Af8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGB + AW0BWwH/AYEBbQFbAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA + AYEBhAGCAf8B9wHxAesB/wGkAYEBYQH/AagBgQFnAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGB + AWcB/wGoAYEBZwH/AagBgQFnAf8BpAGBAWEB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/A9QB/wGBAYQBggH/EAADNQFVA2UB9AOBAf8BqAGGAYEB/wGn AYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGjAoEB/wGdAoEB/wGVAoEB/wOBAf8B0wHWAdUB/wGZ AZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/Ad8B4wHhAf8B3AHgAd4B/wHWAdkB2AH/AYEBhAGC Af8MAAM1AVUDZQH0A4EB/wGoAYYBgQH/AacBhQGBAf8BpgGEAYEB/wGlAYIBgQH/AaQCgQH/AaMCgQH/ AZ0CgQH/AZUCgQH/A4EB/wHTAdYB1QH/AZkBngGcAf8BxQHKAcgB/wHeAeIB4AH/Ad8B4wHhAf8B3wHj AeEB/wHcAeAB3gH/AdYB2QHYAf8BgQGEAYIB/wwAAYEBhAGCAf8BxwLIAf8D7QH/A+0B/wPtAf8D7QH/ - A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGB - AW4BXAH/AYEBbgFcAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 + A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGB + AW0BWwH/AYEBbQFbAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AdkC2gH/ - AYEBhAGCAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGkAYEBYgH/AfcB8QHrAf8BgQGE - AYIB/xAAAzUBVQNlAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ + AYEBhAGCAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGkAYEBYQH/AfcB8QHrAf8BgQGE + AYIB/xAAAzUBVQNjAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ AaACgQH/AZ8CgQH/A4EB/wHAAcMBwQH/AZYBmQGXAf8DgQH/A4EB/wOBAf8DgQH/A1kBwgHcAd8B3gH/ - AYEBhAGCAf8MAAM1AVUDZQHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh + AYEBhAGCAf8MAAM1AVUDYwHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh AoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/A4EB/wNZAcIB3AHf Ad4B/wGBAYQBggH/DAADVAGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBcgH/ - AZMBgQFyAf8BkwGBAXMB/wGUAYEBcgH/AYEBbgFcAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBcQH/ + AZMBgQFxAf8BkwGBAXIB/wGUAYEBcQH/AYEBbQFbAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFsAf8BugGBAWwB/wG6AYEBbQH/AbsBgQFsAf8BqAGB - AWgB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW + Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFrAf8BugGBAWsB/wG6AYEBbAH/AbsBgQFrAf8BqAGB + AWcB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW AoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGdAoEB/wHUAdYB1AH/AZsBnQGbAf8BlwKBAf8BmAKBAf8BlwKB Af8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/DAADNQFVA2UB9ANUAawBlgKBAf8BlgKBAf8BlgKB Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGbAZ0BmwH/AZcCgQH/ - AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBbgFc - Af8BqwGKAYEB/wGdAoEB/wGPAYEBbwH/AZABgQFvAf8BkAGBAW8B/wGQAYEBbwH/AY8BgQFvAf8BgQFu - AVwB/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBaAH/AdABngGBAf8BxAGLAYEB/wG2 - AYEBagH/AbcBgQFqAf8BtwGBAWoB/wG3AYEBagH/AbYBgQFqAf8BqAGBAWgB/wH4AfIB7AH/AYEBhAGC + AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBbQFb + Af8BqwGKAYEB/wGdAoEB/wGPAYEBbgH/AZABgQFuAf8BkAGBAW4B/wGQAYEBbgH/AY8BgQFuAf8BgQFt + AVsB/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBZwH/AdABngGBAf8BxAGLAYEB/wG2 + AYEBaQH/AbcBgQFpAf8BtwGBAWkB/wG3AYEBaQH/AbYBgQFpAf8BqAGBAWcB/wH4AfIB7AH/AYEBhAGC Af8QAAMhATADXgHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmwGfAZ0B/wGJAYcBgQH/AZQCgQH/AZMCgQH/ - AZQCgQH/AZQCgQH/AZsCgQH/A7gB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe + AZQCgQH/AZQCgQH/AZsCgQH/A7cB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ - AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO4Af0BgQGEAYIB/ygA - AYEBhAGCAf8D7gH/AYEBbgFcAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL - AYEBbAH/AYwBgQFsAf8BgQFuAVwB/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBaAH/ - Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXMB/wGyAYEBZwH/AbQBgQFnAf8BqAGB - AWgB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDagH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ - AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DeAH8AYEBhAGCAf8oAANSAakDagH1 + AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO3Af0BgQGEAYIB/ygA + AYEBhAGCAf8D7gH/AYEBbQFbAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL + AYEBawH/AYwBgQFrAf8BgQFtAVsB/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBZwH/ + Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXIB/wGyAYEBZgH/AbQBgQFmAf8BqAGB + AWcB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDaQH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ + AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DdwH8AYEBhAGCAf8oAANSAakDaQH1 AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKB - Af8BjgKBAf8DeAH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAW4BXAH/AaYBgwGBAf8BpQGEAYEB/wGm - AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF3Af8BgQFuAVwB/wPwAf8BgQGEAYIB/ywA - AYEBhAGCAf8B+AH0Ae4B/wGoAYEBaAH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY - AYEB/wHMAZgBgQH/AbcBgQFxAf8BqAGBAWgB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDawHxAYkCgQH/ + Af8BjgKBAf8DdwH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAW0BWwH/AaYBgwGBAf8BpQGEAYEB/wGm + AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF2Af8BgQFtAVsB/wPwAf8BgQGEAYIB/ywA + AYEBhAGCAf8B+AH0Ae4B/wGoAYEBZwH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY + AYEB/wHMAZgBgQH/AbcBgQFwAf8BqAGBAWcB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDaQHxAYkCgQH/ AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGOAoEB/wGLAoEB/wGX - AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNrAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE + AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNpAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE AYEB/wGmAYIBgQH/AZYCgQH/AY4CgQH/AYsCgQH/AZcCgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wPz - Af8BgQFuAVwB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAW4BXAH/ - A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFoAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB - Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBaAH/AfgB9AHuAf8BgQGEAYIB/ywA - A1IBqQNjAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb - AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNjAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ + Af8BgQFtAVsB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAW0BWwH/ + A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFnAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB + Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBZwH/AfgB9AHuAf8BgQGEAYIB/ywA + A1IBqQNhAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb + AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNhAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGbAoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygA - AYEBhAGCAf8D7QH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/ - AYEBbgFcAf8BgQFuAVwB/wGBAW4BXAH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFi - Af8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGoAYEBaAH/AagBgQFoAf8BqAGBAWgB/wGk - AYEBYgH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNiAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB + AYEBhAGCAf8D7QH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/ + AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFh + Af8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGk + AYEBYQH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNiAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB Af8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKBAf8DagH5AYEBhAGCAf8oAANSAakDYgHp AYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGkAYIBgQH/AaMCgQH/AaICgQH/AaECgQH/AaACgQH/AZ4CgQH/ AZACgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wHHAsgB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt Af8D7QH/A+0B/wHZAtoB/wGBAYQBggH/LAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/AfcB8QHrAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wHZAtoB/wGB - AYQBggH/LAADUgGpA2MB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8DYAHWA2sB8QGBAYQBggH/KAADUgGpA2MB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA2sB8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE + AYQBggH/LAADUgGpA2EB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8DYAHWA2kB8QGBAYQBggH/KAADUgGpA2EB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA2kB8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ AYEBhAGCAf8BgQGEAYIB/wNPAZcsAANUAawBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A08BlywA diff --git a/Sshfs/Sshfs/SftpContext.cs b/Sshfs/Sshfs/SftpContext.cs index 125cb4b..2c40bfd 100644 --- a/Sshfs/Sshfs/SftpContext.cs +++ b/Sshfs/Sshfs/SftpContext.cs @@ -26,9 +26,17 @@ internal sealed class SftpContext : IDisposable private SftpContextStream _stream; + public bool deleteOnCloseWorkaround = false; + public SftpContext(SftpFileAttributes attributes) { _attributes = attributes; + } + + public SftpContext(SftpFileAttributes attributes, bool aDeleteOnCloseWorkaround) + { + _attributes = attributes; + this.deleteOnCloseWorkaround = aDeleteOnCloseWorkaround; } public SftpContext(SftpSession session, string path, FileMode mode, FileAccess access, diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index a71c12a..5ef84ae 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -329,7 +329,8 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS return DokanError.ErrorFileNotFound; } - LogFSActionInit("OpenFile", fileName, (SftpContext)info.Context, "Mode:{0}", mode); + LogFSActionInit("OpenFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1}", mode,options); + string path = GetUnixPath(fileName); // var sftpFileAttributes = GetAttributes(path); @@ -362,7 +363,13 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS { //Log("JustInfo:{0},{1}", fileName, sftpFileAttributes.IsDirectory); info.IsDirectory = sftpFileAttributes.IsDirectory; - info.Context = new SftpContext(sftpFileAttributes); + + if (options.HasFlag(FileOptions.DeleteOnClose)) + { + info.DeleteOnClose = true;//dosnt work, is reset somewhere inside + } + info.Context = new SftpContext(sftpFileAttributes, options.HasFlag(FileOptions.DeleteOnClose)); + LogFSActionOther("OpenFile", fileName, (SftpContext)info.Context, "Dir open or get attrs"); return DokanError.ErrorSuccess; } @@ -510,14 +517,18 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) //Log("Cleanup:{0},Delete:{1}", info.Context,info.DeleteOnClose); LogFSActionInit("Cleanup", fileName, (SftpContext)info.Context, ""); + bool deleteOnCloseWorkAround = false; + if (info.Context != null) { + deleteOnCloseWorkAround = ((SftpContext)info.Context).deleteOnCloseWorkaround; + (info.Context as SftpContext).Release(); info.Context = null; } - if (info.DeleteOnClose) + if (info.DeleteOnClose || deleteOnCloseWorkAround) { string path = GetUnixPath(fileName); if (info.IsDirectory) @@ -547,7 +558,7 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) { //Log("Close:{0}", info.Context); LogFSActionInit("CloseFile", fileName, (SftpContext)info.Context, ""); - //???flush? + if (info.Context != null) { SftpContext context = (SftpContext) info.Context; @@ -557,11 +568,9 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) (info.Context as SftpContext).Stream.Dispose(); } } - //_cache.Remove(fileName); - /* cache reset for dir close is not good idea, will read it verz soon probablz again - CacheReset(GetUnixPath(fileName)); - */ + + /* cache reset for dir close is not good idea, will read it verz soon probablz again, */ if (!info.IsDirectory) { CacheReset(GetUnixPath(fileName)); @@ -838,7 +847,7 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList Date: Wed, 30 Jul 2014 14:00:41 +0200 Subject: [PATCH 050/134] added TODO list --- TODO | 1 + 1 file changed, 1 insertion(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 0000000..85123e2 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +- new connection not in VFS \ No newline at end of file From 3c10d050f753c0d45dae450f02109f3d8c38fdb3 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 30 Jul 2014 14:01:22 +0200 Subject: [PATCH 051/134] Match pattern using Dokan native method Is case insensitive, but drive works case sensitive. Probably not a problem --- DokanNet/Dokan.cs | 5 +++++ DokanNet/Native/NativeMethods.cs | 4 ++-- Sshfs/Sshfs/SftpFilesystem.cs | 5 +---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/DokanNet/Dokan.cs b/DokanNet/Dokan.cs index 838d7da..4d2abc1 100644 --- a/DokanNet/Dokan.cs +++ b/DokanNet/Dokan.cs @@ -136,6 +136,11 @@ public static int Version public static int DriverVersion { get { return (int) NativeMethods.DokanDriverVersion(); } + } + + public static bool IsNameInExpression(string expression, string name, bool ignoreCase) + { + return DokanNet.Native.NativeMethods.DokanIsNameInExpression(expression, name, ignoreCase); } } } \ No newline at end of file diff --git a/DokanNet/Native/NativeMethods.cs b/DokanNet/Native/NativeMethods.cs index 2bb7d73..c31f310 100644 --- a/DokanNet/Native/NativeMethods.cs +++ b/DokanNet/Native/NativeMethods.cs @@ -27,11 +27,11 @@ internal static class NativeMethods public static extern IntPtr DokanOpenRequestorToken(DokanFileInfo rawFileInfo); - /* + [DllImport("dokan.dll", CharSet = CharSet.Unicode)] public static extern bool DokanIsNameInExpression([MarshalAs(UnmanagedType.LPWStr)] string expression, // matching pattern [MarshalAs(UnmanagedType.LPWStr)] string name, // file name - [MarshalAs(UnmanagedType.Bool)] bool ignoreCase);*/ + [MarshalAs(UnmanagedType.Bool)] bool ignoreCase); } } \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 5ef84ae..5a30c67 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -942,17 +942,14 @@ DokanError IDokanOperations.FindFilesWithPattern(string fileName, string searchP //apply pattern List filteredfiles = new List(); - Regex repattern = new Regex("^"+Regex.Escape(searchPattern).Replace("\\*", ".*")+"$"); foreach(FileInformation fi in files){ - if (repattern.IsMatch(fi.FileName)) + if (Dokan.IsNameInExpression(searchPattern, fi.FileName, true)) { filteredfiles.Add(fi); LogFSActionOther("FindFilesPat", fileName, (SftpContext)info.Context, "Result:{0}", fi.FileName); } } files = filteredfiles; - /*not sure, whats right... if (files.Count == 0) - return DokanError.ErrorFileNotFound;*/ LogFSActionSuccess("FindFilesPat", fileName, (SftpContext)info.Context, "Pattern:{0} Count:{1}", searchPattern, files.Count); return DokanError.ErrorSuccess; From c6f8d661978aa93a232145813e238c5fdaeba7a5 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 30 Jul 2014 19:52:28 +0200 Subject: [PATCH 052/134] VFS drive bug, sorting of listview with drives --- Sshfs/Sshfs/MainForm.Designer.cs | 4 +- Sshfs/Sshfs/MainForm.cs | 18 ++- Sshfs/Sshfs/MainForm.resx | 192 +++++++++++++++---------------- TODO | 1 - 4 files changed, 113 insertions(+), 102 deletions(-) diff --git a/Sshfs/Sshfs/MainForm.Designer.cs b/Sshfs/Sshfs/MainForm.Designer.cs index 255fa4b..d4b4d20 100644 --- a/Sshfs/Sshfs/MainForm.Designer.cs +++ b/Sshfs/Sshfs/MainForm.Designer.cs @@ -117,7 +117,7 @@ private void InitializeComponent() this.fieldsPanel.ColumnCount = 3; this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 26.10063F)); this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 73.89937F)); - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 13F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 14F)); this.fieldsPanel.Controls.Add(this.nameBox, 1, 0); this.fieldsPanel.Controls.Add(this.label1, 0, 0); this.fieldsPanel.Controls.Add(this.hostBox, 1, 1); @@ -394,7 +394,7 @@ private void InitializeComponent() this.mountPointBox.Dock = System.Windows.Forms.DockStyle.Fill; this.mountPointBox.Location = new System.Drawing.Point(84, 267); this.mountPointBox.Name = "mountPointBox"; - this.mountPointBox.Size = new System.Drawing.Size(226, 20); + this.mountPointBox.Size = new System.Drawing.Size(225, 20); this.mountPointBox.TabIndex = 18; // // driveListView diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index bed9583..d103e00 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -97,8 +97,11 @@ protected override void OnLoad(EventArgs e) driveListView.BeginUpdate(); for (int i = 0; i < _drives.Count; i++) { - driveListView.Items.Add((_drives[i].Tag = - new ListViewItem(_drives[i].Name, 0) {Tag = _drives[i]}) as ListViewItem); + driveListView.Items.Add( + ( + _drives[i].Tag = new ListViewItem(_drives[i].Name, 0) {Tag = _drives[i]} + ) as ListViewItem + ); _drives[i].StatusChanged += drive_StatusChanged; if (_drives[i].Name.StartsWith("New Drive")) _namecount++; @@ -111,6 +114,7 @@ protected override void OnLoad(EventArgs e) driveListView.SelectedIndices.Add(0); } + driveListView.Sorting = SortOrder.Ascending; driveListView.EndUpdate(); @@ -264,8 +268,11 @@ private void addButton_Click(object sender, EventArgs e) Letter = letter, MountPoint = "" }; + + drive.StatusChanged += drive_StatusChanged; _drives.Add(drive); + this.virtualDrive.AddSubFS(drive); var item = (drive.Tag = new ListViewItem(drive.Name, 0) {Tag = drive, Selected = true}) as ListViewItem; @@ -277,6 +284,7 @@ private void addButton_Click(object sender, EventArgs e) SetupPanels(); _dirty = true; + } private void drive_StatusChanged(object sender, EventArgs e) @@ -327,6 +335,7 @@ private void removeButton_Click(object sender, EventArgs e) drive.StatusChanged -= drive_StatusChanged; drive.Unmount(); + virtualDrive.RemoveSubFS(drive); _drives.Remove(drive); @@ -413,8 +422,11 @@ private void saveButton_Click(object sender, EventArgs e) nameBox.Text = String.Format("{0}@'{1}'", userBox.Text, hostBox.Text); } - driveListView.SelectedItems[0].Text = drive.Name = nameBox.Text; + driveListView.SelectedItems[0].EnsureVisible(); + driveListView.Sorting = SortOrder.None; + driveListView.Sorting = SortOrder.Ascending; + drive.Host = hostBox.Text; drive.Port = (int) portBox.Value; drive.Username = userBox.Text; diff --git a/Sshfs/Sshfs/MainForm.resx b/Sshfs/Sshfs/MainForm.resx index 86fa340..13f860d 100644 --- a/Sshfs/Sshfs/MainForm.resx +++ b/Sshfs/Sshfs/MainForm.resx @@ -125,181 +125,181 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACi - LAAAAk1TRnQBSQFMAgEBBAEAAUQBAAFEAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg + LAAAAk1TRnQBSQFMAgEBBAEAAUwBAAFMAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ - AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABsAH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ - AoEBuAH/Al8BagHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK + AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABrwH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ + AoEBuAH/Al8BaAHzA0gBhAcAAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJAwcBCgMHAQoDBwEK AwgBCwMHAQoDBwEJAwUBBwMBAQIgAAMPARQDDwEUAxIBGAMeASsDJgE5AygBPAMoATwDJgE4AyIBMQMY - ASIDEAEWAxABFQMRARcDSAGDAiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/ + ASIDEAEWAxABFQMRARcDSAGDAiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/ A0YBfggAAw8BFAMPARQDEgEYAx4BKwMmATkDKAE8AygBPAMmATgDIgExAxgBIgMQARYDEAEVAxEBFwMH AQkDAgEDAwsBDwMWAR4DFgEfAwsBDwMBAQIQAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGz A1YBtANVAbUDVQG1A1UBtQNbAcgDagH5AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/AoEB0wH/ - AoEB0wH/AoEB1AH/AmQBaQHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 + AoEB0wH/AoEB1AH/AmQBZwHxBAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNWAbQDVQG1 A1UBtQNVAbUDVQG1A1YBtAM9AWgDDgETAwMBBBwAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8CJQGkAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHM - Af8CJQGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8CJAGkAf8CJAHMAf8CJAHMAf8CJAHMAf8CJAHMAf8CJAHMAf8CJAHMAf8CJAHM + Af8CJAGkAf8IAAGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AxkBIwMU - ARsDNQFWA2EB2gF0AnUB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg + ARsDNQFWA2EB2gFzAnQB+gNbAcgDHgErEAABqAGrAaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHg Af8B3wLhAf8B3wLhAf8B3wLhAf8B3wLhAf8B4QHiAeEB/wHeAeEB3wH/AeEB4gHhAf8BmAGaAc8B/wKB - AcwB/wJjAcwB/wKBAdEB/wKBAdAB/wJjAcwB/wJjAcwB/wKBAdQB/wJnAc0B/wKBAdMB/wKBAbgB/wQA + AcwB/wJiAcwB/wKBAdEB/wKBAdAB/wJiAcwB/wJiAcwB/wKBAdQB/wJmAc0B/wKBAdMB/wKBAbgB/wQA AagBqwGqAf8B5ALlAf8B4QHkAeMB/wHfAuEB/wHfAeEB4AH/Ad8C4QH/Ad8C4QH/Ad8C4QH/Ad8C4QH/ AeEB4gHhAf8B3gHhAd8B/wHhAeIB4QH/AeMB5QHkAf8B4QHjAeIB/wN/Af4DGAEiAwYBCBwAAYEBhAGC - Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AiUBpAH/ - AiUBzAn/AiUBzAn/AiUBzAH/AiUBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ + Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AiQBpAH/ + AiQBzAn/AiQBzAn/AiQBzAH/AiQBpAH/CAABgQGEAYIB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ Af8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8DgQH/A4EB/wOBAf8BnAKdAf8DtgH/AawCrQH/A1oBwBAA - A0ABbgNeAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv - Af8ByALJAf8CgQGhAf8CgQHMAf8CYwHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT - Af8CgQHTAf8CgQG4Af8EAANAAW4DXgHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr + A0ABbgNdAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGqAqsB/wO4Af8BrgKv + Af8ByALJAf8CgQGhAf8CgQHMAf8CYgHMAf8CgQHfAf8CwQHzAf8CgQHWAf8CgQHfAf8CzQH1Af8CgQHT + Af8CgQHTAf8CgQG4Af8EAANAAW4DXQHqAboCuwH/AbYCtwH/Aa0CrgH/AaoBqwGqAf8BqgKrAf8BqgKr Af8BqgKrAf8DuAH/Aa4CrwH/AcgCyQH/AZ0BnwGeAf8DYgHvA0kBiAMgAS4DBAEFHAABgQGEAYIB/wPz - Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CJQGkAf8CJQHM - Ff8CJQHMAf8CJQGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ + Af8D8AH/A9EB/wPsAf8D6wH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8CJAGkAf8CJAHM + Ff8CJAHMAf8CJAGkAf8IAAGBAYQBggH/A/MB/wPwAf8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/ A8UB/wPoAf8BgQGEAYIB/wOQAf8DswH/A4EB/wO2Af8D6wH/A9UB/wNqAfkQAAMIAQsDOAFdA38B/gGW AZoBmAH/AZUBmQGYAf8BkgGVAZQB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGRAZQBkwH/AZcBmgGZ - Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJjAcwB/wJzAc8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJj + Af8BmAGcAZsB/wKBAZQB/wKBAcwB/wJiAcwB/wJyAc8B/wKIAecB/wLaAfgB/wLNAfUB/wKBAd8B/wJi AcwB/wKBAdMB/wKBAbgB/wQAAwgBCwM4AV0DfwH+AZYBmgGYAf8BlQGZAZgB/wGSAZUBlAH/AZIBlQGU Af8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGaAZkB/wGYAZwBmwH/A14B1QM/AW0DTAGSA0cBgAMA AQEcAANPAZcBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQIlAaQB/wIlAcwB/wIlAcwN/wIl - AcwB/wIlAcwB/wIlAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A0UBfQIkAaQB/wIkAcwB/wIkAcwN/wIk + AcwB/wIkAcwB/wIkAaQB/wgAA08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9A4EB/wOB Af8DgQH/AaMCpAH/A9UB/wPXAf8DWQG/FAADAQECAwQBBQGVAZkBmAH/AcsBzwHOAf8B2wHdAdwB/wHY - AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmMBzAH/ - AmMBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmMBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ + AdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/AlQBVgGrAoEBzAH/AmIBzAH/ + AmIBzAH/AoEB3wX/AtoB+AH/AoEB1gH/AmIBzAH/AoEB0wH/AoEBuAH/CAADAQECAwQBBQGVAZkBmAH/ AcsBzwHOAf8B2wHdAdwB/wHYAdoB2QH/AcsBzwHOAf8BwAHEAcMB/wG3AbsBugH/A1wBzQGBAYQBggH/ A0oBiQNbAcQDXgHOAxkBIyAAAYEBhAGCAf8D1gH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt - Af8D7QH/A9QB/wGBAYQBggH/AiUBpAH/AiUBzBX/AiUBzAH/AiUBpAH/CAABgQGEAYIB/wPWAf8B9wHx + Af8D7QH/A9QB/wGBAYQBggH/AiQBpAH/AiQBzBX/AiQBzAH/AiQBpAH/CAABgQGEAYIB/wPWAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/ AfcB8QHrAf8D1AH/AYEBhAGCAf8EAAMFAQcDJgE4A4EB/wOBAf8DgQH/AxoBJRAAAx8BLANeAdUBoQGm AaQB/wGhAaUBowH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/ - AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJjAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB - AfMB/wJ1Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANeAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl + AaUBqQGnAf8BpQGpAacB/wKBAaYB/wKBAcwB/wJiAcwB/wKBAdsB/wLNAfUB/wKBAd8B/wKHAecB/wLB + AfMB/wJ0Ac8B/wKBAdMB/wKBAbgB/wQAAx8BLANeAdUBoQGmAaQB/wGhAaUBowH/AaEBpQGjAf8BoQGl AaMB/wGhAaUBowH/AaQBqAGnAf8BpQGqAagB/wGlAaoBqAH/AaUBqQGnAf8BpQGpAacB/wGgAaUBowH/ - A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBbQFb - Af8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGB - AW0BWwH/A+0B/wGBAYQBggH/AiUBpAH/AiUBzAn/AiUBzAn/AiUBzAH/AiUBpAH/CAABgQGEAYIB/wH6 - AfYB8gH/AaQBgQFhAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFn - Af8BqAGBAWcB/wGkAYEBYQH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA + A1sBwwMHAQkDBwEKAwcBCgMHAQoDCAELAwcBCgMHAQkDBAEGAwEBAgQAAYEBhAGCAf8D8wH/AYEBbAFa + Af8BgQFsAVoB/wGBAWwBWgH/AYEBbAFaAf8BgQFsAVoB/wGBAWwBWgH/AYEBbAFaAf8BgQFsAVoB/wGB + AWwBWgH/A+0B/wGBAYQBggH/AiQBpAH/AiQBzAn/AiQBzAn/AiQBzAH/AiQBpAH/CAABgQGEAYIB/wH6 + AfYB8gH/AaQBgQFgAf8BqAGBAWYB/wGoAYEBZgH/AagBgQFmAf8BqAGBAWYB/wGoAYEBZgH/AagBgQFm + Af8BqAGBAWYB/wGkAYEBYAH/AfcB8QHrAf8BgQGEAYIB/wMLAQ8DDAEQAwwBEAOBAf8D0wH/A4EB/xQA AzUBVQG/AcMBwQH/AdcB3AHZAf8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/ - Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJjAcwB/wKBAdcB/wKB - AdsB/wJjAcwB/wJzAc8B/wKBAeEB/wJ3AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ + Ad0B4gHfAf8B3wHkAeEB/wHfAeMB4QH/Ad8B4wHhAf8BjwGSAcYB/wKBAcwB/wJiAcwB/wKBAdcB/wKB + AdsB/wJiAcwB/wJyAc8B/wKBAeEB/wJ2AdAB/wKBAdMB/wKBAbgB/wQAAzUBVQG/AcMBwQH/AdcB3AHZ Af8B4gHmAeQB/wHiAeYB5AH/AeAB5AHiAf8BxgHLAcgB/wHFAcoByAH/Ad0B4gHfAf8B3wHkAeEB/wHf AeMB4QH/Ad8B4wHhAf8B1gHaAdgB/wGcAaEBnwH/A1YBtANWAbQDVQG1A1UBtQNVAbUDVQG1A1YBswMx - AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFtAVsB/wGTAYEBcQH/AZMBgQFxAf8BlAGBAXEB/wGTAYEBcQH/ - AZMBgQFxAf8BkwGBAXIB/wGUAYEBcQH/AYEBbQFbAf8D7gH/AYEBhAGCAf8CJQGkAf8CJQHMAf8CJQHM - Af8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQHMAf8CJQGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB - AWcB/wG6AYEBawH/AboBgQFrAf8BuwGBAWsB/wG6AYEBawH/AboBgQFrAf8BugGBAWwB/wG7AYEBawH/ - AagBgQFnAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA + AU4DCwEPBAABgQGEAYIB/wPuAf8BgQFsAVoB/wGTAYEBcAH/AZMBgQFwAf8BlAGBAXAB/wGTAYEBcAH/ + AZMBgQFwAf8BkwGBAXEB/wGUAYEBcAH/AYEBbAFaAf8D7gH/AYEBhAGCAf8CJAGkAf8CJAHMAf8CJAHM + Af8CJAHMAf8CJAHMAf8CJAHMAf8CJAHMAf8CJAHMAf8CJAGkAf8IAAGBAYQBggH/AfgB8gHsAf8BqAGB + AWYB/wG6AYEBagH/AboBgQFqAf8BuwGBAWoB/wG6AYEBagH/AboBgQFqAf8BugGBAWsB/wG7AYEBagH/ + AagBgQFmAf8B+AHyAewB/wGBAYQBggH/AzgBXgM4AV4DNgFYA4EB/wHHAsgB/wOBAf8DAwEEAwUBBwwA AzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/AZMBlgHK - Af8CgQHMAf8CaAHNAf8CYwHMAf8CYwHMAf8CYwHMAf8CYwHMAf8CYwHMAf8CYwHMAf8CgQHTAf8CQAGw + Af8CgQHMAf8CZwHNAf8CYgHMAf8CYgHMAf8CYgHMAf8CYgHMAf8CYgHMAf8CYgHMAf8CgQHTAf8CQAGv Af0EAAM1AVUBwgHFAcQB/wNSAaMDgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wGdAoEB/wHb AeAB3gH/AZ4BowGhAf8B3wLhAf8B4AHiAeEB/wHfAeIB4AH/Ad4B4QHfAf8B4gHjAeIB/wHjAeUB4wH/ - AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBbQFbAf8BqwGKAYEB/wGdAoEB/wGPAYEBbgH/ - AZABgQFuAf8BkAGBAW4B/wGQAYEBbgH/AY8BgQFuAf8BgQFtAVsB/wPuAf8BgQGEAYIB/wFlAWcBkgH/ - AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/AiUBpAH/A0YBfggAAYEBhAGCAf8B+AHy - AewB/wGoAYEBZwH/AdABngGBAf8BxAGLAYEB/wG2AYEBaQH/AbcBgQFpAf8BtwGBAWkB/wG3AYEBaQH/ - AbYBgQFpAf8BqAGBAWcB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE - AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDtwH9A4EB/wGgAoEB/wGfAoEB/wGa + AeEB4wHiAf8DVwG4AxUBHQQAAYEBhAGCAf8D7gH/AYEBbAFaAf8BqwGKAYEB/wGdAoEB/wGPAYEBbQH/ + AZABgQFtAf8BkAGBAW0B/wGQAYEBbQH/AY8BgQFtAf8BgQFsAVoB/wPuAf8BgQGEAYIB/wFkAWYBkgH/ + AiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/AiQBpAH/A0YBfggAAYEBhAGCAf8B+AHy + AewB/wGoAYEBZgH/AdABngGBAf8BxAGLAYEB/wG2AYEBaAH/AbcBgQFoAf8BtwGBAWgB/wG3AYEBaAH/ + AbYBgQFoAf8BqAGBAWYB/wH4AfIB7AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGE + AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wMBAQIMAAM1AVUDtgH9A4EB/wGgAoEB/wGfAoEB/wGa AoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8BtgG5AdYB/wKBAbsB/wKBAcwB/wKB - AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7cB/QOBAf8BoAKB + AcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wKBAcwB/wNfAfsDVgGzBAADNQFVA7YB/QOBAf8BoAKB Af8BnwKBAf8BmgKBAf8BmAKBAf8BlwKBAf8BmAKBAf8BmAKBAf8BmAKBAf8DgQH/Ad8B4gHhAf8BngGi AaAB/wGqAqsB/wGxArIB/wGuAq8B/wGyArMB/wG6ArsB/wGcAZ4BnQH/A2QB5wNAAW4DFgEfBAABgQGE - AYIB/wPuAf8BgQFtAVsB/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFr - Af8BjAGBAWsB/wGBAW0BWwH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC - Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFnAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG - AYEB/wG6AYEBcgH/AbIBgQFmAf8BtAGBAWYB/wGoAYEBZwH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ + AYIB/wPuAf8BgQFsAVoB/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFq + Af8BjAGBAWoB/wGBAWwBWgH/A+8B/wGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGC + Af8DAQECDAABgQGEAYIB/wH4AfIB7AH/AagBgQFmAf8BzgGbAYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGG + AYEB/wG6AYEBcQH/AbIBgQFlAf8BtAGBAWUB/wGoAYEBZgH/AfgB8wHtAf8BgQGEAYIB/wPZAf8D2QH/ A9kB/wPZAf8D2QH/A9kB/wGBAYQBggH/AwEBAgwAAzUBVQNfAfsDgQH/AawBiwGBAf8BqwGJAYEB/wGl AoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B2gLeAf8BggGGAaUB/wKBAZIB/wKB AZEB/wKBAZEB/wKBAZEB/wKBAZEB/wJUAVYBqwNFAXwDVwG6AzUBVgQAAzUBVQNfAfsDgQH/AawBiwGB Af8BqwGJAYEB/wGlAoEB/wGZAoEB/wGVAoEB/wGTAoEB/wGTAoEB/wGUAoEB/wOBAf8B3QHhAd8B/wGc AaABnwH/AZIBlQGUAf8BkgGVAZQB/wGQAZMBkgH/AZsBngGdAf8BlAGXAZYB/wNWAasDOwFkA1IBoQMr - AUIEAAGBAYQBggH/A/AB/wGBAW0BWwH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE - AYEB/wGmAYQBgQH/AZABgQF2Af8BgQFtAVsB/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ - A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFnAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB - Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAXAB/wGoAYEBZwH/AfgB9AHuAf8BgQGEAYIB/wPr - Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3cB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ + AUIEAAGBAYQBggH/A/AB/wGBAWwBWgH/AaYBgwGBAf8BpQGEAYEB/wGmAYQBgQH/AaUBhAGBAf8BpgGE + AYEB/wGmAYQBgQH/AZABgQF1Af8BgQFsAVoB/wPwAf8BgQGEAYIB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ + A+gB/wGBAYQBggH/EAABgQGEAYIB/wH4AfQB7gH/AagBgQFmAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGB + Af8BywGYAYEB/wHMAZgBgQH/AcwBmAGBAf8BtwGBAW8B/wGoAYEBZgH/AfgB9AHuAf8BgQGEAYIB/wPr + Af8D6wH/A8gB/wPFAf8DxQH/A+gB/wGBAYQBggH/EAADNQFVA3YB+AOBAf8BqwGJAYEB/wGpAYcBgQH/ AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOBAf8BvQHBAcAB/wGXAZsBmQH/ - AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3cB+AOB + AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQAZsDDQERBAADNQFVA3YB+AOB Af8BqwGJAYEB/wGpAYcBgQH/AakBhwGBAf8BqAGFAYEB/wGaAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wOB Af8BvQHBAcAB/wGXAZsBmQH/AcgBzAHLAf8BvgHCAcEB/wG1AbkBuAH/A1YBswNeAdUDTQGRA14B2QNQ - AZsDDQERBAABgQGEAYIB/wPzAf8BgQFtAVsB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi - AoEB/wGjAoEB/wGBAW0BWwH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBZwH/AckBlQGBAf8ByQGU - AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWcB/wH4AfQB7gH/ + AZsDDQERBAABgQGEAYIB/wPzAf8BgQFsAVoB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGi + AoEB/wGjAoEB/wGBAWwBWgH/A/AB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/A0UBfRAAAYEBhAGCAf8B+gH2AfIB/wGoAYEBZgH/AckBlQGBAf8ByQGU + AYEB/wHJAZQBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8BqAGBAWYB/wH4AfQB7gH/ AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9 EAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGBAf8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/ AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGqAagB/wGlAaoBqAH/AaUBqgGnAf8BpQGp AacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAADNQFVA2IB9gOBAf8BqgGIAYEB/wGoAYcBgQH/AacBhgGB Af8BpwGEAYEB/wGmAYIBgQH/AZkCgQH/AY8CgQH/AYwCgQH/A4EB/wHTAdYB1AH/AZkBngGcAf8BpQGq AagB/wGlAaoBqAH/AaUBqgGnAf8BpQGpAacB/wGlAakBpwH/AZwBoAGeAf8DVAGmDAABgQGEAYIB/wPt - Af8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGB - AW0BWwH/AYEBbQFbAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA - AYEBhAGCAf8B9wHxAesB/wGkAYEBYQH/AagBgQFnAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGB - AWcB/wGoAYEBZwH/AagBgQFnAf8BpAGBAWEB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ + Af8BgQFsAVoB/wGBAWwBWgH/AYEBbAFaAf8BgQFsAVoB/wGBAWwBWgH/AYEBbAFaAf8BgQFsAVoB/wGB + AWwBWgH/AYEBbAFaAf8D8AH/AYEBhAGCAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPUAf8BgQGEAYIB/xAA + AYEBhAGCAf8B9wHxAesB/wGkAYEBYAH/AagBgQFmAf8BqAGBAWYB/wGoAYEBZgH/AagBgQFmAf8BqAGB + AWYB/wGoAYEBZgH/AagBgQFmAf8BpAGBAWAB/wH4AfQB7gH/AYEBhAGCAf8B9wHxAesB/wH3AfEB6wH/ AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/A9QB/wGBAYQBggH/EAADNQFVA2UB9AOBAf8BqAGGAYEB/wGn AYUBgQH/AaYBhAGBAf8BpQGCAYEB/wGkAoEB/wGjAoEB/wGdAoEB/wGVAoEB/wOBAf8B0wHWAdUB/wGZ AZ4BnAH/AcUBygHIAf8B3gHiAeAB/wHfAeMB4QH/Ad8B4wHhAf8B3AHgAd4B/wHWAdkB2AH/AYEBhAGC Af8MAAM1AVUDZQH0A4EB/wGoAYYBgQH/AacBhQGBAf8BpgGEAYEB/wGlAYIBgQH/AaQCgQH/AaMCgQH/ AZ0CgQH/AZUCgQH/A4EB/wHTAdYB1QH/AZkBngGcAf8BxQHKAcgB/wHeAeIB4AH/Ad8B4wHhAf8B3wHj AeEB/wHcAeAB3gH/AdYB2QHYAf8BgQGEAYIB/wwAAYEBhAGCAf8BxwLIAf8D7QH/A+0B/wPtAf8D7QH/ - A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGB - AW0BWwH/AYEBbQFbAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 + A+0B/wPtAf8D7QH/A+0B/wPtAf8B2QLaAf8BgQGEAYIB/wGBAWwBWgH/AYEBbAFaAf8BgQFsAVoB/wGB + AWwBWgH/AYEBbAFaAf8D7QH/AYEBhAGCAf8QAAGBAYQBggH/AccCyAH/AfcB8QHrAf8B9wHxAesB/wH3 AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AdkC2gH/ - AYEBhAGCAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGkAYEBYQH/AfcB8QHrAf8BgQGE - AYIB/xAAAzUBVQNjAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ + AYEBhAGCAf8BqAGBAWYB/wGoAYEBZgH/AagBgQFmAf8BqAGBAWYB/wGkAYEBYAH/AfcB8QHrAf8BgQGE + AYIB/xAAAzUBVQNhAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ AaACgQH/AZ8CgQH/A4EB/wHAAcMBwQH/AZYBmQGXAf8DgQH/A4EB/wOBAf8DgQH/A1kBwgHcAd8B3gH/ - AYEBhAGCAf8MAAM1AVUDYwHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh + AYEBhAGCAf8MAAM1AVUDYQHwA4EB/wGnAYUBgQH/AaUBhAGBAf8BpAGDAYEB/wGkAoEB/wGiAoEB/wGh AoEB/wGgAoEB/wGfAoEB/wOBAf8BwAHDAcEB/wGWAZkBlwH/A4EB/wOBAf8DgQH/A4EB/wNZAcIB3AHf Ad4B/wGBAYQBggH/DAADVAGsAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBcQH/ - AZMBgQFxAf8BkwGBAXIB/wGUAYEBcQH/AYEBbQFbAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGHAoEB/wGTAYEBcAH/ + AZMBgQFwAf8BkwGBAXEB/wGUAYEBcAH/AYEBbAFaAf8D7gH/AYEBhAGCAf8QAANUAawBgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFrAf8BugGBAWsB/wG6AYEBbAH/AbsBgQFrAf8BqAGB - AWcB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW + Af8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AboBgQFqAf8BugGBAWoB/wG6AYEBawH/AbsBgQFqAf8BqAGB + AWYB/wH4AfIB7AH/AYEBhAGCAf8QAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGW AoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGdAoEB/wHUAdYB1AH/AZsBnQGbAf8BlwKBAf8BmAKBAf8BlwKB Af8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/DAADNQFVA2UB9ANUAawBlgKBAf8BlgKBAf8BlgKB Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BnQKBAf8B1AHWAdQB/wGbAZ0BmwH/AZcCgQH/ - AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBbQFb - Af8BqwGKAYEB/wGdAoEB/wGPAYEBbgH/AZABgQFuAf8BkAGBAW4B/wGQAYEBbgH/AY8BgQFuAf8BgQFt - AVsB/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBZwH/AdABngGBAf8BxAGLAYEB/wG2 - AYEBaQH/AbcBgQFpAf8BtwGBAWkB/wG3AYEBaQH/AbYBgQFpAf8BqAGBAWcB/wH4AfIB7AH/AYEBhAGC + AZgCgQH/AZcCgQH/AZgCgQH/AZsCgQH/AdwB3wHdAf8BgQGEAYIB/ygAAYEBhAGCAf8D7gH/AYEBbAFa + Af8BqwGKAYEB/wGdAoEB/wGPAYEBbQH/AZABgQFtAf8BkAGBAW0B/wGQAYEBbQH/AY8BgQFtAf8BgQFs + AVoB/wPuAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBZgH/AdABngGBAf8BxAGLAYEB/wG2 + AYEBaAH/AbcBgQFoAf8BtwGBAWgB/wG3AYEBaAH/AbYBgQFoAf8BqAGBAWYB/wH4AfIB7AH/AYEBhAGC Af8QAAMhATADXgHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmwGfAZ0B/wGJAYcBgQH/AZQCgQH/AZMCgQH/ - AZQCgQH/AZQCgQH/AZsCgQH/A7cB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe + AZQCgQH/AZQCgQH/AZsCgQH/A7YB/QGBAYQBggH/DAADIQEwA14B2QGaAZ4BnQH/AZoBngGdAf8BmgGe AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ - AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO3Af0BgQGEAYIB/ygA - AYEBhAGCAf8D7gH/AYEBbQFbAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL - AYEBawH/AYwBgQFrAf8BgQFtAVsB/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBZwH/ - Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXIB/wGyAYEBZgH/AbQBgQFmAf8BqAGB - AWcB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDaQH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ - AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DdwH8AYEBhAGCAf8oAANSAakDaQH1 + AZsBnwGdAf8BiQGHAYEB/wGUAoEB/wGTAoEB/wGUAoEB/wGUAoEB/wGbAoEB/wO2Af0BgQGEAYIB/ygA + AYEBhAGCAf8D7gH/AYEBbAFaAf8BqQGHAYEB/wGpAYcBgQH/AagBhwGBAf8BmgKBAf8BkwGBAYAB/wGL + AYEBagH/AYwBgQFqAf8BgQFsAVoB/wPvAf8BgQGEAYIB/ywAAYEBhAGCAf8B+AHyAewB/wGoAYEBZgH/ + Ac4BmwGBAf8BzgGbAYEB/wHOAZsBgQH/AcIBhgGBAf8BugGBAXEB/wGyAYEBZQH/AbQBgQFlAf8BqAGB + AWYB/wH4AfMB7QH/AYEBhAGCAf8sAANSAakDaAH1AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/ + AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKBAf8BjgKBAf8DdgH8AYEBhAGCAf8oAANSAakDaAH1 AYkCgQH/AaoBiAGBAf8BqQGHAYEB/wGpAYYBgQH/AagBhQGBAf8BkwKBAf8BkAKBAf8BkAKBAf8BkAKB - Af8BjgKBAf8DdwH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAW0BWwH/AaYBgwGBAf8BpQGEAYEB/wGm - AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF2Af8BgQFtAVsB/wPwAf8BgQGEAYIB/ywA - AYEBhAGCAf8B+AH0Ae4B/wGoAYEBZwH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY - AYEB/wHMAZgBgQH/AbcBgQFwAf8BqAGBAWcB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDaQHxAYkCgQH/ + Af8BjgKBAf8DdgH8AYEBhAGCAf8oAAGBAYQBggH/A/AB/wGBAWwBWgH/AaYBgwGBAf8BpQGEAYEB/wGm + AYQBgQH/AaUBhAGBAf8BpgGEAYEB/wGmAYQBgQH/AZABgQF1Af8BgQFsAVoB/wPwAf8BgQGEAYIB/ywA + AYEBhAGCAf8B+AH0Ae4B/wGoAYEBZgH/AcwBlwGBAf8BywGYAYEB/wHMAZgBgQH/AcsBmAGBAf8BzAGY + AYEB/wHMAZgBgQH/AbcBgQFvAf8BqAGBAWYB/wH4AfQB7gH/AYEBhAGCAf8sAANSAakDZwHxAYkCgQH/ AakBhwGBAf8BqAGHAYEB/wGnAYYBgQH/AaYBhAGBAf8BpgGCAYEB/wGWAoEB/wGOAoEB/wGLAoEB/wGX - AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNpAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE + AoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNnAfEBiQKBAf8BqQGHAYEB/wGoAYcBgQH/AacBhgGBAf8BpgGE AYEB/wGmAYIBgQH/AZYCgQH/AY4CgQH/AYsCgQH/AZcCgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wPz - Af8BgQFtAVsB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAW0BWwH/ - A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFnAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB - Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBZwH/AfgB9AHuAf8BgQGEAYIB/ywA + Af8BgQFsAVoB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGjAoEB/wGiAoEB/wGjAoEB/wGBAWwBWgH/ + A/AB/wGBAYQBggH/LAABgQGEAYIB/wH6AfYB8gH/AagBgQFmAf8ByQGVAYEB/wHJAZQBgQH/AckBlAGB + Af8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBZgH/AfgB9AHuAf8BgQGEAYIB/ywA A1IBqQNhAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGb AoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygAA1IBqQNhAe4BiAKBAf8BpwGGAYEB/wGnAYUBgQH/ AaYBhAGBAf8BpAGCAYEB/wGkAoEB/wGiAoEB/wGbAoEB/wGSAoEB/wGXAoEB/wNtAfcBgQGEAYIB/ygA - AYEBhAGCAf8D7QH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/ - AYEBbQFbAf8BgQFtAVsB/wGBAW0BWwH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFh - Af8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGoAYEBZwH/AagBgQFnAf8BqAGBAWcB/wGk - AYEBYQH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNiAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB + AYEBhAGCAf8D7QH/AYEBbAFaAf8BgQFsAVoB/wGBAWwBWgH/AYEBbAFaAf8BgQFsAVoB/wGBAWwBWgH/ + AYEBbAFaAf8BgQFsAVoB/wGBAWwBWgH/A/AB/wGBAYQBggH/LAABgQGEAYIB/wH3AfEB6wH/AaQBgQFg + Af8BqAGBAWYB/wGoAYEBZgH/AagBgQFmAf8BqAGBAWYB/wGoAYEBZgH/AagBgQFmAf8BqAGBAWYB/wGk + AYEBYAH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNiAekBiAKBAf8BpgGEAYEB/wGlAYQBgQH/AaQBggGB Af8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKBAf8DagH5AYEBhAGCAf8oAANSAakDYgHp AYgCgQH/AaYBhAGBAf8BpQGEAYEB/wGkAYIBgQH/AaMCgQH/AaICgQH/AaECgQH/AaACgQH/AZ4CgQH/ AZACgQH/A2oB+QGBAYQBggH/KAABgQGEAYIB/wHHAsgB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt Af8D7QH/A+0B/wHZAtoB/wGBAYQBggH/LAABgQGEAYIB/wHHAsgB/wH3AfEB6wH/AfcB8QHrAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wHZAtoB/wGB AYQBggH/LAADUgGpA2EB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8DYAHWA2kB8QGBAYQBggH/KAADUgGpA2EB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB - Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA2kB8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE + Af8BlgKBAf8DYAHWA2cB8QGBAYQBggH/KAADUgGpA2EB7gNbAcgBlgKBAf8BlgKBAf8BlgKBAf8BlgKB + Af8BlgKBAf8BlgKBAf8BlgKBAf8BlgKBAf8DYAHWA2cB8QGBAYQBggH/KAADVAGsAYEBhAGCAf8BgQGE AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ AYEBhAGCAf8BgQGEAYIB/wNPAZcsAANUAawBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/A08BlywA diff --git a/TODO b/TODO index 85123e2..e69de29 100644 --- a/TODO +++ b/TODO @@ -1 +0,0 @@ -- new connection not in VFS \ No newline at end of file From 8fac74c6f92dc7d03f3330f23431abfd6c91c1b2 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 30 Jul 2014 20:01:27 +0200 Subject: [PATCH 053/134] version repair (1.5.12.5) --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 4 ++-- Sshfs/Sshfs/Sshfs.csproj | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index ed43730..a7e2f10 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.5.12")] -[assembly: AssemblyFileVersion("0.1.5.12")] +[assembly: AssemblyVersion("1.5.12.5")] +[assembly: AssemblyFileVersion("1.5.12.5")] diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 1ae0456..9782573 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -15,7 +15,7 @@ 512 true - X:\public\winsshfs\ + w:\dev\default\public\winsshfs\ true Web true @@ -34,8 +34,8 @@ true index.html false - 12 - 0.1.5.12 + 5 + 1.5.12.5 true true true From d2700bfbd4cb1354a70ab49c605b52273d2236bc Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 30 Jul 2014 20:01:27 +0200 Subject: [PATCH 054/134] v 1.5.12.5, new readme --- README.md | 25 ++++++++++++++++++++++--- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 4 ++-- Sshfs/Sshfs/Sshfs.csproj | 6 +++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ea1fc26..55c141e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,23 @@ -win-sshfs -========= +WinSshFS 4every1 edition +======================== -Git repo for http://code.google.com/p/win-sshfs/ \ No newline at end of file +I decided to share my clone of win-sshfs based on +I did some improvments for my needs. Current devel branch version seems to be very stable. + +There are several changes, main differences: + +* current Renci SSH (2014.4.6beta) +* solved few bugs like payload, 2 hosts and others +* Puttyant support +* settings files location is in fixed place (%USER%/APPDATA/winsshfs) +* "spooldrive" - all remote hosts can by mount as mountpoint dir in one virtual drive +* archive flag of file in windows represents and controls permission for group: + * ON => group have same rights as owner + * OFF => same rights as others) +* I use different versioning: 1.5.12.5 = version.subversion.release.build + +And probably others , see logs for details. + + + + \ No newline at end of file diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index ed43730..a7e2f10 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.5.12")] -[assembly: AssemblyFileVersion("0.1.5.12")] +[assembly: AssemblyVersion("1.5.12.5")] +[assembly: AssemblyFileVersion("1.5.12.5")] diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 1ae0456..9782573 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -15,7 +15,7 @@ 512 true - X:\public\winsshfs\ + w:\dev\default\public\winsshfs\ true Web true @@ -34,8 +34,8 @@ true index.html false - 12 - 0.1.5.12 + 5 + 1.5.12.5 true true true From b45b159868f77452f068492a281a98ecd1781418 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Fri, 15 Aug 2014 09:25:10 +0200 Subject: [PATCH 055/134] Fixed socket end symlinks interpretation --- Sshfs/Sshfs/SftpFilesystem.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 5a30c67..840c9ea 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -807,7 +807,7 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList Date: Thu, 16 Oct 2014 20:14:26 +0200 Subject: [PATCH 056/134] Swap to public key without password --- .gitignore | 2 + DokanNet/DokanNet.csproj | 325 +++++++++++++++--------------- DokanNet/WinSSH4e1-public.snk | Bin 0 -> 596 bytes DokanNet/sshfs_4every1.pfx | Bin 1764 -> 0 bytes Renci.SshNet/Renci.SshNet.csproj | 3 +- Renci.SshNet/Sftp/SftpSession.cs | 3 +- Renci.SshNet/WinSSH4e1-public.snk | Bin 0 -> 596 bytes Renci.SshNet/sshfs_4every1.pfx | Bin 1764 -> 0 bytes Sshfs/Sshfs/Sshfs.csproj | 5 +- Sshfs/Sshfs/VirtualDrive.cs | 24 +-- Sshfs/Sshfs/WinSSH4e1-public.snk | Bin 0 -> 596 bytes Sshfs/Sshfs/sshfs_4every1.pfx | Bin 1764 -> 0 bytes Sshfs/showPublicKeyFile.bat | 4 + Sshfs/sshfs_4every1.pfx | Bin 1764 -> 0 bytes 14 files changed, 188 insertions(+), 178 deletions(-) create mode 100644 DokanNet/WinSSH4e1-public.snk delete mode 100644 DokanNet/sshfs_4every1.pfx create mode 100644 Renci.SshNet/WinSSH4e1-public.snk delete mode 100644 Renci.SshNet/sshfs_4every1.pfx create mode 100644 Sshfs/Sshfs/WinSSH4e1-public.snk delete mode 100644 Sshfs/Sshfs/sshfs_4every1.pfx create mode 100644 Sshfs/showPublicKeyFile.bat delete mode 100644 Sshfs/sshfs_4every1.pfx diff --git a/.gitignore b/.gitignore index fe7fcd9..9526e41 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ TestResults /Sshfs/Sshfs/publish /FreeTDS-0.91.98-x86.zip /README.zip +/Dokan +/bluescreenview diff --git a/DokanNet/DokanNet.csproj b/DokanNet/DokanNet.csproj index edbaab0..ea7cc92 100644 --- a/DokanNet/DokanNet.csproj +++ b/DokanNet/DokanNet.csproj @@ -1,169 +1,170 @@ - - - - Debug - AnyCPU - 8.0.50727 - 2.0 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C} - Library - Properties - DokanNet - DokanNet - - - 3.5 - v4.5 - - - - - - - - - - - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - true - full - true - bin\Debug\ - DEBUG - prompt - 4 - AllRules.ruleset - false - x86 - false - - - pdbonly - true - bin\Release\ - - - prompt - 4 - AllRules.ruleset - AnyCPU - false - - - - - - - true - bin\x86\Debug\ - TRACE;DEBUG;DEBUGDOKAN - full - x86 - bin\Debug\DokanNet.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - AllRules.ruleset - ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - false - false - false - - - bin\x86\Release\ - - - true - pdbonly - x86 - bin\Release\DokanNet.dll.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - AllRules.ruleset - ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - false - ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - false - false - - - true - - - sshfs_4every1.pfx - - - - - - - - - - - - - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - - - - - - - - - - + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C} + Library + Properties + DokanNet + DokanNet + + + 3.5 + v4.5 + + + + + + + + + + + + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + true + bin\Debug\ + DEBUG + prompt + 4 + AllRules.ruleset + false + x86 + false + + + pdbonly + true + bin\Release\ + + + prompt + 4 + AllRules.ruleset + AnyCPU + false + + + + + + + true + bin\x86\Debug\ + TRACE;DEBUG;DEBUGDOKAN + full + x86 + bin\Debug\DokanNet.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + false + false + false + + + bin\x86\Release\ + + + true + pdbonly + x86 + bin\Release\DokanNet.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + AllRules.ruleset + ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + false + ;E:\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + false + false + + + true + + + WinSSH4e1-public.snk + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + + + + --> \ No newline at end of file diff --git a/DokanNet/WinSSH4e1-public.snk b/DokanNet/WinSSH4e1-public.snk new file mode 100644 index 0000000000000000000000000000000000000000..d64facbaf6f8f1f9124741c0367df314668125a4 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096~H-={bxh^^?U0dKOEZYw+ZK^0WCK78f z7Lrx7#Uk|;lhIpUvmK<}zCLdC*|-V?-SpA1yrIT?rYrEU#L=?qJwiePdLUA>!^;=B zfOn1-4tXfdY3vNh`dPAu;xwL^~P?JIdU4St=Tj*(CA!$qnew}95%+cH!0c3Bg3`d*cC-w>a3fT0F1ahZx9KZ#eoLwGke21WKd4*D>I$(09HLhOz0@fP1KOnwFxl^Z)16f z?rVd*?%Y48*ZKWd?9+TH@dl^q8;3J=ZrYa{2ux$JDzS1+L5Mv7g7J|Wvtf+(98@1@ z?rG@%95`MPbizi^%dnm1&zu~RF%BG&G`n6O)h-NOD&dcb@w!fA zIAayDAeiTBmR2ucNg(^qk92(4@^j5Ggi| iY+5nt3VuPH#1G#Kje%Vk>Z4#+xAJ6rcY0XH3vUg9v>|{1 literal 0 HcmV?d00001 diff --git a/DokanNet/sshfs_4every1.pfx b/DokanNet/sshfs_4every1.pfx deleted file mode 100644 index 7e733984409fe3dc7a316d99e4b77ccb53671ebf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1764 zcmY*Yc{JPU8vYWIL}*eYmI{(yYpFI-OEA@1QekXeYz;!FNX@lX(@R6$khB%FgFzLs zbYf2_Lu0K}DIJE^!L*Jt4BBg2%e2)wE%#^6y)$Rdci#7X-sgGW?|grJ51EgggToZa zd}Iky$y7)ee%J#;!OHo_Yy=;99ijzfzCzpIB!y}OU!e-3#ZcTvD*taw1p|YZ^Wkk| zKKve82Z{MV4PA~!pvx^v=3&G)3^*J$hTy|*qLux)+U-``|3pOC#F`uzp5G3rEOVcu)lPvA-*L_ z{p7syn0=SK2QOZk$ZWEHWY;@eQHf}--!61tz*!v$aQmuj!&cwvUO_u0&VA*^+{Jf6 zTFEFKRU`|C$yK!(Bk7zrl|?rgt&aPZu!uq8?YM~Aba@zE2rH@=LPTkx=$86T>Tzxua6WJsq-6L_TapbR>NVuo^IbvQQ7~E;q%g> zQ4(cdN6UDiW>PWe+tf&U=HmVBHrpQbdiBkm7<|j{G6g|TFta_l&Wx{p>ef-_sL;wR z-aiEI4n}s`tXJy_RL%~CI2o`4Ig!SG+;*D9bO?Hb+V$EK7g}n@RnAV8UCxSLONfNSf}M+?v`~1+0+ehSE|~Jw^96gTnF~@`ok5=-^RSYL z7VW)G`S&E^%+!c<75K-n4ECPULyXeNamj8rm1!VoaNjVg@dqvr)Y0SAPG1P}`@ zKoJQN0STOj@-7nt*ih#T;Uo|Taj{UJ0Ky;~^~V+o`1~=)g}MlE4kSQM9Q^b3C?sUg z`w-O%hXJTG{-wbFW>@5{UE_knR~#3MCU)(b#)qd_BL-AX=QpoyiRul`UA$*GaBh)y ze=n!I^5(PFGg-|7yf|}^E`72bs-di96_RWzeI)XrP5a;4be#2XSXh4^ricHP)mPEM z`eCGbEZ{oTj9YGSIyi8;OuzpJ`lNWm>3qLWuw`egEO*JhV}1n#He!RSCKn&_hU_dj z(Q7Rufm1mi#U5*an)6(07EuFD{o(Egb+N!4fd0p9%z=<{6V%?Y3U&ti#LxSUS(5shHy_?aIH`Aa<===$=x(m~e2dOL zwN$|~s@-5V1p>`@!* zm!9VR;f3;NcwFbKAgO&@$k8;}895(Cz-~QB2N7sTnJMvwn5_TUHSxT;d{PujPvR+D z^>d4Nu3UKRW_rl(!mD>bo^&6+MtP6lA32eOH%YhnN2P}6)#AHG8`+7EtzXJlvIb~X zt;znHtfxBz`42QBhlhC1*cihvstO%Y%&WN%8h;tdC98DY?zvXEF*ucp*4 z^waU#=szM4 H`+@%p)@true - sshfs_4every1.pfx + WinSSH4e1-public.snk @@ -475,6 +475,7 @@ + diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs index 6a6da17..c556ddf 100644 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ b/Renci.SshNet/Sftp/SftpSession.cs @@ -10,7 +10,8 @@ using Renci.SshNet.Sftp.Requests; using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("WinSshFS, PublicKey=0024000004800000940000000602000000240000525341310004000001000100bb1df492d3d63bb4aedb73672b0c0694ad7838ea17c6d49685ef1a03301ae6de5a4b4b1795844de0ddab49254d05bd533b5a02c24a580346274b204b8975536ffe36b4e7bb8c82e419264c335682ad44861e99eef16e95ee978742064c9335283ecb6e2fd325ea97942a51a3fc1a7d9948dd919b0d4ad25148d5490cc37f85b4")] + +[assembly: InternalsVisibleTo("WinSshFS, PublicKey=00240000048000009400000006020000002400005253413100040000010001005337866700b92e3a2a5d5be0292cdb0f2f6daa283526126b30169255b3c522f51593d15b5db31da4ddbe3e6ef5d9b80a05ddf4d1b1bca1c67ca62bf0b0c4d1b2ea3d4242027a2052b3c3cb17b98077a5c9f08143617ec3a1143c97c48bf27a378a9ec250220fb899f25c084599f477e36f699ec74aa452a3fd9e90007648a397")] namespace Renci.SshNet.Sftp { diff --git a/Renci.SshNet/WinSSH4e1-public.snk b/Renci.SshNet/WinSSH4e1-public.snk new file mode 100644 index 0000000000000000000000000000000000000000..d64facbaf6f8f1f9124741c0367df314668125a4 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096~H-={bxh^^?U0dKOEZYw+ZK^0WCK78f z7Lrx7#Uk|;lhIpUvmK<}zCLdC*|-V?-SpA1yrIT?rYrEU#L=?qJwiePdLUA>!^;=B zfOn1-4tXfdY3vNh`dPAu;xwL^~P?JIdU4St=Tj*(CA!$qnew}95%+cH!0c3Bg3`d*cC-w>a3fT0F1ahZx9KZ#eoLwGke21WKd4*D>I$(09HLhOz0@fP1KOnwFxl^Z)16f z?rVd*?%Y48*ZKWd?9+TH@dl^q8;3J=ZrYa{2ux$JDzS1+L5Mv7g7J|Wvtf+(98@1@ z?rG@%95`MPbizi^%dnm1&zu~RF%BG&G`n6O)h-NOD&dcb@w!fA zIAayDAeiTBmR2ucNg(^qk92(4@^j5Ggi| iY+5nt3VuPH#1G#Kje%Vk>Z4#+xAJ6rcY0XH3vUg9v>|{1 literal 0 HcmV?d00001 diff --git a/Renci.SshNet/sshfs_4every1.pfx b/Renci.SshNet/sshfs_4every1.pfx deleted file mode 100644 index 7e733984409fe3dc7a316d99e4b77ccb53671ebf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1764 zcmY*Yc{JPU8vYWIL}*eYmI{(yYpFI-OEA@1QekXeYz;!FNX@lX(@R6$khB%FgFzLs zbYf2_Lu0K}DIJE^!L*Jt4BBg2%e2)wE%#^6y)$Rdci#7X-sgGW?|grJ51EgggToZa zd}Iky$y7)ee%J#;!OHo_Yy=;99ijzfzCzpIB!y}OU!e-3#ZcTvD*taw1p|YZ^Wkk| zKKve82Z{MV4PA~!pvx^v=3&G)3^*J$hTy|*qLux)+U-``|3pOC#F`uzp5G3rEOVcu)lPvA-*L_ z{p7syn0=SK2QOZk$ZWEHWY;@eQHf}--!61tz*!v$aQmuj!&cwvUO_u0&VA*^+{Jf6 zTFEFKRU`|C$yK!(Bk7zrl|?rgt&aPZu!uq8?YM~Aba@zE2rH@=LPTkx=$86T>Tzxua6WJsq-6L_TapbR>NVuo^IbvQQ7~E;q%g> zQ4(cdN6UDiW>PWe+tf&U=HmVBHrpQbdiBkm7<|j{G6g|TFta_l&Wx{p>ef-_sL;wR z-aiEI4n}s`tXJy_RL%~CI2o`4Ig!SG+;*D9bO?Hb+V$EK7g}n@RnAV8UCxSLONfNSf}M+?v`~1+0+ehSE|~Jw^96gTnF~@`ok5=-^RSYL z7VW)G`S&E^%+!c<75K-n4ECPULyXeNamj8rm1!VoaNjVg@dqvr)Y0SAPG1P}`@ zKoJQN0STOj@-7nt*ih#T;Uo|Taj{UJ0Ky;~^~V+o`1~=)g}MlE4kSQM9Q^b3C?sUg z`w-O%hXJTG{-wbFW>@5{UE_knR~#3MCU)(b#)qd_BL-AX=QpoyiRul`UA$*GaBh)y ze=n!I^5(PFGg-|7yf|}^E`72bs-di96_RWzeI)XrP5a;4be#2XSXh4^ricHP)mPEM z`eCGbEZ{oTj9YGSIyi8;OuzpJ`lNWm>3qLWuw`egEO*JhV}1n#He!RSCKn&_hU_dj z(Q7Rufm1mi#U5*an)6(07EuFD{o(Egb+N!4fd0p9%z=<{6V%?Y3U&ti#LxSUS(5shHy_?aIH`Aa<===$=x(m~e2dOL zwN$|~s@-5V1p>`@!* zm!9VR;f3;NcwFbKAgO&@$k8;}895(Cz-~QB2N7sTnJMvwn5_TUHSxT;d{PujPvR+D z^>d4Nu3UKRW_rl(!mD>bo^&6+MtP6lA32eOH%YhnN2P}6)#AHG8`+7EtzXJlvIb~X zt;znHtfxBz`42QBhlhC1*cihvstO%Y%&WN%8h;tdC98DY?zvXEF*ucp*4 z^waU#=szM4 H`+@%p)@true - true + false app.manifest @@ -88,7 +88,7 @@ true - sshfs_4every1.pfx + WinSSH4e1-public.snk LocalIntranet @@ -158,6 +158,7 @@ True + diff --git a/Sshfs/Sshfs/VirtualDrive.cs b/Sshfs/Sshfs/VirtualDrive.cs index 6bb280e..93bf05e 100644 --- a/Sshfs/Sshfs/VirtualDrive.cs +++ b/Sshfs/Sshfs/VirtualDrive.cs @@ -237,19 +237,19 @@ public void Dispose() #region Implementation of ISerializable - public VirtualDrive(SerializationInfo info, - StreamingContext context) + public VirtualDrive(SerializationInfo info, + StreamingContext context) { - Letter = info.GetChar("letter"); - } - - - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("letter", Letter); - } - + Letter = info.GetChar("letter"); + } + + + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("letter", Letter); + } + #endregion } diff --git a/Sshfs/Sshfs/WinSSH4e1-public.snk b/Sshfs/Sshfs/WinSSH4e1-public.snk new file mode 100644 index 0000000000000000000000000000000000000000..d64facbaf6f8f1f9124741c0367df314668125a4 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096~H-={bxh^^?U0dKOEZYw+ZK^0WCK78f z7Lrx7#Uk|;lhIpUvmK<}zCLdC*|-V?-SpA1yrIT?rYrEU#L=?qJwiePdLUA>!^;=B zfOn1-4tXfdY3vNh`dPAu;xwL^~P?JIdU4St=Tj*(CA!$qnew}95%+cH!0c3Bg3`d*cC-w>a3fT0F1ahZx9KZ#eoLwGke21WKd4*D>I$(09HLhOz0@fP1KOnwFxl^Z)16f z?rVd*?%Y48*ZKWd?9+TH@dl^q8;3J=ZrYa{2ux$JDzS1+L5Mv7g7J|Wvtf+(98@1@ z?rG@%95`MPbizi^%dnm1&zu~RF%BG&G`n6O)h-NOD&dcb@w!fA zIAayDAeiTBmR2ucNg(^qk92(4@^j5Ggi| iY+5nt3VuPH#1G#Kje%Vk>Z4#+xAJ6rcY0XH3vUg9v>|{1 literal 0 HcmV?d00001 diff --git a/Sshfs/Sshfs/sshfs_4every1.pfx b/Sshfs/Sshfs/sshfs_4every1.pfx deleted file mode 100644 index 7e733984409fe3dc7a316d99e4b77ccb53671ebf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1764 zcmY*Yc{JPU8vYWIL}*eYmI{(yYpFI-OEA@1QekXeYz;!FNX@lX(@R6$khB%FgFzLs zbYf2_Lu0K}DIJE^!L*Jt4BBg2%e2)wE%#^6y)$Rdci#7X-sgGW?|grJ51EgggToZa zd}Iky$y7)ee%J#;!OHo_Yy=;99ijzfzCzpIB!y}OU!e-3#ZcTvD*taw1p|YZ^Wkk| zKKve82Z{MV4PA~!pvx^v=3&G)3^*J$hTy|*qLux)+U-``|3pOC#F`uzp5G3rEOVcu)lPvA-*L_ z{p7syn0=SK2QOZk$ZWEHWY;@eQHf}--!61tz*!v$aQmuj!&cwvUO_u0&VA*^+{Jf6 zTFEFKRU`|C$yK!(Bk7zrl|?rgt&aPZu!uq8?YM~Aba@zE2rH@=LPTkx=$86T>Tzxua6WJsq-6L_TapbR>NVuo^IbvQQ7~E;q%g> zQ4(cdN6UDiW>PWe+tf&U=HmVBHrpQbdiBkm7<|j{G6g|TFta_l&Wx{p>ef-_sL;wR z-aiEI4n}s`tXJy_RL%~CI2o`4Ig!SG+;*D9bO?Hb+V$EK7g}n@RnAV8UCxSLONfNSf}M+?v`~1+0+ehSE|~Jw^96gTnF~@`ok5=-^RSYL z7VW)G`S&E^%+!c<75K-n4ECPULyXeNamj8rm1!VoaNjVg@dqvr)Y0SAPG1P}`@ zKoJQN0STOj@-7nt*ih#T;Uo|Taj{UJ0Ky;~^~V+o`1~=)g}MlE4kSQM9Q^b3C?sUg z`w-O%hXJTG{-wbFW>@5{UE_knR~#3MCU)(b#)qd_BL-AX=QpoyiRul`UA$*GaBh)y ze=n!I^5(PFGg-|7yf|}^E`72bs-di96_RWzeI)XrP5a;4be#2XSXh4^ricHP)mPEM z`eCGbEZ{oTj9YGSIyi8;OuzpJ`lNWm>3qLWuw`egEO*JhV}1n#He!RSCKn&_hU_dj z(Q7Rufm1mi#U5*an)6(07EuFD{o(Egb+N!4fd0p9%z=<{6V%?Y3U&ti#LxSUS(5shHy_?aIH`Aa<===$=x(m~e2dOL zwN$|~s@-5V1p>`@!* zm!9VR;f3;NcwFbKAgO&@$k8;}895(Cz-~QB2N7sTnJMvwn5_TUHSxT;d{PujPvR+D z^>d4Nu3UKRW_rl(!mD>bo^&6+MtP6lA32eOH%YhnN2P}6)#AHG8`+7EtzXJlvIb~X zt;znHtfxBz`42QBhlhC1*cihvstO%Y%&WN%8h;tdC98DY?zvXEF*ucp*4 z^waU#=szM4 H`+@%p)@)+U-``|3pOC#F`uzp5G3rEOVcu)lPvA-*L_ z{p7syn0=SK2QOZk$ZWEHWY;@eQHf}--!61tz*!v$aQmuj!&cwvUO_u0&VA*^+{Jf6 zTFEFKRU`|C$yK!(Bk7zrl|?rgt&aPZu!uq8?YM~Aba@zE2rH@=LPTkx=$86T>Tzxua6WJsq-6L_TapbR>NVuo^IbvQQ7~E;q%g> zQ4(cdN6UDiW>PWe+tf&U=HmVBHrpQbdiBkm7<|j{G6g|TFta_l&Wx{p>ef-_sL;wR z-aiEI4n}s`tXJy_RL%~CI2o`4Ig!SG+;*D9bO?Hb+V$EK7g}n@RnAV8UCxSLONfNSf}M+?v`~1+0+ehSE|~Jw^96gTnF~@`ok5=-^RSYL z7VW)G`S&E^%+!c<75K-n4ECPULyXeNamj8rm1!VoaNjVg@dqvr)Y0SAPG1P}`@ zKoJQN0STOj@-7nt*ih#T;Uo|Taj{UJ0Ky;~^~V+o`1~=)g}MlE4kSQM9Q^b3C?sUg z`w-O%hXJTG{-wbFW>@5{UE_knR~#3MCU)(b#)qd_BL-AX=QpoyiRul`UA$*GaBh)y ze=n!I^5(PFGg-|7yf|}^E`72bs-di96_RWzeI)XrP5a;4be#2XSXh4^ricHP)mPEM z`eCGbEZ{oTj9YGSIyi8;OuzpJ`lNWm>3qLWuw`egEO*JhV}1n#He!RSCKn&_hU_dj z(Q7Rufm1mi#U5*an)6(07EuFD{o(Egb+N!4fd0p9%z=<{6V%?Y3U&ti#LxSUS(5shHy_?aIH`Aa<===$=x(m~e2dOL zwN$|~s@-5V1p>`@!* zm!9VR;f3;NcwFbKAgO&@$k8;}895(Cz-~QB2N7sTnJMvwn5_TUHSxT;d{PujPvR+D z^>d4Nu3UKRW_rl(!mD>bo^&6+MtP6lA32eOH%YhnN2P}6)#AHG8`+7EtzXJlvIb~X zt;znHtfxBz`42QBhlhC1*cihvstO%Y%&WN%8h;tdC98DY?zvXEF*ucp*4 z^waU#=szM4 H`+@%p)@ Date: Wed, 29 Jul 2015 22:31:46 +0300 Subject: [PATCH 057/134] cert issue --- DokanNet/DokanNet.csproj | 3 +- Renci.SshNet/Renci.SshNet.csproj | 1 - .../Sftp/SftpFileSystemInformation.cs | 136 ------------------ Sshfs/Sshfs/Properties/AssemblyInfo.cs | 68 ++++----- Sshfs/Sshfs/Sshfs.csproj | 13 -- 5 files changed, 35 insertions(+), 186 deletions(-) delete mode 100644 Renci.SshNet/Sftp/SftpFileSystemInformation.cs diff --git a/DokanNet/DokanNet.csproj b/DokanNet/DokanNet.csproj index ea7cc92..e9b8f5d 100644 --- a/DokanNet/DokanNet.csproj +++ b/DokanNet/DokanNet.csproj @@ -151,13 +151,12 @@ - - + - \ No newline at end of file diff --git a/DokanNet/DokanNet.sln b/DokanNet/DokanNet.sln deleted file mode 100644 index 4c512db..0000000 --- a/DokanNet/DokanNet.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DokanNet", "DokanNet.csproj", "{A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|x86.ActiveCfg = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|x86.Build.0 = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|Any CPU.ActiveCfg = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|Any CPU.Build.0 = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|x86.ActiveCfg = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/DokanNet/DokanOperationProxy.cs b/DokanNet/DokanOperationProxy.cs deleted file mode 100644 index 1844c21..0000000 --- a/DokanNet/DokanOperationProxy.cs +++ /dev/null @@ -1,872 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Security.AccessControl; -using System.Text; -using DokanNet.Native; -using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; - -namespace DokanNet -{ - internal sealed class DokanOperationProxy - { - - - #region Delegates - - public delegate int CleanupDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPStruct), In, Out] DokanFileInfo rawFileInfo); - - public delegate int CloseFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPStruct), In, Out] DokanFileInfo rawFileInfo); - - public delegate int CreateDirectoryDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int CreateFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, uint rawAccessMode, uint rawShare, - uint rawCreationDisposition, uint rawFlagsAndAttributes, - [MarshalAs(UnmanagedType.LPStruct), In, Out] DokanFileInfo dokanFileInfo); - - public delegate int DeleteDirectoryDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int DeleteFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int FindFilesDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, IntPtr rawFillFindData, // function pointer - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int FindFilesWithPatternDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPWStr)] string rawSearchPattern, - IntPtr rawFillFindData, // function pointer - [MarshalAs(UnmanagedType.LPStruct), In, Out] DokanFileInfo rawFileInfo); - - public delegate int FlushFileBuffersDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int GetDiskFreeSpaceDelegate(ref long rawFreeBytesAvailable, ref long rawTotalNumberOfBytes, - ref long rawTotalNumberOfFreeBytes, - [MarshalAs(UnmanagedType.LPStruct), In] DokanFileInfo rawFileInfo); - - public delegate int GetFileInformationDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string fileName, ref BY_HANDLE_FILE_INFORMATION handleFileInfo, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo fileInfo); - - public delegate int GetFileSecurityDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName,[In] ref SECURITY_INFORMATION rawRequestedInformation, - IntPtr rawSecurityDescriptor, uint rawSecurityDescriptorLength, - ref uint rawSecurityDescriptorLengthNeeded, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int GetVolumeInformationDelegate( - [MarshalAs(UnmanagedType.LPWStr)] StringBuilder rawVolumeNameBuffer, uint rawVolumeNameSize, - ref uint rawVolumeSerialNumber, - ref uint rawMaximumComponentLength, ref FileSystemFeatures rawFileSystemFlags, - [MarshalAs(UnmanagedType.LPWStr)] StringBuilder rawFileSystemNameBuffer, - uint rawFileSystemNameSize, [MarshalAs(UnmanagedType.LPStruct), In] DokanFileInfo rawFileInfo); - - public delegate int LockFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, long rawByteOffset, long rawLength, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int MoveFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPWStr)] string rawNewFileName, - [MarshalAs(UnmanagedType.Bool)] bool rawReplaceIfExisting, - [MarshalAs(UnmanagedType.LPStruct), In, Out] DokanFileInfo rawFileInfo); - - public delegate int OpenDirectoryDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string fileName, - [MarshalAs(UnmanagedType.LPStruct), In, Out] DokanFileInfo fileInfo); - - public delegate int ReadFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2), Out] byte[] rawBuffer, uint rawBufferLength, - ref int rawReadLength, long rawOffset, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int SetAllocationSizeDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, long rawLength, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int SetEndOfFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, long rawByteOffset, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int SetFileAttributesDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, uint rawAttributes, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int SetFileSecurityDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName,[In] ref SECURITY_INFORMATION rawSecurityInformation, - IntPtr rawSecurityDescriptor, uint rawSecurityDescriptorLength, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int SetFileTimeDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - ref FILETIME rawCreationTime, - ref FILETIME rawLastAccessTime, - ref FILETIME rawLastWriteTime, [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int UnlockFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, long rawByteOffset, long rawLength, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - public delegate int UnmountDelegate([MarshalAs(UnmanagedType.LPStruct), In] DokanFileInfo rawFileInfo); - - public delegate int WriteFileDelegate( - [MarshalAs(UnmanagedType.LPWStr)] string rawFileName, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] rawBuffer, uint rawNumberOfBytesToWrite, - ref int rawNumberOfBytesWritten, long rawOffset, - [MarshalAs(UnmanagedType.LPStruct), In/*, Out*/] DokanFileInfo rawFileInfo); - - #endregion - private const int ERROR_FILE_NOT_FOUND = -2; - private const int ERROR_INVALID_FUNCTION = -1; - private const int ERROR_SUCCESS = 0; - private const int ERROR_INSUFFICIENT_BUFFER = -122; - private const int ERROR_INVALID_HANDLE = -6; - - - private readonly IDokanOperations _operations; - - private readonly uint _serialNumber; - - - [Conditional("DEBUG")] - private void Log(string format, params object[] arg) - { - //Console.WriteLine(format, arg); - Debug.Write(DateTime.Now.ToLongTimeString() + " DOKAN proxy: "); - Debug.WriteLine(format, arg); - } - - - public DokanOperationProxy(IDokanOperations operations) - { - _operations = operations; - _serialNumber = (uint) _operations.GetHashCode(); - - } - - public int CreateFileProxy(string rawFileName, uint rawAccessMode, - uint rawShare, uint rawCreationDisposition, uint rawFlagsAndAttributes, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.CreateFile(rawFileName, (FileAccess) rawAccessMode, (FileShare) rawShare, - (FileMode) rawCreationDisposition, - (FileOptions) (rawFlagsAndAttributes & 0xffffc000), //& 0xffffc000 - (FileAttributes) (rawFlagsAndAttributes & 0x3fff), rawFileInfo); - //& 0x3ffflower 14 bits i think are file atributes and rest are file options WRITE_TROUGH etc. - } - catch - { -#if DEBUGDOKAN - Log("CreateFileProxy: {0}\n",rawFileName); - //throw; -#endif - - return ERROR_FILE_NOT_FOUND; - } - } - - //// - - public int OpenDirectoryProxy(string rawFileName, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.OpenDirectory(rawFileName, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("OpenDirectoryProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int CreateDirectoryProxy(string rawFileName, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.CreateDirectory(rawFileName, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("CreateDirectoryProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int CleanupProxy(string rawFileName, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.Cleanup(rawFileName, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("CleanupProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int CloseFileProxy(string rawFileName, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.CloseFile(rawFileName, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("CloseFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - - public int ReadFileProxy(string rawFileName, byte[] rawBuffer, - uint rawBufferLength, ref int rawReadLength, long rawOffset, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.ReadFile(rawFileName, rawBuffer, out rawReadLength, rawOffset, - rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("ReadFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int WriteFileProxy(string rawFileName, byte[] rawBuffer, - uint rawNumberOfBytesToWrite, ref int rawNumberOfBytesWritten, long rawOffset, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.WriteFile(rawFileName, rawBuffer, - out rawNumberOfBytesWritten, rawOffset, - rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("WriteFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int FlushFileBuffersProxy(string rawFileName, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.FlushFileBuffers(rawFileName, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("FlushFileBuffersProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int GetFileInformationProxy(string rawFileName, - ref BY_HANDLE_FILE_INFORMATION rawHandleFileInformation, - DokanFileInfo rawFileInfo) - { - FileInformation fi ; - try - { - int ret = (int) _operations.GetFileInformation(rawFileName, out fi, rawFileInfo); - - if (ret == ERROR_SUCCESS) - { - Debug.Assert(fi.FileName!=null); - rawHandleFileInformation.dwFileAttributes = (uint) fi.Attributes /* + FILE_ATTRIBUTE_VIRTUAL*/; - - long ctime = fi.CreationTime.ToFileTime(); - long atime = fi.LastAccessTime.ToFileTime(); - long mtime = fi.LastWriteTime.ToFileTime(); - rawHandleFileInformation.ftCreationTime.dwHighDateTime = (int) (ctime >> 32); - rawHandleFileInformation.ftCreationTime.dwLowDateTime = - (int) (ctime & 0xffffffff); - - rawHandleFileInformation.ftLastAccessTime.dwHighDateTime = - (int) (atime >> 32); - rawHandleFileInformation.ftLastAccessTime.dwLowDateTime = - (int) (atime & 0xffffffff); - - rawHandleFileInformation.ftLastWriteTime.dwHighDateTime = - (int) (mtime >> 32); - rawHandleFileInformation.ftLastWriteTime.dwLowDateTime = - (int) (mtime & 0xffffffff); - - rawHandleFileInformation.dwVolumeSerialNumber = _serialNumber; - - rawHandleFileInformation.nFileSizeLow = (uint) (fi.Length & 0xffffffff); - rawHandleFileInformation.nFileSizeHigh = (uint) (fi.Length >> 32); - rawHandleFileInformation.dwNumberOfLinks = 1; - rawHandleFileInformation.nFileIndexHigh = 0; - rawHandleFileInformation.nFileIndexLow = (uint) fi.FileName.GetHashCode(); - } - - return ret; - } - catch - { -#if DEBUGDOKAN - Log("GetFileInformationProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - - - public int FindFilesProxy(string rawFileName, IntPtr rawFillFindData, - // function pointer - DokanFileInfo rawFileInfo) - { - try - { - IList files ; - - - int ret = (int) _operations.FindFiles(rawFileName, out files, rawFileInfo); - - - Debug.Assert(files!=null); - if (ret == ERROR_SUCCESS&&files.Count!=0) - { - var fill = - (FILL_FIND_DATA)Marshal.GetDelegateForFunctionPointer(rawFillFindData, typeof(FILL_FIND_DATA)); - // Used a single entry call to speed up the "enumeration" of the list - for (int index = 0; index < files.Count; index++) - - { - Addto(fill, rawFileInfo, files[index]); - } - } - return ret; - } - catch - { -#if DEBUGDOKAN - Log("FindFilesProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_HANDLE; - } - } - - public int FindFilesWithPatternProxy(string rawFileName, string searchPattern, IntPtr rawFillFindData, - // function pointer - DokanFileInfo rawFileInfo) - { - try - { - IList files; - - - int ret = (int)_operations.FindFilesWithPattern(rawFileName, searchPattern, out files, rawFileInfo); - - - Debug.Assert(files != null); - if (ret == ERROR_SUCCESS && files.Count != 0) - { - var fill = - (FILL_FIND_DATA)Marshal.GetDelegateForFunctionPointer(rawFillFindData, typeof(FILL_FIND_DATA)); - // Used a single entry call to speed up the "enumeration" of the list - for (int index = 0; index < files.Count; index++) - { - Addto(fill, rawFileInfo, files[index]); - } - } - return ret; - } - catch - { -#if DEBUGDOKAN - Log("FindFilesProxyWithPattern: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_HANDLE; - } - - } - - - private static void Addto(FILL_FIND_DATA fill, DokanFileInfo rawFileInfo, FileInformation fi) - { - Debug.Assert(!String.IsNullOrEmpty(fi.FileName)); - long ctime = fi.CreationTime.ToFileTime(); - long atime = fi.LastAccessTime.ToFileTime(); - long mtime = fi.LastWriteTime.ToFileTime(); - var data = new WIN32_FIND_DATA - { - dwFileAttributes = fi.Attributes, - ftCreationTime = - { - dwHighDateTime = (int) (ctime >> 32), - dwLowDateTime = (int) (ctime & 0xffffffff) - }, - ftLastAccessTime = - { - dwHighDateTime = (int) (atime >> 32), - dwLowDateTime = (int) (atime & 0xffffffff) - }, - ftLastWriteTime = - { - dwHighDateTime = (int) (mtime >> 32), - dwLowDateTime = (int) (mtime & 0xffffffff) - }, - nFileSizeLow = (uint) (fi.Length & 0xffffffff), - nFileSizeHigh = (uint) (fi.Length >> 32), - cFileName = fi.FileName - }; - //ZeroMemory(&data, sizeof(WIN32_FIND_DATAW)); - - fill(ref data, rawFileInfo); - } - - //// - - public int SetEndOfFileProxy(string rawFileName, long rawByteOffset, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.SetEndOfFile(rawFileName, rawByteOffset, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("SetEndOfFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - - public int SetAllocationSizeProxy(string rawFileName, long rawLength, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.SetAllocationSize(rawFileName, rawLength, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("SetAllocationSizeProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - - //// - - public int SetFileAttributesProxy(string rawFileName, uint rawAttributes, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.SetFileAttributes(rawFileName, (FileAttributes) rawAttributes, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("SetFileAttributesProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int SetFileTimeProxy(string rawFileName, - ref FILETIME rawCreationTime, - ref FILETIME rawLastAccessTime, - ref FILETIME rawLastWriteTime, - DokanFileInfo rawFileInfo) - { - - var ctime = ( rawCreationTime.dwLowDateTime != 0 || rawCreationTime.dwHighDateTime != 0) && (rawCreationTime.dwLowDateTime != -1 || rawCreationTime.dwHighDateTime != -1) - ? DateTime.FromFileTime(((long) rawCreationTime.dwHighDateTime << 32) | - (uint) rawCreationTime.dwLowDateTime) - : (DateTime?) null; - var atime =(rawLastAccessTime.dwLowDateTime != 0 || rawLastAccessTime.dwHighDateTime != 0) && (rawLastAccessTime.dwLowDateTime != -1 || rawLastAccessTime.dwHighDateTime != -1) - ? DateTime.FromFileTime(((long) rawLastAccessTime.dwHighDateTime << 32) | - (uint) rawLastAccessTime.dwLowDateTime) - : (DateTime?) null; - var mtime = (rawLastWriteTime.dwLowDateTime != 0 || rawLastWriteTime.dwHighDateTime != 0) && (rawLastWriteTime.dwLowDateTime != -1 || rawLastWriteTime.dwHighDateTime != -1) - ? DateTime.FromFileTime(((long) rawLastWriteTime.dwHighDateTime << 32) | - (uint) rawLastWriteTime.dwLowDateTime) - : (DateTime?) null; - - - try - { - return (int) _operations.SetFileTime(rawFileName, ctime, atime, - mtime, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("SetFileTimeProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int DeleteFileProxy(string rawFileName, DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.DeleteFile(rawFileName, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("DeleteFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int DeleteDirectoryProxy(string rawFileName, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.DeleteDirectory(rawFileName, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("DeleteDirectoryProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int MoveFileProxy(string rawFileName, - string rawNewFileName, bool rawReplaceIfExisting, - DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.MoveFile(rawFileName, rawNewFileName, rawReplaceIfExisting, - rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("MoveFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int LockFileProxy(string rawFileName, long rawByteOffset, - long rawLength, DokanFileInfo rawFileInfo) - { - try - { - return - (int) _operations.LockFile(rawFileName, rawByteOffset, rawLength, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("LockFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int UnlockFileProxy(string rawFileName, long rawByteOffset, - long rawLength, DokanFileInfo rawFileInfo) - { - try - { - return - (int) - _operations.UnlockFile(rawFileName, rawByteOffset, rawLength, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("UnlockFileProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - //// - - public int GetDiskFreeSpaceProxy(ref long rawFreeBytesAvailable, ref long rawTotalNumberOfBytes, - ref long rawTotalNumberOfFreeBytes, DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.GetDiskFreeSpace(out rawFreeBytesAvailable, out rawTotalNumberOfBytes, - out rawTotalNumberOfFreeBytes, - rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("GetDiskFreeSpaceProxy: {0}\n", rawFileInfo); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - public int GetVolumeInformationProxy(StringBuilder rawVolumeNameBuffer, - uint rawVolumeNameSize, - ref uint rawVolumeSerialNumber, - ref uint rawMaximumComponentLength, ref FileSystemFeatures rawFileSystemFlags, - StringBuilder rawFileSystemNameBuffer, - uint rawFileSystemNameSize, - DokanFileInfo fileInfo) - { - rawMaximumComponentLength = 256; - rawVolumeSerialNumber = _serialNumber; - string label; - string name; - try - { - - int ret = (int) _operations.GetVolumeInformation(out label, - out rawFileSystemFlags, out name, - fileInfo); - - if (ret == ERROR_SUCCESS) - { - Debug.Assert(!String.IsNullOrEmpty(name)); - Debug.Assert(!String.IsNullOrEmpty(label)); - rawVolumeNameBuffer.Append(label); - rawFileSystemNameBuffer.Append(name); - - } - return ret; - } - catch - { -#if DEBUGDOKAN - Log("GetVolumeInformationProxy: {0}\n", rawVolumeNameBuffer); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - - public int UnmountProxy(DokanFileInfo rawFileInfo) - { - try - { - return (int) _operations.Unmount(rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("UnmountProxy: {0}\n", rawFileInfo); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - public int GetFileSecurityProxy(string rawFileName, ref SECURITY_INFORMATION rawRequestedInformation, - IntPtr rawSecurityDescriptor, uint rawSecurityDescriptorLength, - ref uint rawSecurityDescriptorLengthNeeded, - DokanFileInfo rawFileInfo) - { - FileSystemSecurity sec; - - var sect = AccessControlSections.None; - if (rawRequestedInformation.HasFlag(SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Owner; - } - if (rawRequestedInformation.HasFlag(SECURITY_INFORMATION.GROUP_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Group; - } - if (rawRequestedInformation.HasFlag(SECURITY_INFORMATION.DACL_SECURITY_INFORMATION) || - rawRequestedInformation.HasFlag(SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION) || - rawRequestedInformation.HasFlag(SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Access; - } - if (rawRequestedInformation.HasFlag(SECURITY_INFORMATION.SACL_SECURITY_INFORMATION) || - rawRequestedInformation.HasFlag(SECURITY_INFORMATION.PROTECTED_SACL_SECURITY_INFORMATION) || - rawRequestedInformation.HasFlag(SECURITY_INFORMATION.UNPROTECTED_SACL_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Audit; - } - try - { - - int ret = (int) _operations.GetFileSecurity(rawFileName, out sec, sect, rawFileInfo); - if (ret == ERROR_SUCCESS /*&& sec != null*/) - { - Debug.Assert(sec!=null); - var buffer = sec.GetSecurityDescriptorBinaryForm(); - rawSecurityDescriptorLengthNeeded = (uint)buffer.Length; - if (buffer.Length > rawSecurityDescriptorLength) - { - - return ERROR_INSUFFICIENT_BUFFER; - } - - Marshal.Copy(buffer, 0, rawSecurityDescriptor, buffer.Length); - - - } - return ret; - } - catch - { -#if DEBUGDOKAN - Log("UnmountProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - public int SetFileSecurityProxy( - string rawFileName, ref SECURITY_INFORMATION rawSecurityInformation, - IntPtr rawSecurityDescriptor, uint rawSecurityDescriptorLength, - DokanFileInfo rawFileInfo) - - { - var sect = AccessControlSections.None; - if (rawSecurityInformation.HasFlag(SECURITY_INFORMATION.OWNER_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Owner; - } - if (rawSecurityInformation.HasFlag(SECURITY_INFORMATION.GROUP_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Group; - } - if (rawSecurityInformation.HasFlag(SECURITY_INFORMATION.DACL_SECURITY_INFORMATION) || - rawSecurityInformation.HasFlag(SECURITY_INFORMATION.PROTECTED_DACL_SECURITY_INFORMATION) || - rawSecurityInformation.HasFlag(SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Access; - } - if (rawSecurityInformation.HasFlag(SECURITY_INFORMATION.SACL_SECURITY_INFORMATION) || - rawSecurityInformation.HasFlag(SECURITY_INFORMATION.PROTECTED_SACL_SECURITY_INFORMATION) || - rawSecurityInformation.HasFlag(SECURITY_INFORMATION.UNPROTECTED_SACL_SECURITY_INFORMATION)) - { - sect |= AccessControlSections.Audit; - } - var buffer = new byte[rawSecurityDescriptorLength]; - try - { - Marshal.Copy(rawSecurityDescriptor, buffer, 0, (int) rawSecurityDescriptorLength); - var sec = rawFileInfo.IsDirectory ? (FileSystemSecurity) new DirectorySecurity() : new FileSecurity(); - sec.SetSecurityDescriptorBinaryForm(buffer); - - return (int) _operations.SetFileSecurity(rawFileName, sec, sect, rawFileInfo); - } - catch - { -#if DEBUGDOKAN - Log("SetFileSecurityProxy: {0}\n", rawFileName); - //throw; -#endif - return ERROR_INVALID_FUNCTION; - } - } - - #region Nested type: FILL_FIND_DATA - - private delegate int FILL_FIND_DATA( - ref WIN32_FIND_DATA rawFindData, [MarshalAs(UnmanagedType.LPStruct), In] DokanFileInfo rawFileInfo); - - #endregion - } -} \ No newline at end of file diff --git a/DokanNet/DokanOptions.cs b/DokanNet/DokanOptions.cs deleted file mode 100644 index 901c64d..0000000 --- a/DokanNet/DokanOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace DokanNet -{ - [Flags] - public enum DokanOptions : ulong - { - DebugMode = 1, // ouput debug message - StderrOutput = 2, // ouput debug message to stderr - AltStream = 4, // use alternate stream - KeepAlive = 8, // use auto unmount - NetworkDrive = 16, // use network drive, you need to install Dokan network provider. - RemovableDrive = 32, // use removable drive - FixedDrive=0, - } -} \ No newline at end of file diff --git a/DokanNet/FileAccess.cs b/DokanNet/FileAccess.cs deleted file mode 100644 index 0df2bfb..0000000 --- a/DokanNet/FileAccess.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace DokanNet -{ - [Flags] - public enum FileAccess : uint - { - GenericRead = 2147483648, //0x80000000 - GenericWrite = 1073741824, //0x40000000 - GenericExecute = 536870912, //0x20000000 - ReadData = 1, //0x00000001 - WriteData = 2, //0x00000002 - AppendData = 4, //0x00000004 - ReadExtendedAttributes = 8, //0x00000008 - WriteExtendedAttributes = 16, //0x00000010 - Execute = 32, //0x00000020 - ReadAttributes = 128, //0x00000080 - WriteAttributes = 256, //0x00000100 - Delete = 65536, //0x00010000 - ReadPermissions = 131072, //0x00020000 - ChangePermissions = 262144, //0x00040000 - SetOwnership = 524288, //0x00080000 - Synchronize = 1048576, //0x00100000 - } -} \ No newline at end of file diff --git a/DokanNet/FileInformation.cs b/DokanNet/FileInformation.cs deleted file mode 100644 index a090051..0000000 --- a/DokanNet/FileInformation.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace DokanNet -{ - [StructLayout(LayoutKind.Auto)] - public struct FileInformation - { - public string FileName { get; set; } - public FileAttributes Attributes{ get; set; } - public DateTime CreationTime { get; set; } - public DateTime LastAccessTime { get; set; } - public DateTime LastWriteTime { get; set; } - public long Length { get; set; } - } -} \ No newline at end of file diff --git a/DokanNet/FileSystemFeatures.cs b/DokanNet/FileSystemFeatures.cs deleted file mode 100644 index 940f1b9..0000000 --- a/DokanNet/FileSystemFeatures.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace DokanNet -{ - [Flags] - public enum FileSystemFeatures : uint - { - CaseSensitiveSearch = 1, - CasePreservedNames = 2, - UnicodeOnDisk = 4, - PersistentAcls = 8, - SupportsRemoteStorage = 256, - VolumeQuotas = 32, - SupportsSparseFiles = 64, - SupportsReparsePoints = 128, - VolumeIsCompressed = 32768, - SupportsObjectIDs = 65536, - SupportsEncryption = 131072, - NamedStreams = 262144, - ReadOnlyVolume = 524288, - SequentialWriteOnce = 1048576, - SupportsTransactions = 2097152, - } -} \ No newline at end of file diff --git a/DokanNet/IDokanOperations.cs b/DokanNet/IDokanOperations.cs deleted file mode 100644 index 9a6d2d1..0000000 --- a/DokanNet/IDokanOperations.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.AccessControl; - -namespace DokanNet -{ - public interface IDokanOperations - { - DokanError CreateFile(string fileName, FileAccess access, FileShare share, FileMode mode, - FileOptions options, FileAttributes attributes, DokanFileInfo info); - - DokanError OpenDirectory(string fileName, DokanFileInfo info); - - DokanError CreateDirectory(string fileName, DokanFileInfo info); - - DokanError Cleanup(string fileName, DokanFileInfo info); - - DokanError CloseFile(string fileName, DokanFileInfo info); - - DokanError ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, - DokanFileInfo info); - - DokanError WriteFile(string fileName, byte[] buffer, out int bytesWritten, - long offset, DokanFileInfo info); - - DokanError FlushFileBuffers(string fileName, DokanFileInfo info); - - DokanError GetFileInformation(string fileName, out FileInformation fileInfo, DokanFileInfo info); - - DokanError FindFiles(string fileName, out IList files, DokanFileInfo info); - - DokanError FindFilesWithPattern(string fileName, string searchPattern, out IList files, DokanFileInfo info); - - DokanError SetFileAttributes(string fileName, FileAttributes attributes, DokanFileInfo info); - - DokanError SetFileTime(string fileName, DateTime? creationTime, DateTime? lastAccessTime, - DateTime? lastWriteTime, DokanFileInfo info); - - DokanError DeleteFile(string fileName, DokanFileInfo info); - - DokanError DeleteDirectory(string fileName, DokanFileInfo info); - - DokanError MoveFile(string oldName, string newName, bool replace, DokanFileInfo info); - - DokanError SetEndOfFile(string fileName, long length, DokanFileInfo info); - - DokanError SetAllocationSize(string fileName, long length, DokanFileInfo info); - - DokanError LockFile(string fileName, long offset, long length, DokanFileInfo info); - - DokanError UnlockFile(string fileName, long offset, long length, DokanFileInfo info); - - DokanError GetDiskFreeSpace(out long free, out long total, out long used, - DokanFileInfo info); - - DokanError GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features, - out string fileSystemName, DokanFileInfo info); - - DokanError GetFileSecurity(string fileName, out FileSystemSecurity security, AccessControlSections sections, - DokanFileInfo info); - - DokanError SetFileSecurity(string fileName, FileSystemSecurity security, AccessControlSections sections, - DokanFileInfo info); - - DokanError Unmount(DokanFileInfo info); - } -} \ No newline at end of file diff --git a/DokanNet/Native/BY_HANDLE_FILE_INFORMATION.cs b/DokanNet/Native/BY_HANDLE_FILE_INFORMATION.cs deleted file mode 100644 index cbe21f0..0000000 --- a/DokanNet/Native/BY_HANDLE_FILE_INFORMATION.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Runtime.InteropServices; -using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; - -namespace DokanNet.Native -{ - [StructLayout(LayoutKind.Sequential, Pack = 4)] - internal struct BY_HANDLE_FILE_INFORMATION - { - public uint dwFileAttributes; - public FILETIME ftCreationTime; - public FILETIME ftLastAccessTime; - public FILETIME ftLastWriteTime; - internal uint dwVolumeSerialNumber; - public uint nFileSizeHigh; - public uint nFileSizeLow; - internal uint dwNumberOfLinks; - internal uint nFileIndexHigh; - internal uint nFileIndexLow; - } -} \ No newline at end of file diff --git a/DokanNet/Native/DOKAN_OPERATIONS.cs b/DokanNet/Native/DOKAN_OPERATIONS.cs deleted file mode 100644 index b9808a4..0000000 --- a/DokanNet/Native/DOKAN_OPERATIONS.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace DokanNet.Native -{ - [StructLayout(LayoutKind.Sequential, Pack = 4)] - internal struct DOKAN_OPERATIONS - { - public DokanOperationProxy.CreateFileDelegate CreateFile; - public DokanOperationProxy.OpenDirectoryDelegate OpenDirectory; - public DokanOperationProxy.CreateDirectoryDelegate CreateDirectory; - public DokanOperationProxy.CleanupDelegate Cleanup; - public DokanOperationProxy.CloseFileDelegate CloseFile; - public DokanOperationProxy.ReadFileDelegate ReadFile; - public DokanOperationProxy.WriteFileDelegate WriteFile; - public DokanOperationProxy.FlushFileBuffersDelegate FlushFileBuffers; - public DokanOperationProxy.GetFileInformationDelegate GetFileInformation; - public DokanOperationProxy.FindFilesDelegate FindFiles; - - public DokanOperationProxy.FindFilesWithPatternDelegate FindFilesWithPattern; - - public DokanOperationProxy.SetFileAttributesDelegate SetFileAttributes; - public DokanOperationProxy.SetFileTimeDelegate SetFileTime; - public DokanOperationProxy.DeleteFileDelegate DeleteFile; - public DokanOperationProxy.DeleteDirectoryDelegate DeleteDirectory; - public DokanOperationProxy.MoveFileDelegate MoveFile; - public DokanOperationProxy.SetEndOfFileDelegate SetEndOfFile; - public DokanOperationProxy.SetAllocationSizeDelegate SetAllocationSize; - public DokanOperationProxy.LockFileDelegate LockFile; - public DokanOperationProxy.UnlockFileDelegate UnlockFile; - public DokanOperationProxy.GetDiskFreeSpaceDelegate GetDiskFreeSpace; - public DokanOperationProxy.GetVolumeInformationDelegate GetVolumeInformation; - public DokanOperationProxy.UnmountDelegate Unmount; - - public DokanOperationProxy.GetFileSecurityDelegate GetFileSecurity; - public DokanOperationProxy.SetFileSecurityDelegate SetFileSecurity; - } -} \ No newline at end of file diff --git a/DokanNet/Native/DOKAN_OPTIONS.cs b/DokanNet/Native/DOKAN_OPTIONS.cs deleted file mode 100644 index b0763bc..0000000 --- a/DokanNet/Native/DOKAN_OPTIONS.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DokanNet.Native -{ - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] - internal struct DOKAN_OPTIONS - { - public ushort Version; - public ushort ThreadCount; // number of threads to be used - public uint Options; - public ulong GlobalContext; - [MarshalAs(UnmanagedType.LPWStr)] public string MountPoint; - } -} \ No newline at end of file diff --git a/DokanNet/Native/NativeMethods.cs b/DokanNet/Native/NativeMethods.cs deleted file mode 100644 index c31f310..0000000 --- a/DokanNet/Native/NativeMethods.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace DokanNet.Native -{ - internal static class NativeMethods - { - [DllImport("dokan.dll",ExactSpelling = true)] - public static extern int DokanMain(ref DOKAN_OPTIONS options, ref DOKAN_OPERATIONS operations); - - [DllImport("dokan.dll",ExactSpelling = true,CharSet = CharSet.Auto)] - public static extern int DokanUnmount(char driveLetter); - - [DllImport("dokan.dll",ExactSpelling = true)] - public static extern uint DokanVersion(); - - [DllImport("dokan.dll",ExactSpelling = true)] - public static extern uint DokanDriverVersion(); - - [DllImport("dokan.dll",ExactSpelling = true,CharSet = CharSet.Auto)] - public static extern int DokanRemoveMountPoint([MarshalAs(UnmanagedType.LPWStr)] string mountPoint); - - [DllImport("dokan.dll",ExactSpelling = true) ][return :MarshalAs(UnmanagedType.Bool)] - public static extern bool DokanResetTimeout(uint timeout, DokanFileInfo rawFileInfo); - - [DllImport("dokan.dll",ExactSpelling = true)] - public static extern IntPtr DokanOpenRequestorToken(DokanFileInfo rawFileInfo); - - - - [DllImport("dokan.dll", CharSet = CharSet.Unicode)] - public static extern bool DokanIsNameInExpression([MarshalAs(UnmanagedType.LPWStr)] string expression, - // matching pattern - [MarshalAs(UnmanagedType.LPWStr)] string name, // file name - [MarshalAs(UnmanagedType.Bool)] bool ignoreCase); - } -} \ No newline at end of file diff --git a/DokanNet/Native/SECURITY_INFORMATION.cs b/DokanNet/Native/SECURITY_INFORMATION.cs deleted file mode 100644 index dac871b..0000000 --- a/DokanNet/Native/SECURITY_INFORMATION.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace DokanNet.Native -{ - [Flags] - internal enum SECURITY_INFORMATION : uint - { - /// - /// Structure taken from http://www.pinvoke.net/default.aspx/Enums/SECURITY_INFORMATION.html - /// - OWNER_SECURITY_INFORMATION = 0x00000001, - GROUP_SECURITY_INFORMATION = 0x00000002, - DACL_SECURITY_INFORMATION = 0x00000004, - SACL_SECURITY_INFORMATION = 0x00000008, - // Dokan may not be passing Label ?? 0x00000010 - UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000, - UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000, - PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000, - PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000 - } -} \ No newline at end of file diff --git a/DokanNet/Native/WIN32_FIND_DATA.cs b/DokanNet/Native/WIN32_FIND_DATA.cs deleted file mode 100644 index 6261b91..0000000 --- a/DokanNet/Native/WIN32_FIND_DATA.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.IO; -using System.Runtime.InteropServices; -using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; - -namespace DokanNet.Native -{ - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] - internal struct WIN32_FIND_DATA - { - public FileAttributes dwFileAttributes; - public FILETIME ftCreationTime; - public FILETIME ftLastAccessTime; - public FILETIME ftLastWriteTime; - public uint nFileSizeHigh; - public uint nFileSizeLow; - private readonly uint dwReserved0; - private readonly uint dwReserved1; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] private readonly string cAlternateFileName; - } -} \ No newline at end of file diff --git a/DokanNet/Properties/AssemblyInfo.cs b/DokanNet/Properties/AssemblyInfo.cs deleted file mode 100644 index 2578f75..0000000 --- a/DokanNet/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// - -[assembly: AssemblyTitle("DokanNet")] -[assembly: AssemblyDescription(".NET wraper for dokan - user mode filesystem")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("DokanNet")] -[assembly: AssemblyCopyright("Copyright (C) 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(true)] -[assembly: CLSCompliant(true)] - - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("faf94eee-9bae-4ada-8f96-614ca17f7854")] - - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly: AssemblyVersion("0.6.0.0")] -[assembly: AssemblyFileVersion("0.6.0.0")] \ No newline at end of file diff --git a/DokanNet/StyleCop.Cache b/DokanNet/StyleCop.Cache deleted file mode 100644 index c0c00f3..0000000 --- a/DokanNet/StyleCop.Cache +++ /dev/null @@ -1,4141 +0,0 @@ - - 10 - - DEBUG;TRACE - - - - 06/30/2010 12:43:56 - 0 - - 08/17/2011 15:24:13 - 671 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - The enum must have a documentation header. - 3 - False - - - A single-line comment must be preceded by a blank line or another single-line comment, or must be the first item in its scope. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 16 - False - - - A single-line comment must not be followed by a blank line. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 16 - False - - - A single-line comment must be preceded by a blank line or another single-line comment, or must be the first item in its scope. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 19 - False - - - The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 19 - False - - - The enumeration sub-item must have a documentation header. - 6 - False - - - The enumeration sub-item must have a documentation header. - 7 - False - - - The enumeration sub-item must have a documentation header. - 8 - False - - - The enumeration sub-item must have a documentation header. - 9 - False - - - The enumeration sub-item must have a documentation header. - 10 - False - - - The enumeration sub-item must have a documentation header. - 11 - False - - - The enumeration sub-item must have a documentation header. - 12 - False - - - The enumeration sub-item must have a documentation header. - 13 - False - - - The enumeration sub-item must have a documentation header. - 15 - False - - - The enumeration sub-item must have a documentation header. - 18 - False - - - The enumeration sub-item must have a documentation header. - 20 - False - - - The enumeration sub-item must have a documentation header. - 21 - False - - - The enumeration sub-item must have a documentation header. - 22 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 14:03:12 - 500 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - The class must have a documentation header. - 6 - False - - - An opening curly bracket must not be followed by a blank line. - 7 - False - - - The constructor must have a documentation header. - 9 - False - - - The call to HResult should only use the 'base.' prefix if the item is declared virtual in the base class and an override is defined in the local class. Otherwise, prefix the call with this rather than base. - 11 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 14:15:45 - 328 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - All using directives must be placed inside of the namespace. - 3 - False - - - All using directives must be placed inside of the namespace. - 4 - False - - - The class must have a documentation header. - 10 - False - - - An opening curly bracket must not be followed by a blank line. - 11 - False - - - The code must not contain multiple blank lines in a row. - 24 - False - - - The code must not contain multiple blank lines in a row. - 43 - False - - - The field must have a documentation header. - 13 - False - - - Field names must not start with an underscore. - 13 - False - - - All constant and readonly private fields must be placed before all non-constant, non-readonly private fields. - 13 - False - - - The field must have a documentation header. - 14 - False - - - Field names must not start with an underscore. - 14 - False - - - The field must have a documentation header. - 15 - False - - - The field must have a documentation header. - 16 - False - - - Field names must not start with an underscore. - 16 - False - - - The field must have a documentation header. - 17 - False - - - Field names must not start with an underscore. - 17 - False - - - The field must have a documentation header. - 18 - False - - - Field names must not start with an underscore. - 18 - False - - - The field must have a documentation header. - 19 - False - - - Field names must not start with an underscore. - 19 - False - - - The field must have a documentation header. - 20 - False - - - Field names must not start with an underscore. - 20 - False - - - The field must have a documentation header. - 21 - False - - - Field names must not start with an underscore. - 21 - False - - - The field must have a documentation header. - 22 - False - - - Field names must not start with an underscore. - 22 - False - - - The property must have a documentation header. - 25 - False - - - An opening curly bracket must not be preceded by a blank line. - 27 - False - - - An accessor can only be placed on a single line if all of the accessors in the property are placed on a single line. - 28 - False - - - The call to _context must begin with the 'this.' prefix to indicate that the item is a member of the class. - 28 - False - - - Invalid spacing around the closing parenthesis. - 28 - False - - - Invalid spacing around the opening parenthesis. - 28 - False - - - The call to _context must begin with the 'this.' prefix to indicate that the item is a member of the class. - 33 - False - - - The call to _context must begin with the 'this.' prefix to indicate that the item is a member of the class. - 34 - False - - - The call to _context must begin with the 'this.' prefix to indicate that the item is a member of the class. - 31 - False - - - The call to _context must begin with the 'this.' prefix to indicate that the item is a member of the class. - 38 - False - - - Invalid spacing around the closing parenthesis. - 33 - False - - - Invalid spacing around the opening parenthesis. - 33 - False - - - Invalid spacing around the closing parenthesis. - 38 - False - - - Invalid spacing around the opening parenthesis. - 38 - False - - - The property must have a documentation header. - 45 - False - - - The call to _processId must begin with the 'this.' prefix to indicate that the item is a member of the class. - 47 - False - - - Invalid spacing around the closing parenthesis. - 47 - False - - - The property must have a documentation header. - 50 - False - - - The call to _isDirectory must begin with the 'this.' prefix to indicate that the item is a member of the class. - 52 - False - - - The call to _isDirectory must begin with the 'this.' prefix to indicate that the item is a member of the class. - 53 - False - - - The property must have a documentation header. - 56 - False - - - The call to _deleteOnClose must begin with the 'this.' prefix to indicate that the item is a member of the class. - 58 - False - - - The call to _deleteOnClose must begin with the 'this.' prefix to indicate that the item is a member of the class. - 59 - False - - - The property must have a documentation header. - 62 - False - - - The call to _pagingIo must begin with the 'this.' prefix to indicate that the item is a member of the class. - 64 - False - - - The property must have a documentation header. - 67 - False - - - The call to _synchronousIo must begin with the 'this.' prefix to indicate that the item is a member of the class. - 69 - False - - - The property must have a documentation header. - 72 - False - - - The call to _nocache must begin with the 'this.' prefix to indicate that the item is a member of the class. - 74 - False - - - The property must have a documentation header. - 77 - False - - - The call to _writeToEndOfFile must begin with the 'this.' prefix to indicate that the item is a member of the class. - 79 - False - - - The property must have a documentation header. - 82 - False - - - The method must have a documentation header. - 87 - False - - - Invalid spacing around the closing parenthesis. - 89 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 14:03:12 - 593 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - The code must not contain multiple blank lines in a row. - 4 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - An opening curly bracket must not be followed by a blank line. - 6 - False - - - The class must have a documentation header. - 8 - False - - - The code must not contain multiple blank lines in a row. - 119 - False - - - The field must have a documentation header. - 12 - False - - - Field names must not contain underscores. - 12 - False - - - The field must have a documentation header. - 24 - False - - - Field names must not contain underscores. - 24 - False - - - The field must have a documentation header. - 25 - False - - - Field names must not contain underscores. - 25 - False - - - The field must have a documentation header. - 26 - False - - - Field names must not contain underscores. - 26 - False - - - The field must have a documentation header. - 27 - False - - - Field names must not contain underscores. - 27 - False - - - The field must have a documentation header. - 28 - False - - - Field names must not contain underscores. - 28 - False - - - The field must have a documentation header. - 29 - False - - - Field names must not contain underscores. - 29 - False - - - The field must have a documentation header. - 30 - False - - - Field names must not contain underscores. - 30 - False - - - The method must have a documentation header. - 33 - False - - - All methods must be placed after all properties. - 130 - False - - - All methods must be placed after all properties. - 135 - False - - - The method must have a documentation header. - 38 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 41 - False - - - The method must have a documentation header. - 42 - False - - - Adjacent elements must be separated by a blank line. - 42 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 45 - False - - - Invalid spacing around the comma. - 42 - False - - - Invalid spacing around the comma. - 44 - False - - - The method must have a documentation header. - 46 - False - - - Adjacent elements must be separated by a blank line. - 46 - False - - - An opening curly bracket must not be followed by a blank line. - 47 - False - - - The code must not contain multiple blank lines in a row. - 49 - False - - - An opening curly bracket must not be followed by a blank line. - 53 - False - - - The code must not contain multiple blank lines in a row. - 60 - False - - - A closing curly bracket must not be preceded by a blank line. - 62 - False - - - Invalid spacing around the comma. - 46 - False - - - Invalid spacing around the closing parenthesis. - 55 - False - - - Invalid spacing around the closing parenthesis. - 57 - False - - - Invalid spacing around the closing parenthesis. - 58 - False - - - The method must have a documentation header. - 120 - False - - - The method must have a documentation header. - 125 - False - - - The property must have a documentation header. - 130 - False - - - Invalid spacing around the closing parenthesis. - 132 - False - - - The property must have a documentation header. - 135 - False - - - Invalid spacing around the closing parenthesis. - 137 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 11:54:06 - 609 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - The enum must have a documentation header. - 6 - False - - - The enumeration sub-item must have a documentation header. - 8 - False - - - The enumeration sub-item must have a documentation header. - 9 - False - - - The enumeration sub-item must have a documentation header. - 10 - False - - - The enumeration sub-item must have a documentation header. - 11 - False - - - The enumeration sub-item must have a documentation header. - 12 - False - - - The enumeration sub-item must have a documentation header. - 13 - False - - - The enumeration sub-item must have a documentation header. - 14 - False - - - The spacing around the symbol '=' is invalid. - 14 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 07/31/2010 18:22:01 - 875 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - The struct must have a documentation header. - 6 - False - - - The field must have a documentation header. - 8 - False - - - The field must have a documentation header. - 9 - False - - - The field must have a documentation header. - 10 - False - - - The field must have a documentation header. - 11 - False - - - The field must have a documentation header. - 12 - False - - - The field must have a documentation header. - 13 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 06/25/2011 14:48:06 - 406 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - The enum must have a documentation header. - 6 - False - - - The enumeration sub-item must have a documentation header. - 8 - False - - - The enumeration sub-item must have a documentation header. - 9 - False - - - The enumeration sub-item must have a documentation header. - 10 - False - - - The enumeration sub-item must have a documentation header. - 11 - False - - - The enumeration sub-item must have a documentation header. - 12 - False - - - The enumeration sub-item must have a documentation header. - 13 - False - - - The enumeration sub-item must have a documentation header. - 14 - False - - - The enumeration sub-item must have a documentation header. - 15 - False - - - The enumeration sub-item must have a documentation header. - 16 - False - - - The enumeration sub-item must have a documentation header. - 17 - False - - - The enumeration sub-item must have a documentation header. - 18 - False - - - The enumeration sub-item must have a documentation header. - 19 - False - - - The enumeration sub-item must have a documentation header. - 20 - False - - - The enumeration sub-item must have a documentation header. - 21 - False - - - The enumeration sub-item must have a documentation header. - 22 - False - - - The enumeration sub-item must have a documentation header. - 23 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 14:19:35 - 593 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - The enum must have a documentation header. - 6 - False - - - The enumeration sub-item must have a documentation header. - 8 - False - - - The enumeration sub-item must have a documentation header. - 9 - False - - - The enumeration sub-item must have a documentation header. - 10 - False - - - The enumeration sub-item must have a documentation header. - 11 - False - - - The enumeration sub-item must have a documentation header. - 12 - False - - - The enumeration sub-item must have a documentation header. - 13 - False - - - The enumeration sub-item must have a documentation header. - 14 - False - - - The enumeration sub-item must have a documentation header. - 15 - False - - - The enumeration sub-item must have a documentation header. - 16 - False - - - The enumeration sub-item must have a documentation header. - 17 - False - - - The enumeration sub-item must have a documentation header. - 18 - False - - - The enumeration sub-item must have a documentation header. - 19 - False - - - The enumeration sub-item must have a documentation header. - 20 - False - - - The enumeration sub-item must have a documentation header. - 21 - False - - - The enumeration sub-item must have a documentation header. - 22 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 06/25/2011 14:52:26 - 328 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - The struct must have a documentation header. - 7 - False - - - The field must have a documentation header. - 9 - False - - - The variable name 'dwFileAttributes' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 9 - False - - - Public and internal fields must start with an upper-case letter: dwFileAttributes. - 9 - False - - - The field must have a documentation header. - 10 - False - - - The variable name 'ftCreationTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 10 - False - - - Public and internal fields must start with an upper-case letter: ftCreationTime. - 10 - False - - - The field must have a documentation header. - 11 - False - - - The variable name 'ftLastAccessTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 11 - False - - - Public and internal fields must start with an upper-case letter: ftLastAccessTime. - 11 - False - - - The field must have a documentation header. - 12 - False - - - The variable name 'ftLastWriteTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 12 - False - - - Public and internal fields must start with an upper-case letter: ftLastWriteTime. - 12 - False - - - The field must have a documentation header. - 13 - False - - - The variable name 'dwVolumeSerialNumber' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 13 - False - - - Public and internal fields must start with an upper-case letter: dwVolumeSerialNumber. - 13 - False - - - All internal fields must be placed after all public fields. - 14 - False - - - All internal fields must be placed after all public fields. - 15 - False - - - The field must have a documentation header. - 14 - False - - - The variable name 'nFileSizeHigh' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 14 - False - - - Public and internal fields must start with an upper-case letter: nFileSizeHigh. - 14 - False - - - The field must have a documentation header. - 15 - False - - - The variable name 'nFileSizeLow' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 15 - False - - - Public and internal fields must start with an upper-case letter: nFileSizeLow. - 15 - False - - - The field must have a documentation header. - 16 - False - - - The variable name 'dwNumberOfLinks' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 16 - False - - - Public and internal fields must start with an upper-case letter: dwNumberOfLinks. - 16 - False - - - The field must have a documentation header. - 17 - False - - - The variable name 'nFileIndexHigh' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 17 - False - - - Public and internal fields must start with an upper-case letter: nFileIndexHigh. - 17 - False - - - The field must have a documentation header. - 18 - False - - - The variable name 'nFileIndexLow' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 18 - False - - - Public and internal fields must start with an upper-case letter: nFileIndexLow. - 18 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 06/26/2011 12:46:49 - 500 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - The struct must have a documentation header. - 7 - False - - - The field must have a documentation header. - 9 - False - - - The field must have a documentation header. - 10 - False - - - The field must have a documentation header. - 11 - False - - - The field must have a documentation header. - 12 - False - - - The field must have a documentation header. - 13 - False - - - The field must have a documentation header. - 14 - False - - - The field must have a documentation header. - 15 - False - - - The field must have a documentation header. - 16 - False - - - The field must have a documentation header. - 17 - False - - - The field must have a documentation header. - 18 - False - - - The field must have a documentation header. - 20 - False - - - The field must have a documentation header. - 22 - False - - - The field must have a documentation header. - 23 - False - - - The field must have a documentation header. - 24 - False - - - The field must have a documentation header. - 25 - False - - - The field must have a documentation header. - 26 - False - - - The field must have a documentation header. - 27 - False - - - The field must have a documentation header. - 28 - False - - - The field must have a documentation header. - 29 - False - - - The field must have a documentation header. - 30 - False - - - The field must have a documentation header. - 31 - False - - - The field must have a documentation header. - 32 - False - - - The field must have a documentation header. - 33 - False - - - The field must have a documentation header. - 35 - False - - - The field must have a documentation header. - 36 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 06/25/2011 14:13:10 - 562 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - The struct must have a documentation header. - 6 - False - - - The field must have a documentation header. - 8 - False - - - The field must have a documentation header. - 9 - False - - - The field must have a documentation header. - 10 - False - - - The field must have a documentation header. - 11 - False - - - The field must have a documentation header. - 12 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 14:19:35 - 578 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - The class must have a documentation header. - 6 - False - - - The code must not contain multiple blank lines in a row. - 29 - False - - - Invalid spacing around the comma. - 8 - False - - - Invalid spacing around the comma. - 11 - False - - - Invalid spacing around the comma. - 14 - False - - - Invalid spacing around the comma. - 17 - False - - - Invalid spacing around the comma. - 20 - False - - - Invalid spacing around the comma. - 23 - False - - - Invalid spacing around the closing parenthesis. - 23 - False - - - Invalid spacing around the closing attribute bracket. - 23 - False - - - Invalid spacing around the colon. - 23 - False - - - Invalid spacing around the comma. - 26 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 12:08:18 - 296 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - All using directives must be placed inside of the namespace. - 3 - False - - - All using directives must be placed inside of the namespace. - 4 - False - - - All using directives must be placed inside of the namespace. - 5 - False - - - The struct must have a documentation header. - 10 - False - - - The code must not contain multiple blank lines in a row. - 15 - False - - - The code must not contain multiple blank lines in a row. - 19 - False - - - Invalid spacing around the comma. - 9 - False - - - The field must have a documentation header. - 12 - False - - - The field must have a documentation header. - 13 - False - - - Invalid spacing around the opening curly bracket. - 13 - False - - - Invalid spacing around the closing curly bracket. - 13 - False - - - The field must have a documentation header. - 16 - False - - - The variable name 'dwLowDateTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 16 - False - - - Public and internal fields must start with an upper-case letter: dwLowDateTime. - 16 - False - - - The field must have a documentation header. - 17 - False - - - The variable name 'dwHighDateTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 17 - False - - - Public and internal fields must start with an upper-case letter: dwHighDateTime. - 17 - False - - - The method must have a documentation header. - 20 - False - - - The method must have a documentation header. - 25 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 12:00:13 - 437 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - All using directives must be placed inside of the namespace. - 3 - False - - - The struct must have a documentation header. - 8 - False - - - The field must have a documentation header. - 10 - False - - - The variable name 'dwFileAttributes' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 10 - False - - - Public and internal fields must start with an upper-case letter: dwFileAttributes. - 10 - False - - - The field must have a documentation header. - 11 - False - - - The variable name 'ftCreationTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 11 - False - - - Public and internal fields must start with an upper-case letter: ftCreationTime. - 11 - False - - - The field must have a documentation header. - 12 - False - - - The variable name 'ftLastAccessTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 12 - False - - - Public and internal fields must start with an upper-case letter: ftLastAccessTime. - 12 - False - - - The field must have a documentation header. - 13 - False - - - The variable name 'ftLastWriteTime' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 13 - False - - - Public and internal fields must start with an upper-case letter: ftLastWriteTime. - 13 - False - - - The field must have a documentation header. - 14 - False - - - The variable name 'nFileSizeHigh' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 14 - False - - - Public and internal fields must start with an upper-case letter: nFileSizeHigh. - 14 - False - - - The field must have a documentation header. - 15 - False - - - The variable name 'nFileSizeLow' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 15 - False - - - Public and internal fields must start with an upper-case letter: nFileSizeLow. - 15 - False - - - The field must have a documentation header. - 16 - False - - - The variable name 'dwReserved0' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 16 - False - - - All private fields must be placed after all public fields. - 18 - False - - - The field must have a documentation header. - 17 - False - - - The variable name 'dwReserved1' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 17 - False - - - The field must have a documentation header. - 18 - False - - - The variable name 'cFileName' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 18 - False - - - Public and internal fields must start with an upper-case letter: cFileName. - 18 - False - - - The field must have a documentation header. - 19 - False - - - The variable name 'cAlternateFileName' begins with a prefix that looks like Hungarian notation. Remove the prefix or add it to the list of allowed prefixes. - 19 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 06/30/2011 18:59:46 - 859 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - A single-line comment must not be followed by a blank line. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 9 - False - - - A single-line comment must not be followed by a blank line. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 22 - False - - - The code must not contain multiple blank lines in a row. - 27 - False - - - A single-line comment must not be followed by a blank line. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 28 - False - - - The code must not contain multiple blank lines in a row. - 32 - False - - - A single-line comment must not be followed by a blank line. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 42 - False - - - The comment is empty. Add text to the comment or remove it. - 5 - False - - - The comment is empty. Add text to the comment or remove it. - 9 - False - - - The comment is empty. Add text to the comment or remove it. - 33 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 14:03:12 - 625 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - All using directives must be placed inside of the namespace. - 3 - False - - - All using directives must be placed inside of the namespace. - 4 - False - - - The interface must have a documentation header. - 8 - False - - - The method must have a documentation header. - 10 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 10 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 10 - False - - - The parameter must begin on the line after the previous parameter. - 10 - False - - - The parameter must begin on the line after the previous parameter. - 11 - False - - - The method must have a documentation header. - 13 - False - - - The method must have a documentation header. - 15 - False - - - The method must have a documentation header. - 17 - False - - - The method must have a documentation header. - 19 - False - - - The method must have a documentation header. - 21 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 21 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 21 - False - - - The parameter must begin on the line after the previous parameter. - 21 - False - - - The method must have a documentation header. - 24 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 24 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 24 - False - - - The parameter must begin on the line after the previous parameter. - 24 - False - - - The parameter must begin on the line after the previous parameter. - 25 - False - - - The method must have a documentation header. - 27 - False - - - The method must have a documentation header. - 29 - False - - - The method must have a documentation header. - 31 - False - - - The method must have a documentation header. - 33 - False - - - The method must have a documentation header. - 35 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 35 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 35 - False - - - The parameter must begin on the line after the previous parameter. - 35 - False - - - The parameter must begin on the line after the previous parameter. - 36 - False - - - The method must have a documentation header. - 38 - False - - - The method must have a documentation header. - 40 - False - - - The method must have a documentation header. - 42 - False - - - The method must have a documentation header. - 44 - False - - - The method must have a documentation header. - 46 - False - - - The method must have a documentation header. - 48 - False - - - The method must have a documentation header. - 50 - False - - - The method must have a documentation header. - 52 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 52 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 52 - False - - - The parameter must begin on the line after the previous parameter. - 52 - False - - - The method must have a documentation header. - 55 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 55 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 55 - False - - - The parameter must begin on the line after the previous parameter. - 55 - False - - - The parameter must begin on the line after the previous parameter. - 56 - False - - - The method must have a documentation header. - 58 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 58 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 58 - False - - - The parameter must begin on the line after the previous parameter. - 58 - False - - - The method must have a documentation header. - 61 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 61 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 61 - False - - - The parameter must begin on the line after the previous parameter. - 61 - False - - - The method must have a documentation header. - 64 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 12:09:38 - 781 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - All using directives must be placed inside of the namespace. - 3 - False - - - All using directives must be placed inside of the namespace. - 4 - False - - - The enum must have a documentation header. - 9 - False - - - A single-line comment must be preceded by a blank line or another single-line comment, or must be the first item in its scope. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 18 - False - - - The enumeration sub-item must have a documentation header. - 15 - False - - - The enumeration sub-item must have a documentation header. - 16 - False - - - The enumeration sub-item must have a documentation header. - 17 - False - - - The enumeration sub-item must have a documentation header. - 19 - False - - - The enumeration sub-item must have a documentation header. - 20 - False - - - The enumeration sub-item must have a documentation header. - 21 - False - - - The enumeration sub-item must have a documentation header. - 22 - False - - - - - - 06/30/2010 12:43:56 - 0 - - 08/21/2011 14:03:12 - 703 - - - The file has no header, the header Xml is invalid, or the header is not located at the top of the file. - 1 - False - - - All using directives must be placed inside of the namespace. - 1 - False - - - All using directives must be placed inside of the namespace. - 2 - False - - - All using directives must be placed inside of the namespace. - 3 - False - - - All using directives must be placed inside of the namespace. - 4 - False - - - All using directives must be placed inside of the namespace. - 5 - False - - - All using directives must be placed inside of the namespace. - 6 - False - - - All using directives must be placed inside of the namespace. - 7 - False - - - All using directives must be placed inside of the namespace. - 8 - False - - - All using directives must be placed inside of the namespace. - 9 - False - - - The class must have a documentation header. - 13 - False - - - An opening curly bracket must not be followed by a blank line. - 14 - False - - - The code must not contain multiple blank lines in a row. - 16 - False - - - The code must not contain multiple blank lines in a row. - 141 - False - - - The code must not contain multiple blank lines in a row. - 146 - False - - - The code must not contain multiple blank lines in a row. - 248 - False - - - The code must not contain multiple blank lines in a row. - 360 - False - - - The code must not contain multiple blank lines in a row. - 397 - False - - - The code must not contain multiple blank lines in a row. - 448 - False - - - The code must not contain multiple blank lines in a row. - 465 - False - - - The code must not contain multiple blank lines in a row. - 671 - False - - - The delegate must have a documentation header. - 19 - False - - - All delegates must be placed after all fields. - 135 - False - - - All delegates must be placed after all fields. - 136 - False - - - All delegates must be placed after all fields. - 137 - False - - - All delegates must be placed after all fields. - 138 - False - - - All delegates must be placed after all fields. - 139 - False - - - All delegates must be placed after all fields. - 142 - False - - - All delegates must be placed after all fields. - 144 - False - - - All delegates must be placed after all constructors. - 147 - False - - - The delegate must have a documentation header. - 23 - False - - - The delegate must have a documentation header. - 27 - False - - - The delegate must have a documentation header. - 31 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 31 - False - - - The parameter must begin on the line after the previous parameter. - 32 - False - - - The parameter must begin on the line after the previous parameter. - 33 - False - - - The delegate must have a documentation header. - 36 - False - - - The delegate must have a documentation header. - 40 - False - - - The delegate must have a documentation header. - 44 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 44 - False - - - The parameter must begin on the line after the previous parameter. - 45 - False - - - The delegate must have a documentation header. - 54 - False - - - The delegate must have a documentation header. - 58 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 58 - False - - - If the delegate parameters are on separate lines, the first parameter must begin on the line beneath the name of the delegate. - 58 - False - - - The parameter must begin on the line after the previous parameter. - 58 - False - - - The delegate must have a documentation header. - 62 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 62 - False - - - The parameter must begin on the line after the previous parameter. - 63 - False - - - The delegate must have a documentation header. - 66 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 66 - False - - - The parameter must begin on the line after the previous parameter. - 67 - False - - - The parameter must begin on the line after the previous parameter. - 68 - False - - - Invalid spacing around the comma. - 67 - False - - - The delegate must have a documentation header. - 72 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 72 - False - - - The parameter must begin on the line after the previous parameter. - 73 - False - - - The parameter must begin on the line after the previous parameter. - 75 - False - - - The parameter must begin on the line after the previous parameter. - 77 - False - - - The delegate must have a documentation header. - 79 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 79 - False - - - The parameter must begin on the line after the previous parameter. - 80 - False - - - The delegate must have a documentation header. - 83 - False - - - The delegate must have a documentation header. - 89 - False - - - The delegate must have a documentation header. - 93 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 93 - False - - - The parameter must begin on the line after the previous parameter. - 95 - False - - - The parameter must begin on the line after the previous parameter. - 96 - False - - - The delegate must have a documentation header. - 99 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 99 - False - - - The parameter must begin on the line after the previous parameter. - 100 - False - - - The delegate must have a documentation header. - 103 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 103 - False - - - The parameter must begin on the line after the previous parameter. - 104 - False - - - The delegate must have a documentation header. - 107 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 107 - False - - - The parameter must begin on the line after the previous parameter. - 108 - False - - - The delegate must have a documentation header. - 111 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 111 - False - - - The parameter must begin on the line after the previous parameter. - 112 - False - - - The parameter must begin on the line after the previous parameter. - 113 - False - - - Invalid spacing around the comma. - 112 - False - - - The delegate must have a documentation header. - 116 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 116 - False - - - The parameter must begin on the line after the previous parameter. - 120 - False - - - The delegate must have a documentation header. - 122 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 122 - False - - - The parameter must begin on the line after the previous parameter. - 123 - False - - - The delegate must have a documentation header. - 126 - False - - - The delegate must have a documentation header. - 128 - False - - - All delegate parameters must be placed on the same line, or each parameter must be placed on a separate line. - 128 - False - - - The parameter must begin on the line after the previous parameter. - 130 - False - - - The parameter must begin on the line after the previous parameter. - 131 - False - - - The field must have a documentation header. - 135 - False - - - Field names must not contain underscores. - 135 - False - - - The field must have a documentation header. - 136 - False - - - Field names must not contain underscores. - 136 - False - - - The field must have a documentation header. - 137 - False - - - Field names must not contain underscores. - 137 - False - - - The field must have a documentation header. - 138 - False - - - Field names must not contain underscores. - 138 - False - - - The field must have a documentation header. - 139 - False - - - Field names must not contain underscores. - 139 - False - - - The field must have a documentation header. - 142 - False - - - Field names must not start with an underscore. - 142 - False - - - The field must have a documentation header. - 144 - False - - - Field names must not start with an underscore. - 144 - False - - - Invalid spacing around the closing parenthesis. - 144 - False - - - The constructor must have a documentation header. - 147 - False - - - A closing curly bracket must not be preceded by a blank line. - 151 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 149 - False - - - The method must have a documentation header. - 153 - False - - - A single-line comment must be preceded by a blank line or another single-line comment, or must be the first item in its scope. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 163 - False - - - All methods must be placed after all delegates. - 788 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 153 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 153 - False - - - The parameter must begin on the line after the previous parameter. - 153 - False - - - The parameter must begin on the line after the previous parameter. - 154 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 159 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 159 - False - - - The parameter must begin on the line after the previous parameter. - 159 - False - - - The parameter must begin on the line after the previous parameter. - 162 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 159 - False - - - Invalid spacing around the closing parenthesis. - 159 - False - - - Invalid spacing around the closing parenthesis. - 160 - False - - - Invalid spacing around the closing parenthesis. - 161 - False - - - Invalid spacing around the opening parenthesis. - 161 - False - - - The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 161 - False - - - Invalid spacing around the closing parenthesis. - 162 - False - - - Invalid spacing around the opening parenthesis. - 162 - False - - - The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 163 - False - - - The method must have a documentation header. - 176 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 176 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 181 - False - - - Invalid spacing around the closing parenthesis. - 181 - False - - - The method must have a documentation header. - 194 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 194 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 199 - False - - - Invalid spacing around the closing parenthesis. - 199 - False - - - The method must have a documentation header. - 212 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 212 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 217 - False - - - Invalid spacing around the closing parenthesis. - 217 - False - - - The method must have a documentation header. - 230 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 230 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 235 - False - - - Invalid spacing around the closing parenthesis. - 235 - False - - - The method must have a documentation header. - 249 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 249 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 249 - False - - - The parameter must begin on the line after the previous parameter. - 249 - False - - - The parameter must begin on the line after the previous parameter. - 250 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 255 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 255 - False - - - The parameter must begin on the line after the previous parameter. - 255 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 255 - False - - - Invalid spacing around the closing parenthesis. - 255 - False - - - The method must have a documentation header. - 269 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 269 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 269 - False - - - The parameter must begin on the line after the previous parameter. - 269 - False - - - The parameter must begin on the line after the previous parameter. - 270 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 275 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 275 - False - - - The parameter must begin on the line after the previous parameter. - 275 - False - - - The parameter must begin on the line after the previous parameter. - 276 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 275 - False - - - Invalid spacing around the closing parenthesis. - 275 - False - - - The method must have a documentation header. - 290 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 290 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 295 - False - - - Invalid spacing around the closing parenthesis. - 295 - False - - - The method must have a documentation header. - 308 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 308 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 315 - False - - - The call to _serialNumber must begin with the 'this.' prefix to indicate that the item is a member of the class. - 338 - False - - - Invalid spacing around the semicolon. - 312 - False - - - Invalid spacing around the closing parenthesis. - 315 - False - - - Invalid spacing around the closing parenthesis. - 319 - False - - - Invalid spacing around the closing parenthesis. - 324 - False - - - Invalid spacing around the opening parenthesis. - 324 - False - - - Invalid spacing around the closing parenthesis. - 326 - False - - - Invalid spacing around the opening parenthesis. - 326 - False - - - Invalid spacing around the closing parenthesis. - 329 - False - - - Invalid spacing around the opening parenthesis. - 329 - False - - - Invalid spacing around the closing parenthesis. - 331 - False - - - Invalid spacing around the opening parenthesis. - 331 - False - - - Invalid spacing around the closing parenthesis. - 334 - False - - - Invalid spacing around the opening parenthesis. - 334 - False - - - Invalid spacing around the closing parenthesis. - 336 - False - - - Invalid spacing around the opening parenthesis. - 336 - False - - - Invalid spacing around the closing parenthesis. - 340 - False - - - Invalid spacing around the opening parenthesis. - 340 - False - - - Invalid spacing around the closing parenthesis. - 341 - False - - - Invalid spacing around the opening parenthesis. - 341 - False - - - Invalid spacing around the closing parenthesis. - 344 - False - - - The method must have a documentation header. - 362 - False - - - A single-line comment must be preceded by a blank line or another single-line comment, or must be the first item in its scope. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 363 - False - - - The code must not contain multiple blank lines in a row. - 370 - False - - - The code must not contain multiple blank lines in a row. - 373 - False - - - A single-line comment must be preceded by a blank line or another single-line comment, or must be the first item in its scope. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 379 - False - - - An opening curly bracket must not be preceded by a blank line. - 382 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 385 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 362 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 362 - False - - - The parameter must begin on the line after the previous parameter. - 362 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 371 - False - - - Invalid spacing around the semicolon. - 368 - False - - - Invalid spacing around the closing parenthesis. - 371 - False - - - The spacing around the symbol '&&' is invalid. - 375 - False - - - The method must have a documentation header. - 398 - False - - - A single-line comment must be preceded by a blank line or another single-line comment, or must be the first item in its scope. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 425 - False - - - A single-line comment must not be followed by a blank line. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 425 - False - - - All private methods must be placed after all public methods. - 432 - False - - - All private methods must be placed after all public methods. - 449 - False - - - All private methods must be placed after all public methods. - 468 - False - - - All private methods must be placed after all public methods. - 486 - False - - - All private methods must be placed after all public methods. - 523 - False - - - All private methods must be placed after all public methods. - 540 - False - - - All private methods must be placed after all public methods. - 558 - False - - - All private methods must be placed after all public methods. - 578 - False - - - All private methods must be placed after all public methods. - 597 - False - - - All private methods must be placed after all public methods. - 617 - False - - - All private methods must be placed after all public methods. - 635 - False - - - All private methods must be placed after all public methods. - 672 - False - - - All private methods must be placed after all public methods. - 687 - False - - - All private methods must be placed after all public methods. - 741 - False - - - Invalid spacing around the closing parenthesis. - 408 - False - - - Invalid spacing around the opening parenthesis. - 408 - False - - - Invalid spacing around the closing parenthesis. - 409 - False - - - Invalid spacing around the opening parenthesis. - 409 - False - - - Invalid spacing around the closing parenthesis. - 413 - False - - - Invalid spacing around the opening parenthesis. - 413 - False - - - Invalid spacing around the closing parenthesis. - 414 - False - - - Invalid spacing around the opening parenthesis. - 414 - False - - - Invalid spacing around the closing parenthesis. - 418 - False - - - Invalid spacing around the opening parenthesis. - 418 - False - - - Invalid spacing around the closing parenthesis. - 419 - False - - - Invalid spacing around the opening parenthesis. - 419 - False - - - Invalid spacing around the closing parenthesis. - 421 - False - - - Invalid spacing around the opening parenthesis. - 421 - False - - - Invalid spacing around the closing parenthesis. - 422 - False - - - Invalid spacing around the opening parenthesis. - 422 - False - - - The comment must start with a single space. To ignore this error when commenting out a line of code, begin the comment with '////' rather than '//'. - 425 - False - - - The method must have a documentation header. - 432 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 432 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 432 - False - - - The parameter must begin on the line after the previous parameter. - 432 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 437 - False - - - Invalid spacing around the closing parenthesis. - 437 - False - - - The method must have a documentation header. - 449 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 449 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 449 - False - - - The parameter must begin on the line after the previous parameter. - 449 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 454 - False - - - Invalid spacing around the closing parenthesis. - 454 - False - - - The method must have a documentation header. - 468 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 468 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 468 - False - - - The parameter must begin on the line after the previous parameter. - 468 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 473 - False - - - Invalid spacing around the closing parenthesis. - 473 - False - - - The method must have a documentation header. - 486 - False - - - An opening curly bracket must not be followed by a blank line. - 491 - False - - - The code must not contain multiple blank lines in a row. - 506 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 486 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 509 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 509 - False - - - The parameter must begin on the line after the previous parameter. - 509 - False - - - The parameter must begin on the line after the previous parameter. - 510 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 509 - False - - - The spacing around the symbol '!=' is invalid. - 493 - False - - - Invalid spacing around the closing parenthesis. - 494 - False - - - Invalid spacing around the closing parenthesis. - 495 - False - - - Invalid spacing around the closing parenthesis. - 496 - False - - - The spacing around the symbol '!=' is invalid. - 497 - False - - - Invalid spacing around the closing parenthesis. - 498 - False - - - Invalid spacing around the closing parenthesis. - 499 - False - - - Invalid spacing around the closing parenthesis. - 500 - False - - - The spacing around the symbol '!=' is invalid. - 501 - False - - - Invalid spacing around the closing parenthesis. - 502 - False - - - Invalid spacing around the closing parenthesis. - 503 - False - - - Invalid spacing around the closing parenthesis. - 504 - False - - - Invalid spacing around the closing parenthesis. - 509 - False - - - The method must have a documentation header. - 523 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 527 - False - - - Invalid spacing around the closing parenthesis. - 527 - False - - - The method must have a documentation header. - 540 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 540 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 545 - False - - - Invalid spacing around the closing parenthesis. - 545 - False - - - The method must have a documentation header. - 558 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 558 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 558 - False - - - The parameter must begin on the line after the previous parameter. - 559 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 564 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 564 - False - - - The parameter must begin on the line after the previous parameter. - 564 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 564 - False - - - Invalid spacing around the closing parenthesis. - 564 - False - - - The method must have a documentation header. - 578 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 578 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 578 - False - - - The parameter must begin on the line after the previous parameter. - 578 - False - - - The parameter must begin on the line after the previous parameter. - 579 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 584 - False - - - Invalid spacing around the closing parenthesis. - 584 - False - - - The method must have a documentation header. - 597 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 597 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 597 - False - - - The parameter must begin on the line after the previous parameter. - 597 - False - - - The parameter must begin on the line after the previous parameter. - 598 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 604 - False - - - The method must have a documentation header. - 617 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 617 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 617 - False - - - The parameter must begin on the line after the previous parameter. - 617 - False - - - The parameter must begin on the line after the previous parameter. - 618 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 622 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 622 - False - - - The parameter must begin on the line after the previous parameter. - 622 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 622 - False - - - Invalid spacing around the closing parenthesis. - 622 - False - - - The method must have a documentation header. - 635 - False - - - An opening curly bracket must not be followed by a blank line. - 648 - False - - - A closing curly bracket must not be preceded by a blank line. - 659 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 659 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 635 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 635 - False - - - The parameter must begin on the line after the previous parameter. - 638 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 650 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 650 - False - - - The parameter must begin on the line after the previous parameter. - 651 - False - - - The call to _serialNumber must begin with the 'this.' prefix to indicate that the item is a member of the class. - 644 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 650 - False - - - Invalid spacing around the closing parenthesis. - 650 - False - - - The method must have a documentation header. - 672 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 676 - False - - - Invalid spacing around the closing parenthesis. - 676 - False - - - The method must have a documentation header. - 687 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 697 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 701 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 707 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 713 - False - - - An opening curly bracket must not be followed by a blank line. - 715 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 725 - False - - - The code must not contain multiple blank lines in a row. - 728 - False - - - A closing curly bracket must not be preceded by a blank line. - 729 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 729 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 687 - False - - - If the method parameters are on separate lines, the first parameter must begin on the line beneath the name of the method. - 687 - False - - - The parameter must begin on the line after the previous parameter. - 687 - False - - - The parameter must begin on the line after the previous parameter. - 688 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 717 - False - - - Invalid spacing around the closing parenthesis. - 717 - False - - - Invalid spacing around the closing parenthesis. - 723 - False - - - The method must have a documentation header. - 741 - False - - - An opening curly bracket must not be preceded by a blank line. - 746 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 751 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 755 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 761 - False - - - Statements or elements wrapped in curly brackets must be followed by a blank line. - 767 - False - - - All method parameters must be placed on the same line, or each parameter must be placed on a separate line. - 741 - False - - - The parameter must begin on the line after the previous parameter. - 742 - False - - - The parameter must begin on the line after the previous parameter. - 743 - False - - - The call to _operations must begin with the 'this.' prefix to indicate that the item is a member of the class. - 775 - False - - - Invalid spacing around the closing parenthesis. - 771 - False - - - Invalid spacing around the closing parenthesis. - 772 - False - - - Invalid spacing around the closing parenthesis. - 775 - False - - - The delegate must have a documentation header. - 788 - False - - - - \ No newline at end of file diff --git a/DokanNet/WinSSH4e1-public.snk b/DokanNet/WinSSH4e1-public.snk deleted file mode 100644 index d64facbaf6f8f1f9124741c0367df314668125a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096~H-={bxh^^?U0dKOEZYw+ZK^0WCK78f z7Lrx7#Uk|;lhIpUvmK<}zCLdC*|-V?-SpA1yrIT?rYrEU#L=?qJwiePdLUA>!^;=B zfOn1-4tXfdY3vNh`dPAu;xwL^~P?JIdU4St=Tj*(CA!$qnew}95%+cH!0c3Bg3`d*cC-w>a3fT0F1ahZx9KZ#eoLwGke21WKd4*D>I$(09HLhOz0@fP1KOnwFxl^Z)16f z?rVd*?%Y48*ZKWd?9+TH@dl^q8;3J=ZrYa{2ux$JDzS1+L5Mv7g7J|Wvtf+(98@1@ z?rG@%95`MPbizi^%dnm1&zu~RF%BG&G`n6O)h-NOD&dcb@w!fA zIAayDAeiTBmR2ucNg(^qk92(4@^j5Ggi| iY+5nt3VuPH#1G#Kje%Vk>Z4#+xAJ6rcY0XH3vUg9v>|{1 diff --git a/README.md b/README.md index 80b7adc..a544f68 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,11 @@ WinSshFS 4every1 edition I decided to share my clone of win-sshfs based on I did some improvments for my needs. Current devel branch version seems to be very stable. -![img](https://habrastorage.org/files/d8d/73c/c3c/d8d73cc3c066461988cca55ea1336714.jpg "UI") +![img](https://cloud.githubusercontent.com/assets/1085397/10747956/3f684d3a-7c18-11e5-8ca6-0f37a60426e4.jpg "UI") There are several changes, main differences: +* Windows 10 Support * current Renci SSH (2014.4.6beta) * solved few bugs like payload, 2 hosts and others * Puttyant (Pageant) support diff --git a/Renci.SshNet/Sftp/SftpFileAttributes.cs b/Renci.SshNet/Sftp/SftpFileAttributes.cs index 525820e..ab53808 100644 --- a/Renci.SshNet/Sftp/SftpFileAttributes.cs +++ b/Renci.SshNet/Sftp/SftpFileAttributes.cs @@ -4,8 +4,6 @@ using System.Globalization; using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("WinSshFS, PublicKey=0024000004800000940000000602000000240000525341310004000001000100bb1df492d3d63bb4aedb73672b0c0694ad7838ea17c6d49685ef1a03301ae6de5a4b4b1795844de0ddab49254d05bd533b5a02c24a580346274b204b8975536ffe36b4e7bb8c82e419264c335682ad44861e99eef16e95ee978742064c9335283ecb6e2fd325ea97942a51a3fc1a7d9948dd919b0d4ad25148d5490cc37f85b4")] - namespace Renci.SshNet.Sftp { /// diff --git a/Renci.SshNet/Sftp/SftpFileSystemInformation.cs b/Renci.SshNet/Sftp/SftpFileSystemInformation.cs deleted file mode 100644 index e5bb503..0000000 --- a/Renci.SshNet/Sftp/SftpFileSystemInformation.cs +++ /dev/null @@ -1,136 +0,0 @@ -namespace Renci.SshNet.Sftp -{ - /// - /// Contains File system information exposed by statvfs@openssh.com request. - /// - public class SftpFileSytemInformation - { - private readonly ulong _flag; - - private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; - - private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - public ulong BlockSize { get; private set; } - - /// - /// Gets the total blocks. - /// - /// - /// The total blocks. - /// - public ulong TotalBlocks { get; private set; } - - /// - /// Gets the free blocks. - /// - /// - /// The free blocks. - /// - public ulong FreeBlocks { get; private set; } - - /// - /// Gets the available blocks. - /// - /// - /// The available blocks. - /// - public ulong AvailableBlocks { get; private set; } - - /// - /// Gets the total nodes. - /// - /// - /// The total nodes. - /// - public ulong TotalNodes { get; private set; } - - /// - /// Gets the free nodes. - /// - /// - /// The free nodes. - /// - public ulong FreeNodes { get; private set; } - - /// - /// Gets the available nodes. - /// - /// - /// The available nodes. - /// - public ulong AvailableNodes { get; private set; } - - /// - /// Gets the sid. - /// - /// - /// The sid. - /// - public ulong Sid { get; private set; } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } - } - - /// - /// Gets a value indicating whether [supports set uid]. - /// - /// - /// true if [supports set uid]; otherwise, false. - /// - public bool SupportsSetUid - { - get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } - } - - /// - /// Gets the max name lenght. - /// - /// - /// The max name lenght. - /// - public ulong MaxNameLenght { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bsize. - /// The frsize. - /// The blocks. - /// The bfree. - /// The bavail. - /// The files. - /// The ffree. - /// The favail. - /// The sid. - /// The flag. - /// The namemax. - internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) - { - this.BlockSize = frsize; - this.TotalBlocks = blocks; - this.FreeBlocks = bfree; - this.AvailableBlocks = bavail; - this.TotalNodes = files; - this.FreeNodes = ffree; - this.AvailableNodes = favail; - this.Sid = sid; - this._flag = flag; - this.MaxNameLenght = namemax; - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs index c556ddf..f0b5954 100644 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ b/Renci.SshNet/Sftp/SftpSession.cs @@ -10,9 +10,6 @@ using Renci.SshNet.Sftp.Requests; using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("WinSshFS, PublicKey=00240000048000009400000006020000002400005253413100040000010001005337866700b92e3a2a5d5be0292cdb0f2f6daa283526126b30169255b3c522f51593d15b5db31da4ddbe3e6ef5d9b80a05ddf4d1b1bca1c67ca62bf0b0c4d1b2ea3d4242027a2052b3c3cb17b98077a5c9f08143617ec3a1143c97c48bf27a378a9ec250220fb899f25c084599f477e36f699ec74aa452a3fd9e90007648a397")] - namespace Renci.SshNet.Sftp { internal class SftpSession : SubsystemSession diff --git a/Sshfs/Sshfs.sln b/Sshfs/Sshfs.sln index c164303..5812a1f 100644 --- a/Sshfs/Sshfs.sln +++ b/Sshfs/Sshfs.sln @@ -1,91 +1,93 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DokanNet", "..\DokanNet\DokanNet.csproj", "{A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}" +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sshfs", "Sshfs\Sshfs.csproj", "{FF4FC8BB-91A3-45FF-8449-650647610394}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet", "..\Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sshfs", "Sshfs\Sshfs.csproj", "{FF4FC8BB-91A3-45FF-8449-650647610394}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 DebugNoTimeout|Any CPU = DebugNoTimeout|Any CPU DebugNoTimeout|Mixed Platforms = DebugNoTimeout|Mixed Platforms + DebugNoTimeout|x64 = DebugNoTimeout|x64 DebugNoTimeout|x86 = DebugNoTimeout|x86 Release|Any CPU = Release|Any CPU Release|Mixed Platforms = Release|Mixed Platforms + Release|x64 = Release|x64 Release|x86 = Release|x86 ReleaseFW2|Any CPU = ReleaseFW2|Any CPU ReleaseFW2|Mixed Platforms = ReleaseFW2|Mixed Platforms + ReleaseFW2|x64 = ReleaseFW2|x64 ReleaseFW2|x86 = ReleaseFW2|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|x86.ActiveCfg = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Debug|x86.Build.0 = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.DebugNoTimeout|x86.ActiveCfg = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.DebugNoTimeout|x86.Build.0 = Debug|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|Any CPU.Build.0 = Release|Any CPU - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|Mixed Platforms.Build.0 = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|x86.ActiveCfg = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.Release|x86.Build.0 = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.ReleaseFW2|Any CPU.ActiveCfg = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.ReleaseFW2|Mixed Platforms.Build.0 = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.ReleaseFW2|x86.ActiveCfg = Release|x86 - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C}.ReleaseFW2|x86.Build.0 = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Any CPU.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x64.ActiveCfg = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x64.Build.0 = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x64.ActiveCfg = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x64.Build.0 = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Any CPU.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.Build.0 = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x64.ActiveCfg = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x64.Build.0 = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.Build.0 = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Any CPU.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.Build.0 = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x64.ActiveCfg = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x64.Build.0 = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.Build.0 = Release|x86 {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.Build.0 = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.Build.0 = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.Build.0 = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.Build.0 = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.Build.0 = Debug|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.Build.0 = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.Build.0 = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.ActiveCfg = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.Build.0 = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.Build.0 = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Any CPU.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Any CPU.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.Build.0 = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.Build.0 = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Any CPU.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.Build.0 = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.Build.0 = Release|x86 + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Sshfs/Sshfs/MainForm.Designer.cs b/Sshfs/Sshfs/MainForm.Designer.cs index dfa8c2d..314e2e5 100644 --- a/Sshfs/Sshfs/MainForm.Designer.cs +++ b/Sshfs/Sshfs/MainForm.Designer.cs @@ -64,6 +64,8 @@ private void InitializeComponent() this.mountPointBox = new System.Windows.Forms.TextBox(); this.label10 = new System.Windows.Forms.Label(); this.proxyType = new System.Windows.Forms.ComboBox(); + this.labelKeepAlive = new System.Windows.Forms.Label(); + this.keepAliveIntervalBox = new System.Windows.Forms.NumericUpDown(); this.driveListView = new System.Windows.Forms.ListView(); this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.imageList = new System.Windows.Forms.ImageList(this.components); @@ -88,19 +90,17 @@ private void InitializeComponent() this.startupMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.exitMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.labelKeepAlive = new System.Windows.Forms.Label(); - this.keepAliveIntervalBox = new System.Windows.Forms.NumericUpDown(); this.tableLayoutPanel1.SuspendLayout(); this.fieldsPanel.SuspendLayout(); this.panel3.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.portBox)).BeginInit(); this.panel1.SuspendLayout(); this.panel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.keepAliveIntervalBox)).BeginInit(); this.tableLayoutPanel2.SuspendLayout(); this.buttonPanel.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout(); this.contextMenu.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.keepAliveIntervalBox)).BeginInit(); this.SuspendLayout(); // // tableLayoutPanel1 @@ -129,7 +129,7 @@ private void InitializeComponent() this.fieldsPanel.ColumnCount = 3; this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 26.10063F)); this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 73.89937F)); - this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 25F)); + this.fieldsPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.fieldsPanel.Controls.Add(this.proxyHostBox, 1, 10); this.fieldsPanel.Controls.Add(this.label12, 0, 10); this.fieldsPanel.Controls.Add(this.panel3, 1, 11); @@ -183,7 +183,7 @@ private void InitializeComponent() this.proxyHostBox.Dock = System.Windows.Forms.DockStyle.Fill; this.proxyHostBox.Location = new System.Drawing.Point(81, 322); this.proxyHostBox.Name = "proxyHostBox"; - this.proxyHostBox.Size = new System.Drawing.Size(217, 20); + this.proxyHostBox.Size = new System.Drawing.Size(216, 20); this.proxyHostBox.TabIndex = 10; // // label12 @@ -204,13 +204,13 @@ private void InitializeComponent() this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; this.panel3.Location = new System.Drawing.Point(81, 351); this.panel3.Name = "panel3"; - this.panel3.Size = new System.Drawing.Size(217, 23); + this.panel3.Size = new System.Drawing.Size(216, 23); this.panel3.TabIndex = 11; // // proxyPassBox // this.proxyPassBox.Dock = System.Windows.Forms.DockStyle.Right; - this.proxyPassBox.Location = new System.Drawing.Point(112, 0); + this.proxyPassBox.Location = new System.Drawing.Point(111, 0); this.proxyPassBox.Name = "proxyPassBox"; this.proxyPassBox.Size = new System.Drawing.Size(105, 20); this.proxyPassBox.TabIndex = 1; @@ -239,7 +239,7 @@ private void InitializeComponent() // this.nameBox.Location = new System.Drawing.Point(81, 3); this.nameBox.Name = "nameBox"; - this.nameBox.Size = new System.Drawing.Size(217, 20); + this.nameBox.Size = new System.Drawing.Size(216, 20); this.nameBox.TabIndex = 0; this.nameBox.Leave += new System.EventHandler(this.box_Leave); // @@ -258,7 +258,7 @@ private void InitializeComponent() // this.hostBox.Location = new System.Drawing.Point(81, 32); this.hostBox.Name = "hostBox"; - this.hostBox.Size = new System.Drawing.Size(217, 20); + this.hostBox.Size = new System.Drawing.Size(216, 20); this.hostBox.TabIndex = 1; this.hostBox.Leave += new System.EventHandler(this.box_Leave); // @@ -306,7 +306,7 @@ private void InitializeComponent() // this.userBox.Location = new System.Drawing.Point(81, 90); this.userBox.Name = "userBox"; - this.userBox.Size = new System.Drawing.Size(217, 20); + this.userBox.Size = new System.Drawing.Size(216, 20); this.userBox.TabIndex = 3; this.userBox.Leave += new System.EventHandler(this.box_Leave); // @@ -320,7 +320,7 @@ private void InitializeComponent() "Pageant"}); this.authCombo.Location = new System.Drawing.Point(81, 119); this.authCombo.Name = "authCombo"; - this.authCombo.Size = new System.Drawing.Size(217, 21); + this.authCombo.Size = new System.Drawing.Size(216, 21); this.authCombo.TabIndex = 4; this.authCombo.SelectedIndexChanged += new System.EventHandler(this.authBox_SelectedIndexChanged); // @@ -386,7 +386,7 @@ private void InitializeComponent() "/"}); this.directoryBox.Location = new System.Drawing.Point(81, 206); this.directoryBox.Name = "directoryBox"; - this.directoryBox.Size = new System.Drawing.Size(217, 21); + this.directoryBox.Size = new System.Drawing.Size(216, 21); this.directoryBox.TabIndex = 6; // // label7 @@ -408,7 +408,7 @@ private void InitializeComponent() this.panel2.Controls.Add(this.passwordBox); this.panel2.Location = new System.Drawing.Point(81, 148); this.panel2.Name = "panel2"; - this.panel2.Size = new System.Drawing.Size(217, 52); + this.panel2.Size = new System.Drawing.Size(216, 52); this.panel2.TabIndex = 5; // // passphraseBox @@ -474,7 +474,7 @@ private void InitializeComponent() this.mountPointBox.Dock = System.Windows.Forms.DockStyle.Fill; this.mountPointBox.Location = new System.Drawing.Point(81, 264); this.mountPointBox.Name = "mountPointBox"; - this.mountPointBox.Size = new System.Drawing.Size(217, 20); + this.mountPointBox.Size = new System.Drawing.Size(216, 20); this.mountPointBox.TabIndex = 8; // // label10 @@ -499,9 +499,37 @@ private void InitializeComponent() "SOCKS5"}); this.proxyType.Location = new System.Drawing.Point(81, 293); this.proxyType.Name = "proxyType"; - this.proxyType.Size = new System.Drawing.Size(217, 21); + this.proxyType.Size = new System.Drawing.Size(216, 21); this.proxyType.TabIndex = 9; // + // labelKeepAlive + // + this.labelKeepAlive.AutoSize = true; + this.labelKeepAlive.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelKeepAlive.Location = new System.Drawing.Point(3, 377); + this.labelKeepAlive.Name = "labelKeepAlive"; + this.labelKeepAlive.Size = new System.Drawing.Size(72, 29); + this.labelKeepAlive.TabIndex = 26; + this.labelKeepAlive.Text = "KeepAlive (s)"; + this.labelKeepAlive.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // keepAliveIntervalBox + // + this.keepAliveIntervalBox.Location = new System.Drawing.Point(81, 380); + this.keepAliveIntervalBox.Maximum = new decimal(new int[] { + 999, + 0, + 0, + 0}); + this.keepAliveIntervalBox.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.keepAliveIntervalBox.Name = "keepAliveIntervalBox"; + this.keepAliveIntervalBox.Size = new System.Drawing.Size(120, 20); + this.keepAliveIntervalBox.TabIndex = 27; + // // driveListView // this.driveListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { @@ -780,39 +808,6 @@ private void InitializeComponent() this.exitMenuItem.Text = "Exit"; this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click); // - // labelKeepAlive - // - this.labelKeepAlive.AutoSize = true; - this.labelKeepAlive.Dock = System.Windows.Forms.DockStyle.Fill; - this.labelKeepAlive.Location = new System.Drawing.Point(3, 377); - this.labelKeepAlive.Name = "labelKeepAlive"; - this.labelKeepAlive.Size = new System.Drawing.Size(72, 29); - this.labelKeepAlive.TabIndex = 26; - this.labelKeepAlive.Text = "KeepAlive (s)"; - this.labelKeepAlive.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // - // keepAliveIntervalBox - // - this.keepAliveIntervalBox.Location = new System.Drawing.Point(81, 380); - this.keepAliveIntervalBox.Maximum = new decimal(new int[] { - 999, - 0, - 0, - 0}); - this.keepAliveIntervalBox.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.keepAliveIntervalBox.Name = "keepAliveIntervalBox"; - this.keepAliveIntervalBox.Size = new System.Drawing.Size(120, 20); - this.keepAliveIntervalBox.TabIndex = 27; - this.keepAliveIntervalBox.Value = new decimal(new int[] { - 1, - 0, - 0, - 0}); - // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -838,12 +833,12 @@ private void InitializeComponent() this.panel1.PerformLayout(); this.panel2.ResumeLayout(false); this.panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.keepAliveIntervalBox)).EndInit(); this.tableLayoutPanel2.ResumeLayout(false); this.buttonPanel.ResumeLayout(false); this.tableLayoutPanel3.ResumeLayout(false); this.tableLayoutPanel3.PerformLayout(); this.contextMenu.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.keepAliveIntervalBox)).EndInit(); this.ResumeLayout(false); } diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index 7f3eadd..691055d 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -115,7 +115,7 @@ protected override void OnLoad(EventArgs e) startupMenuItem.Checked = Utilities.IsAppRegistredForStarup(); - // _drives.Presist("config.xml",true); + // _drives.Persist("config.xml",true); virtualDrive = virtualDrive.Load("vfs.xml"); @@ -270,6 +270,12 @@ private void SetupPanels() protected override void OnFormClosing(FormClosingEventArgs e) { + if (_dirty) + { + _drives.Persist("config.xml"); + //virtualDrive.per + } + if (e.CloseReason == CloseReason.UserClosing) { Visible = false; @@ -278,11 +284,6 @@ protected override void OnFormClosing(FormClosingEventArgs e) else { Debug.WriteLine("FormCOveride"); - if (_dirty) - { - _drives.Presist("config.xml"); - //virtualDrive.per - } notifyIcon.Visible = false; } base.OnFormClosing(e); @@ -686,8 +687,7 @@ protected override void OnFormClosed(FormClosedEventArgs e) { SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; - //_drives.Presist("config.xml"); - ; + _drives.Persist("config.xml"); Parallel.ForEach(_drives.Where(d => d.Status != DriveStatus.Unmounted), d => { @@ -761,7 +761,7 @@ private void virtualDriveCombo_SelectedIndexChanged(object sender, EventArgs e) return; virtualDrive.Letter = virtualDriveCombo.Text[0]; - virtualDrive.Presist("vfs.xml"); + virtualDrive.Persist("vfs.xml"); _updateLockvirtualDriveBox = true; ; diff --git a/Sshfs/Sshfs/MainForm.resx b/Sshfs/Sshfs/MainForm.resx index dab3f78..ecccea9 100644 --- a/Sshfs/Sshfs/MainForm.resx +++ b/Sshfs/Sshfs/MainForm.resx @@ -125,172 +125,172 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACe - LAAAAk1TRnQBSQFMAgEBBAEAAZQBAAGUAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg + LAAAAk1TRnQBSQFMAgEBBAEAAZwBAAGcAQABGAEAARgBAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFg AwABMAMAAQEBAAEgBgABSP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP0AAQEDAwEEAwUBBwMGAQgDBgEIAwcBCQMHAQkDBwEJ AwcBCgMHAQoDBwEKAwgBCwMaASUDVgGxAkABqAH9AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/AoEBuAH/ AoEBuAH/A18B8wNIAYQHAAEBAwMBBAMFAQcDBgEIAwYBCAMHAQkDBwEJAwcBCQMHAQoDBwEKAwcBCgMI AQsDBwEKAwcBCQMFAQcDAQECIAADDwEUAw8BFAMSARgDHgErAyYBOQMoATwDKAE8AyYBOAMiATEDGAEi - AxABFgMQARUDEQEXA0gBgwIbAaQB/wIbAaQB/wIbAaQB/wIbAaQB/wIbAaQB/wIbAaQB/wIbAaQB/wNG + AxABFgMQARUDEQEXA0gBgwIaAaQB/wIaAaQB/wIaAaQB/wIaAaQB/wIaAaQB/wIaAaQB/wIaAaQB/wNG AX4IAAMPARQDDwEUAxIBGAMeASsDJgE5AygBPAMoATwDJgE4AyIBMQMYASIDEAEWAxABFQMRARcDBwEJ AwIBAwMLAQ8DFgEeAxYBHwMLAQ8DAQECEAADTAGPA1QBrwNWAbEDVgGzA1YBswNWAbMDVgGzA1YBswNW AbQDVQG1A1UBtQNVAbUDWwHIA2oB+QKBAdMB/wKBAdMB/wKBAdMB/wKBAdMB/wKBAdMB/wKBAdMB/wKB AdMB/wKBAdQB/wNkAfEEAANMAY8DVAGvA1YBsQNWAbMDVgGzA1YBswNWAbMDVgGzA1YBtANVAbUDVQG1 A1UBtQNVAbUDVgG0Az0BaAMOARMDAwEEHAABgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wIbAaQB/wIbAcwB/wIbAcwB/wIbAcwB/wIbAcwB/wIbAcwB/wIbAcwB/wIbAcwB/wIb + Af8BgQGEAYIB/wIaAaQB/wIaAcwB/wIaAcwB/wIaAcwB/wIaAcwB/wIaAcwB/wIaAcwB/wIaAcwB/wIa AaQB/wgAAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DGQEjAxQBGwM1 - AVYDYQHaAWoCawH6A1sByAMeASsQAAGoAasBqgH/AeQC5QH/AeEB5AHjAf8B3wLhAf8B3wHhAeAB/wHf + AVYDYQHaAWkCagH6A1sByAMeASsQAAGoAasBqgH/AeQC5QH/AeEB5AHjAf8B3wLhAf8B3wHhAeAB/wHf AuEB/wHfAuEB/wHfAuEB/wHfAuEB/wHhAeIB4QH/Ad4B4QHfAf8B4QHiAeEB/wGYAZoBzwH/AoEBzAH/ - AlkBzAH/AoEB0QH/AoEB0AH/AlkBzAH/AlkBzAH/AoEB1AH/Al0BzQH/AoEB0wH/AoEBuAH/BAABqAGr + AlgBzAH/AoEB0QH/AoEB0AH/AlgBzAH/AlgBzAH/AoEB1AH/AlwBzQH/AoEB0wH/AoEBuAH/BAABqAGr AaoB/wHkAuUB/wHhAeQB4wH/Ad8C4QH/Ad8B4QHgAf8B3wLhAf8B3wLhAf8B3wLhAf8B3wLhAf8B4QHi AeEB/wHeAeEB3wH/AeEB4gHhAf8B4wHlAeQB/wHhAeMB4gH/A38B/gMYASIDBgEIHAABgQGEAYIB/wPZ - Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8CGwGkAf8CGwHM - Cf8CGwHMCf8CGwHMAf8CGwGkAf8IAAGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ + Af8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/AYEBhAGCAf8CGgGkAf8CGgHM + Cf8CGgHMCf8CGgHMAf8CGgGkAf8IAAGBAYQBggH/A9kB/wPZAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZ Af8D2QH/A9kB/wPZAf8BgQGEAYIB/wOBAf8DgQH/A4EB/wGcAp0B/wO2Af8BrAKtAf8DWgHAEAADQAFu A1wB6gG6ArsB/wG2ArcB/wGtAq4B/wGqAasBqgH/AaoCqwH/AaoCqwH/AaoCqwH/A7gB/wGuAq8B/wHI - AskB/wKBAaEB/wKBAcwB/wJZAcwB/wKBAd8B/wLBAfMB/wKBAdYB/wKBAd8B/wLNAfUB/wKBAdMB/wKB + AskB/wKBAaEB/wKBAcwB/wJYAcwB/wKBAd8B/wLBAfMB/wKBAdYB/wKBAd8B/wLNAfUB/wKBAdMB/wKB AdMB/wKBAbgB/wQAA0ABbgNcAeoBugK7Af8BtgK3Af8BrQKuAf8BqgGrAaoB/wGqAqsB/wGqAqsB/wGq AqsB/wO4Af8BrgKvAf8ByALJAf8BnQGfAZ4B/wNiAe8DSQGIAyABLgMEAQUcAAGBAYQBggH/A/MB/wPw - Af8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/A8UB/wPoAf8BgQGEAYIB/wIbAaQB/wIbAcwV/wIb - AcwB/wIbAaQB/wgAAYEBhAGCAf8D8wH/A/AB/wPRAf8D7AH/A+sB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ + Af8D0QH/A+wB/wPrAf8D6wH/A+sB/wPIAf8DxQH/A8UB/wPoAf8BgQGEAYIB/wIaAaQB/wIaAcwV/wIa + AcwB/wIaAaQB/wgAAYEBhAGCAf8D8wH/A/AB/wPRAf8D7AH/A+sB/wPrAf8D6wH/A8gB/wPFAf8DxQH/ A+gB/wGBAYQBggH/A5AB/wOzAf8DgQH/A7YB/wPrAf8D1QH/A2oB+RAAAwgBCwM4AV0DfwH+AZYBmgGY Af8BlQGZAZgB/wGSAZUBlAH/AZIBlQGUAf8BkgGVAZQB/wGSAZUBlAH/AZEBlAGTAf8BlwGaAZkB/wGY - AZwBmwH/AoEBlAH/AoEBzAH/AlkBzAH/AmkBzwH/AogB5wH/AtoB+AH/As0B9QH/AoEB3wH/AlkBzAH/ + AZwBmwH/AoEBlAH/AoEBzAH/AlgBzAH/AmgBzwH/AogB5wH/AtoB+AH/As0B9QH/AoEB3wH/AlgBzAH/ AoEB0wH/AoEBuAH/BAADCAELAzgBXQN/Af4BlgGaAZgB/wGVAZkBmAH/AZIBlQGUAf8BkgGVAZQB/wGS AZUBlAH/AZIBlQGUAf8BkQGUAZMB/wGXAZoBmQH/AZgBnAGbAf8DXgHVAz8BbQNMAZIDRwGAAwABARwA A08BlwGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ - AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9AhsBpAH/AhsBzAH/AhsBzA3/AhsBzAH/ - AhsBzAH/AhsBpAH/CAADTwGXAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8DRQF9AhoBpAH/AhoBzAH/AhoBzA3/AhoBzAH/ + AhoBzAH/AhoBpAH/CAADTwGXAYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB AYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNFAX0DgQH/A4EB/wOB Af8BowKkAf8D1QH/A9cB/wNZAb8UAAMBAQIDBAEFAZUBmQGYAf8BywHPAc4B/wHbAd0B3AH/AdgB2gHZ - Af8BywHPAc4B/wHAAcQBwwH/AbcBuwG6Af8DXAHNAYEBhAGCAf8CVAFWAasCgQHMAf8CWQHMAf8CWQHM - Af8CgQHfBf8C2gH4Af8CgQHWAf8CWQHMAf8CgQHTAf8CgQG4Af8IAAMBAQIDBAEFAZUBmQGYAf8BywHP + Af8BywHPAc4B/wHAAcQBwwH/AbcBuwG6Af8DXAHNAYEBhAGCAf8CVAFWAasCgQHMAf8CWAHMAf8CWAHM + Af8CgQHfBf8C2gH4Af8CgQHWAf8CWAHMAf8CgQHTAf8CgQG4Af8IAAMBAQIDBAEFAZUBmQGYAf8BywHP Ac4B/wHbAd0B3AH/AdgB2gHZAf8BywHPAc4B/wHAAcQBwwH/AbcBuwG6Af8DXAHNAYEBhAGCAf8DSgGJ A1sBxANeAc4DGQEjIAABgQGEAYIB/wPWAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPtAf8D7QH/A+0B/wPt - Af8D1AH/AYEBhAGCAf8CGwGkAf8CGwHMFf8CGwHMAf8CGwGkAf8IAAGBAYQBggH/A9YB/wH3AfEB6wH/ + Af8D1AH/AYEBhAGCAf8CGgGkAf8CGgHMFf8CGgHMAf8CGgGkAf8IAAGBAYQBggH/A9YB/wH3AfEB6wH/ AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHx AesB/wPUAf8BgQGEAYIB/wQAAwUBBwMmATgDgQH/A4EB/wOBAf8DGgElEAADHwEsA14B1QGhAaYBpAH/ AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/AaEBpQGjAf8BpAGoAacB/wGlAaoBqAH/AaUBqgGoAf8BpQGp - AacB/wGlAakBpwH/AoEBpgH/AoEBzAH/AlkBzAH/AoEB2wH/As0B9QH/AoEB3wH/AocB5wH/AsEB8wH/ - AmsBzwH/AoEB0wH/AoEBuAH/BAADHwEsA14B1QGhAaYBpAH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/ + AacB/wGlAakBpwH/AoEBpgH/AoEBzAH/AlgBzAH/AoEB2wH/As0B9QH/AoEB3wH/AocB5wH/AsEB8wH/ + AmoBzwH/AoEB0wH/AoEBuAH/BAADHwEsA14B1QGhAaYBpAH/AaEBpQGjAf8BoQGlAaMB/wGhAaUBowH/ AaEBpQGjAf8BpAGoAacB/wGlAaoBqAH/AaUBqgGoAf8BpQGpAacB/wGlAakBpwH/AaABpQGjAf8DWwHD - AwcBCQMHAQoDBwEKAwcBCgMIAQsDBwEKAwcBCQMEAQYDAQECBAABgQGEAYIB/wPzAf8BgQFjAVEB/wGB - AWMBUQH/AYEBYwFRAf8BgQFjAVEB/wGBAWMBUQH/AYEBYwFRAf8BgQFjAVEB/wGBAWMBUQH/AYEBYwFR - Af8D7QH/AYEBhAGCAf8CGwGkAf8CGwHMCf8CGwHMCf8CGwHMAf8CGwGkAf8IAAGBAYQBggH/AfoB9gHy - Af8BpAGBAVcB/wGoAYEBXQH/AagBgQFdAf8BqAGBAV0B/wGoAYEBXQH/AagBgQFdAf8BqAGBAV0B/wGo - AYEBXQH/AaQBgQFXAf8B9wHxAesB/wGBAYQBggH/AwsBDwMMARADDAEQA4EB/wPTAf8DgQH/FAADNQFV + AwcBCQMHAQoDBwEKAwcBCgMIAQsDBwEKAwcBCQMEAQYDAQECBAABgQGEAYIB/wPzAf8BgQFiAVAB/wGB + AWIBUAH/AYEBYgFQAf8BgQFiAVAB/wGBAWIBUAH/AYEBYgFQAf8BgQFiAVAB/wGBAWIBUAH/AYEBYgFQ + Af8D7QH/AYEBhAGCAf8CGgGkAf8CGgHMCf8CGgHMCf8CGgHMAf8CGgGkAf8IAAGBAYQBggH/AfoB9gHy + Af8BpAGBAVYB/wGoAYEBXAH/AagBgQFcAf8BqAGBAVwB/wGoAYEBXAH/AagBgQFcAf8BqAGBAVwB/wGo + AYEBXAH/AaQBgQFWAf8B9wHxAesB/wGBAYQBggH/AwsBDwMMARADDAEQA4EB/wPTAf8DgQH/FAADNQFV Ab8BwwHBAf8B1wHcAdkB/wHiAeYB5AH/AeIB5gHkAf8B4AHkAeIB/wHGAcsByAH/AcUBygHIAf8B3QHi - Ad8B/wHfAeQB4QH/Ad8B4wHhAf8B3wHjAeEB/wGPAZIBxgH/AoEBzAH/AlkBzAH/AoEB1wH/AoEB2wH/ - AlkBzAH/AmkBzwH/AoEB4QH/Am0B0AH/AoEB0wH/AoEBuAH/BAADNQFVAb8BwwHBAf8B1wHcAdkB/wHi + Ad8B/wHfAeQB4QH/Ad8B4wHhAf8B3wHjAeEB/wGPAZIBxgH/AoEBzAH/AlgBzAH/AoEB1wH/AoEB2wH/ + AlgBzAH/AmgBzwH/AoEB4QH/AmwB0AH/AoEB0wH/AoEBuAH/BAADNQFVAb8BwwHBAf8B1wHcAdkB/wHi AeYB5AH/AeIB5gHkAf8B4AHkAeIB/wHGAcsByAH/AcUBygHIAf8B3QHiAd8B/wHfAeQB4QH/Ad8B4wHh Af8B3wHjAeEB/wHWAdoB2AH/AZwBoQGfAf8DVgG0A1YBtANVAbUDVQG1A1UBtQNVAbUDVgGzAzEBTgML - AQ8EAAGBAYQBggH/A+4B/wGBAWMBUQH/AZMBgQFnAf8BkwGBAWcB/wGUAYEBZwH/AZMBgQFnAf8BkwGB - AWcB/wGTAYEBaAH/AZQBgQFnAf8BgQFjAVEB/wPuAf8BgQGEAYIB/wIbAaQB/wIbAcwB/wIbAcwB/wIb - AcwB/wIbAcwB/wIbAcwB/wIbAcwB/wIbAcwB/wIbAaQB/wgAAYEBhAGCAf8B+AHyAewB/wGoAYEBXQH/ - AboBgQFhAf8BugGBAWEB/wG7AYEBYQH/AboBgQFhAf8BugGBAWEB/wG6AYEBYgH/AbsBgQFhAf8BqAGB - AV0B/wH4AfIB7AH/AYEBhAGCAf8DOAFeAzgBXgM2AVgDgQH/AccCyAH/A4EB/wMDAQQDBQEHDAADNQFV + AQ8EAAGBAYQBggH/A+4B/wGBAWIBUAH/AZMBgQFmAf8BkwGBAWYB/wGUAYEBZgH/AZMBgQFmAf8BkwGB + AWYB/wGTAYEBZwH/AZQBgQFmAf8BgQFiAVAB/wPuAf8BgQGEAYIB/wIaAaQB/wIaAcwB/wIaAcwB/wIa + AcwB/wIaAcwB/wIaAcwB/wIaAcwB/wIaAcwB/wIaAaQB/wgAAYEBhAGCAf8B+AHyAewB/wGoAYEBXAH/ + AboBgQFgAf8BugGBAWAB/wG7AYEBYAH/AboBgQFgAf8BugGBAWAB/wG6AYEBYQH/AbsBgQFgAf8BqAGB + AVwB/wH4AfIB7AH/AYEBhAGCAf8DOAFeAzgBXgM2AVgDgQH/AccCyAH/A4EB/wMDAQQDBQEHDAADNQFV AcIBxQHEAf8DUgGjA4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8BnQKBAf8BkwGWAcoB/wKB - AcwB/wJeAc0B/wJZAcwB/wJZAcwB/wJZAcwB/wJZAcwB/wJZAcwB/wJZAcwB/wKBAdMB/wJAAagB/QQA + AcwB/wJdAc0B/wJYAcwB/wJYAcwB/wJYAcwB/wJYAcwB/wJYAcwB/wJYAcwB/wKBAdMB/wJAAagB/QQA AzUBVQHCAcUBxAH/A1IBowOBAf8DgQH/A4EB/wOBAf8DgQH/A4EB/wOBAf8DgQH/AZ0CgQH/AdsB4AHe Af8BngGjAaEB/wHfAuEB/wHgAeIB4QH/Ad8B4gHgAf8B3gHhAd8B/wHiAeMB4gH/AeMB5QHjAf8B4QHj - AeIB/wNXAbgDFQEdBAABgQGEAYIB/wPuAf8BgQFjAVEB/wGrAYoBgQH/AZ0CgQH/AY8BgQFkAf8BkAGB - AWQB/wGQAYEBZAH/AZABgQFkAf8BjwGBAWQB/wGBAWMBUQH/A+4B/wGBAYQBggH/AVsBXQGSAf8CGwGk - Af8CGwGkAf8CGwGkAf8CGwGkAf8CGwGkAf8CGwGkAf8CGwGkAf8DRgF+CAABgQGEAYIB/wH4AfIB7AH/ - AagBgQFdAf8B0AGeAYEB/wHEAYsBgQH/AbYBgQFfAf8BtwGBAV8B/wG3AYEBXwH/AbcBgQFfAf8BtgGB - AV8B/wGoAYEBXQH/AfgB8gHsAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ - AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AwEBAgwAAzUBVQOtAf0DgQH/AaACgQH/AZ8CgQH/AZoCgQH/ + AeIB/wNXAbgDFQEdBAABgQGEAYIB/wPuAf8BgQFiAVAB/wGrAYoBgQH/AZ0CgQH/AY8BgQFjAf8BkAGB + AWMB/wGQAYEBYwH/AZABgQFjAf8BjwGBAWMB/wGBAWIBUAH/A+4B/wGBAYQBggH/AVoBXAGSAf8CGgGk + Af8CGgGkAf8CGgGkAf8CGgGkAf8CGgGkAf8CGgGkAf8CGgGkAf8DRgF+CAABgQGEAYIB/wH4AfIB7AH/ + AagBgQFcAf8B0AGeAYEB/wHEAYsBgQH/AbYBgQFeAf8BtwGBAV4B/wG3AYEBXgH/AbcBgQFeAf8BtgGB + AV4B/wGoAYEBXAH/AfgB8gHsAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/ + AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AwEBAgwAAzUBVQOsAf0DgQH/AaACgQH/AZ8CgQH/AZoCgQH/ AZgCgQH/AZcCgQH/AZgCgQH/AZgCgQH/AZgCgQH/A4EB/wG2AbkB1gH/AoEBuwH/AoEBzAH/AoEBzAH/ - AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/A18B+wNWAbMEAAM1AVUDrQH9A4EB/wGgAoEB/wGf + AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/AoEBzAH/A18B+wNWAbMEAAM1AVUDrAH9A4EB/wGgAoEB/wGf AoEB/wGaAoEB/wGYAoEB/wGXAoEB/wGYAoEB/wGYAoEB/wGYAoEB/wOBAf8B3wHiAeEB/wGeAaIBoAH/ AaoCqwH/AbECsgH/Aa4CrwH/AbICswH/AboCuwH/AZwBngGdAf8DZAHnA0ABbgMWAR8EAAGBAYQBggH/ - A+4B/wGBAWMBUQH/AakBhwGBAf8BqQGHAYEB/wGoAYcBgQH/AZoCgQH/AZMBgQGAAf8BiwGBAWEB/wGM - AYEBYQH/AYEBYwFRAf8D7wH/AYEBhAGCAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGEAYIB/wMB - AQIMAAGBAYQBggH/AfgB8gHsAf8BqAGBAV0B/wHOAZsBgQH/Ac4BmwGBAf8BzgGbAYEB/wHCAYYBgQH/ - AboBgQFoAf8BsgGBAVwB/wG0AYEBXAH/AagBgQFdAf8B+AHzAe0B/wGBAYQBggH/A9kB/wPZAf8D2QH/ + A+4B/wGBAWIBUAH/AakBhwGBAf8BqQGHAYEB/wGoAYcBgQH/AZoCgQH/AZMBgQGAAf8BiwGBAWAB/wGM + AYEBYAH/AYEBYgFQAf8D7wH/AYEBhAGCAf8D2QH/A9kB/wPZAf8D2QH/A9kB/wPZAf8BgQGEAYIB/wMB + AQIMAAGBAYQBggH/AfgB8gHsAf8BqAGBAVwB/wHOAZsBgQH/Ac4BmwGBAf8BzgGbAYEB/wHCAYYBgQH/ + AboBgQFnAf8BsgGBAVsB/wG0AYEBWwH/AagBgQFcAf8B+AHzAe0B/wGBAYQBggH/A9kB/wPZAf8D2QH/ A9kB/wPZAf8D2QH/AYEBhAGCAf8DAQECDAADNQFVA18B+wOBAf8BrAGLAYEB/wGrAYkBgQH/AaUCgQH/ AZkCgQH/AZUCgQH/AZMCgQH/AZMCgQH/AZQCgQH/A4EB/wHaAt4B/wGCAYYBpQH/AoEBkgH/AoEBkQH/ AoEBkQH/AoEBkQH/AoEBkQH/AlQBVgGrA0UBfANXAboDNQFWBAADNQFVA18B+wOBAf8BrAGLAYEB/wGr AYkBgQH/AaUCgQH/AZkCgQH/AZUCgQH/AZMCgQH/AZMCgQH/AZQCgQH/A4EB/wHdAeEB3wH/AZwBoAGf Af8BkgGVAZQB/wGSAZUBlAH/AZABkwGSAf8BmwGeAZ0B/wGUAZcBlgH/A1YBqwM7AWQDUgGhAysBQgQA - AYEBhAGCAf8D8AH/AYEBYwFRAf8BpgGDAYEB/wGlAYQBgQH/AaYBhAGBAf8BpQGEAYEB/wGmAYQBgQH/ - AaYBhAGBAf8BkAGBAWwB/wGBAWMBUQH/A/AB/wGBAYQBggH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/ - AYEBhAGCAf8QAAGBAYQBggH/AfgB9AHuAf8BqAGBAV0B/wHMAZcBgQH/AcsBmAGBAf8BzAGYAYEB/wHL - AZgBgQH/AcwBmAGBAf8BzAGYAYEB/wG3AYEBZgH/AagBgQFdAf8B+AH0Ae4B/wGBAYQBggH/A+sB/wPr - Af8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8QAAM1AVUDbQH4A4EB/wGrAYkBgQH/AakBhwGBAf8BqQGH + AYEBhAGCAf8D8AH/AYEBYgFQAf8BpgGDAYEB/wGlAYQBgQH/AaYBhAGBAf8BpQGEAYEB/wGmAYQBgQH/ + AaYBhAGBAf8BkAGBAWsB/wGBAWIBUAH/A/AB/wGBAYQBggH/A+sB/wPrAf8DyAH/A8UB/wPFAf8D6AH/ + AYEBhAGCAf8QAAGBAYQBggH/AfgB9AHuAf8BqAGBAVwB/wHMAZcBgQH/AcsBmAGBAf8BzAGYAYEB/wHL + AZgBgQH/AcwBmAGBAf8BzAGYAYEB/wG3AYEBZQH/AagBgQFcAf8B+AH0Ae4B/wGBAYQBggH/A+sB/wPr + Af8DyAH/A8UB/wPFAf8D6AH/AYEBhAGCAf8QAAM1AVUDawH4A4EB/wGrAYkBgQH/AakBhwGBAf8BqQGH AYEB/wGoAYUBgQH/AZoCgQH/AZACgQH/AZACgQH/AZACgQH/A4EB/wG9AcEBwAH/AZcBmwGZAf8ByAHM - AcsB/wG+AcIBwQH/AbUBuQG4Af8DVgGzA14B1QNNAZEDXgHZA1ABmwMNAREEAAM1AVUDbQH4A4EB/wGr + AcsB/wG+AcIBwQH/AbUBuQG4Af8DVgGzA14B1QNNAZEDXgHZA1ABmwMNAREEAAM1AVUDawH4A4EB/wGr AYkBgQH/AakBhwGBAf8BqQGHAYEB/wGoAYUBgQH/AZoCgQH/AZACgQH/AZACgQH/AZACgQH/A4EB/wG9 AcEBwAH/AZcBmwGZAf8ByAHMAcsB/wG+AcIBwQH/AbUBuQG4Af8DVgGzA14B1QNNAZEDXgHZA1ABmwMN - AREEAAGBAYQBggH/A/MB/wGBAWMBUQH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaICgQH/ - AaMCgQH/AYEBYwFRAf8D8AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8DRQF9EAABgQGEAYIB/wH6AfYB8gH/AagBgQFdAf8ByQGVAYEB/wHJAZQBgQH/ - AckBlAGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBXQH/AfgB9AHuAf8BgQGE + AREEAAGBAYQBggH/A/MB/wGBAWIBUAH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaICgQH/ + AaMCgQH/AYEBYgFQAf8D8AH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB + AYQBggH/AYEBhAGCAf8DRQF9EAABgQGEAYIB/wH6AfYB8gH/AagBgQFcAf8ByQGVAYEB/wHJAZQBgQH/ + AckBlAGBAf8ByQGVAYEB/wHJAZUBgQH/AckBlQGBAf8ByQGVAYEB/wGoAYEBXAH/AfgB9AHuAf8BgQGE AYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wNFAX0QAAM1 AVUDYgH2A4EB/wGqAYgBgQH/AagBhwGBAf8BpwGGAYEB/wGnAYQBgQH/AaYBggGBAf8BmQKBAf8BjwKB Af8BjAKBAf8DgQH/AdMB1gHUAf8BmQGeAZwB/wGlAaoBqAH/AaUBqgGoAf8BpQGqAacB/wGlAakBpwH/ AaUBqQGnAf8BnAGgAZ4B/wNUAaYMAAM1AVUDYgH2A4EB/wGqAYgBgQH/AagBhwGBAf8BpwGGAYEB/wGn AYQBgQH/AaYBggGBAf8BmQKBAf8BjwKBAf8BjAKBAf8DgQH/AdMB1gHUAf8BmQGeAZwB/wGlAaoBqAH/ AaUBqgGoAf8BpQGqAacB/wGlAakBpwH/AaUBqQGnAf8BnAGgAZ4B/wNUAaYMAAGBAYQBggH/A+0B/wGB - AWMBUQH/AYEBYwFRAf8BgQFjAVEB/wGBAWMBUQH/AYEBYwFRAf8BgQFjAVEB/wGBAWMBUQH/AYEBYwFR - Af8BgQFjAVEB/wPwAf8BgQGEAYIB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A9QB/wGBAYQBggH/EAABgQGE - AYIB/wH3AfEB6wH/AaQBgQFXAf8BqAGBAV0B/wGoAYEBXQH/AagBgQFdAf8BqAGBAV0B/wGoAYEBXQH/ - AagBgQFdAf8BqAGBAV0B/wGkAYEBVwH/AfgB9AHuAf8BgQGEAYIB/wH3AfEB6wH/AfcB8QHrAf8B9wHx + AWIBUAH/AYEBYgFQAf8BgQFiAVAB/wGBAWIBUAH/AYEBYgFQAf8BgQFiAVAB/wGBAWIBUAH/AYEBYgFQ + Af8BgQFiAVAB/wPwAf8BgQGEAYIB/wPtAf8D7QH/A+0B/wPtAf8D7QH/A9QB/wGBAYQBggH/EAABgQGE + AYIB/wH3AfEB6wH/AaQBgQFWAf8BqAGBAVwB/wGoAYEBXAH/AagBgQFcAf8BqAGBAVwB/wGoAYEBXAH/ + AagBgQFcAf8BqAGBAVwB/wGkAYEBVgH/AfgB9AHuAf8BgQGEAYIB/wH3AfEB6wH/AfcB8QHrAf8B9wHx AesB/wH3AfEB6wH/AfcB8QHrAf8D1AH/AYEBhAGCAf8QAAM1AVUDZQH0A4EB/wGoAYYBgQH/AacBhQGB Af8BpgGEAYEB/wGlAYIBgQH/AaQCgQH/AaMCgQH/AZ0CgQH/AZUCgQH/A4EB/wHTAdYB1QH/AZkBngGc Af8BxQHKAcgB/wHeAeIB4AH/Ad8B4wHhAf8B3wHjAeEB/wHcAeAB3gH/AdYB2QHYAf8BgQGEAYIB/wwA AzUBVQNlAfQDgQH/AagBhgGBAf8BpwGFAYEB/wGmAYQBgQH/AaUBggGBAf8BpAKBAf8BowKBAf8BnQKB Af8BlQKBAf8DgQH/AdMB1gHVAf8BmQGeAZwB/wHFAcoByAH/Ad4B4gHgAf8B3wHjAeEB/wHfAeMB4QH/ AdwB4AHeAf8B1gHZAdgB/wGBAYQBggH/DAABgQGEAYIB/wHHAsgB/wPtAf8D7QH/A+0B/wPtAf8D7QH/ - A+0B/wPtAf8D7QH/A+0B/wHZAtoB/wGBAYQBggH/AYEBYwFRAf8BgQFjAVEB/wGBAWMBUQH/AYEBYwFR - Af8BgQFjAVEB/wPtAf8BgQGEAYIB/xAAAYEBhAGCAf8BxwLIAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHr + A+0B/wPtAf8D7QH/A+0B/wHZAtoB/wGBAYQBggH/AYEBYgFQAf8BgQFiAVAB/wGBAWIBUAH/AYEBYgFQ + Af8BgQFiAVAB/wPtAf8BgQGEAYIB/xAAAYEBhAGCAf8BxwLIAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHr Af8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B9wHxAesB/wH3AfEB6wH/AfcB8QHrAf8B2QLaAf8BgQGE - AYIB/wGoAYEBXQH/AagBgQFdAf8BqAGBAV0B/wGoAYEBXQH/AaQBgQFXAf8B9wHxAesB/wGBAYQBggH/ + AYIB/wGoAYEBXAH/AagBgQFcAf8BqAGBAVwB/wGoAYEBXAH/AaQBgQFWAf8B9wHxAesB/wGBAYQBggH/ EAADNQFVA10B8AOBAf8BpwGFAYEB/wGlAYQBgQH/AaQBgwGBAf8BpAKBAf8BogKBAf8BoQKBAf8BoAKB Af8BnwKBAf8DgQH/AcABwwHBAf8BlgGZAZcB/wOBAf8DgQH/A4EB/wOBAf8DWQHCAdwB3wHeAf8BgQGE AYIB/wwAAzUBVQNdAfADgQH/AacBhQGBAf8BpQGEAYEB/wGkAYMBgQH/AaQCgQH/AaICgQH/AaECgQH/ AaACgQH/AZ8CgQH/A4EB/wHAAcMBwQH/AZYBmQGXAf8DgQH/A4EB/wOBAf8DgQH/A1kBwgHcAd8B3gH/ AYEBhAGCAf8MAANUAawBgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGC - Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AZMBgQFnAf8BkwGB - AWcB/wGTAYEBaAH/AZQBgQFnAf8BgQFjAVEB/wPuAf8BgQGEAYIB/xAAA1QBrAGBAYQBggH/AYEBhAGC + Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYcCgQH/AZMBgQFmAf8BkwGB + AWYB/wGTAYEBZwH/AZQBgQFmAf8BgQFiAVAB/wPuAf8BgQGEAYIB/xAAA1QBrAGBAYQBggH/AYEBhAGC Af8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGBAYQBggH/AYEBhAGCAf8BgQGEAYIB/wGB - AYQBggH/AYEBhAGCAf8BhwKBAf8BugGBAWEB/wG6AYEBYQH/AboBgQFiAf8BuwGBAWEB/wGoAYEBXQH/ + AYQBggH/AYEBhAGCAf8BhwKBAf8BugGBAWAB/wG6AYEBYAH/AboBgQFhAf8BuwGBAWAB/wGoAYEBXAH/ AfgB8gHsAf8BgQGEAYIB/xAAAzUBVQNlAfQDVAGsAZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/AZYCgQH/ AZYCgQH/AZYCgQH/AZYCgQH/AZ0CgQH/AdQB1gHUAf8BmwGdAZsB/wGXAoEB/wGYAoEB/wGXAoEB/wGY AoEB/wGbAoEB/wHcAd8B3QH/AYEBhAGCAf8MAAM1AVUDZQH0A1QBrAGWAoEB/wGWAoEB/wGWAoEB/wGW AoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGWAoEB/wGdAoEB/wHUAdYB1AH/AZsBnQGbAf8BlwKBAf8BmAKB - Af8BlwKBAf8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/KAABgQGEAYIB/wPuAf8BgQFjAVEB/wGr - AYoBgQH/AZ0CgQH/AY8BgQFkAf8BkAGBAWQB/wGQAYEBZAH/AZABgQFkAf8BjwGBAWQB/wGBAWMBUQH/ - A+4B/wGBAYQBggH/LAABgQGEAYIB/wH4AfIB7AH/AagBgQFdAf8B0AGeAYEB/wHEAYsBgQH/AbYBgQFf - Af8BtwGBAV8B/wG3AYEBXwH/AbcBgQFfAf8BtgGBAV8B/wGoAYEBXQH/AfgB8gHsAf8BgQGEAYIB/xAA + Af8BlwKBAf8BmAKBAf8BmwKBAf8B3AHfAd0B/wGBAYQBggH/KAABgQGEAYIB/wPuAf8BgQFiAVAB/wGr + AYoBgQH/AZ0CgQH/AY8BgQFjAf8BkAGBAWMB/wGQAYEBYwH/AZABgQFjAf8BjwGBAWMB/wGBAWIBUAH/ + A+4B/wGBAYQBggH/LAABgQGEAYIB/wH4AfIB7AH/AagBgQFcAf8B0AGeAYEB/wHEAYsBgQH/AbYBgQFe + Af8BtwGBAV4B/wG3AYEBXgH/AbcBgQFeAf8BtgGBAV4B/wGoAYEBXAH/AfgB8gHsAf8BgQGEAYIB/xAA AyEBMANeAdkBmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGe AZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGbAZ8BnQH/AYkBhwGBAf8BlAKBAf8BkwKBAf8BlAKB - Af8BlAKBAf8BmwKBAf8DrQH9AYEBhAGCAf8MAAMhATADXgHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ + Af8BlAKBAf8BmwKBAf8DrAH9AYEBhAGCAf8MAAMhATADXgHZAZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/ AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmgGeAZ0B/wGaAZ4BnQH/AZoBngGdAf8BmwGf - AZ0B/wGJAYcBgQH/AZQCgQH/AZMCgQH/AZQCgQH/AZQCgQH/AZsCgQH/A60B/QGBAYQBggH/KAABgQGE - AYIB/wPuAf8BgQFjAVEB/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFh - Af8BjAGBAWEB/wGBAWMBUQH/A+8B/wGBAYQBggH/LAABgQGEAYIB/wH4AfIB7AH/AagBgQFdAf8BzgGb - AYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGGAYEB/wG6AYEBaAH/AbIBgQFcAf8BtAGBAVwB/wGoAYEBXQH/ + AZ0B/wGJAYcBgQH/AZQCgQH/AZMCgQH/AZQCgQH/AZQCgQH/AZsCgQH/A6wB/QGBAYQBggH/KAABgQGE + AYIB/wPuAf8BgQFiAVAB/wGpAYcBgQH/AakBhwGBAf8BqAGHAYEB/wGaAoEB/wGTAYEBgAH/AYsBgQFg + Af8BjAGBAWAB/wGBAWIBUAH/A+8B/wGBAYQBggH/LAABgQGEAYIB/wH4AfIB7AH/AagBgQFcAf8BzgGb + AYEB/wHOAZsBgQH/Ac4BmwGBAf8BwgGGAYEB/wG6AYEBZwH/AbIBgQFbAf8BtAGBAVsB/wGoAYEBXAH/ AfgB8wHtAf8BgQGEAYIB/ywAA1IBqQNZAfUBiQKBAf8BqgGIAYEB/wGpAYcBgQH/AakBhgGBAf8BqAGF - AYEB/wGTAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wGOAoEB/wNtAfwBgQGEAYIB/ygAA1IBqQNZAfUBiQKB + AYEB/wGTAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wGOAoEB/wNsAfwBgQGEAYIB/ygAA1IBqQNZAfUBiQKB Af8BqgGIAYEB/wGpAYcBgQH/AakBhgGBAf8BqAGFAYEB/wGTAoEB/wGQAoEB/wGQAoEB/wGQAoEB/wGO - AoEB/wNtAfwBgQGEAYIB/ygAAYEBhAGCAf8D8AH/AYEBYwFRAf8BpgGDAYEB/wGlAYQBgQH/AaYBhAGB - Af8BpQGEAYEB/wGmAYQBgQH/AaYBhAGBAf8BkAGBAWwB/wGBAWMBUQH/A/AB/wGBAYQBggH/LAABgQGE - AYIB/wH4AfQB7gH/AagBgQFdAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGBAf8BywGYAYEB/wHMAZgBgQH/ - AcwBmAGBAf8BtwGBAWYB/wGoAYEBXQH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNkAfEBiQKBAf8BqQGH + AoEB/wNsAfwBgQGEAYIB/ygAAYEBhAGCAf8D8AH/AYEBYgFQAf8BpgGDAYEB/wGlAYQBgQH/AaYBhAGB + Af8BpQGEAYEB/wGmAYQBgQH/AaYBhAGBAf8BkAGBAWsB/wGBAWIBUAH/A/AB/wGBAYQBggH/LAABgQGE + AYIB/wH4AfQB7gH/AagBgQFcAf8BzAGXAYEB/wHLAZgBgQH/AcwBmAGBAf8BywGYAYEB/wHMAZgBgQH/ + AcwBmAGBAf8BtwGBAWUB/wGoAYEBXAH/AfgB9AHuAf8BgQGEAYIB/ywAA1IBqQNkAfEBiQKBAf8BqQGH AYEB/wGoAYcBgQH/AacBhgGBAf8BpgGEAYEB/wGmAYIBgQH/AZYCgQH/AY4CgQH/AYsCgQH/AZcCgQH/ A2oB+QGBAYQBggH/KAADUgGpA2QB8QGJAoEB/wGpAYcBgQH/AagBhwGBAf8BpwGGAYEB/wGmAYQBgQH/ AaYBggGBAf8BlgKBAf8BjgKBAf8BiwKBAf8BlwKBAf8DagH5AYEBhAGCAf8oAAGBAYQBggH/A/MB/wGB - AWMBUQH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaICgQH/AaMCgQH/AYEBYwFRAf8D8AH/ - AYEBhAGCAf8sAAGBAYQBggH/AfoB9gHyAf8BqAGBAV0B/wHJAZUBgQH/AckBlAGBAf8ByQGUAYEB/wHJ - AZUBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AagBgQFdAf8B+AH0Ae4B/wGBAYQBggH/LAADUgGp + AWIBUAH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaMCgQH/AaICgQH/AaMCgQH/AYEBYgFQAf8D8AH/ + AYEBhAGCAf8sAAGBAYQBggH/AfoB9gHyAf8BqAGBAVwB/wHJAZUBgQH/AckBlAGBAf8ByQGUAYEB/wHJ + AZUBgQH/AckBlQGBAf8ByQGVAYEB/wHJAZUBgQH/AagBgQFcAf8B+AH0Ae4B/wGBAYQBggH/LAADUgGp A2EB7gGIAoEB/wGnAYYBgQH/AacBhQGBAf8BpgGEAYEB/wGkAYIBgQH/AaQCgQH/AaICgQH/AZsCgQH/ AZICgQH/AZcCgQH/A20B9wGBAYQBggH/KAADUgGpA2EB7gGIAoEB/wGnAYYBgQH/AacBhQGBAf8BpgGE AYEB/wGkAYIBgQH/AaQCgQH/AaICgQH/AZsCgQH/AZICgQH/AZcCgQH/A20B9wGBAYQBggH/KAABgQGE - AYIB/wPtAf8BgQFjAVEB/wGBAWMBUQH/AYEBYwFRAf8BgQFjAVEB/wGBAWMBUQH/AYEBYwFRAf8BgQFj - AVEB/wGBAWMBUQH/AYEBYwFRAf8D8AH/AYEBhAGCAf8sAAGBAYQBggH/AfcB8QHrAf8BpAGBAVcB/wGo - AYEBXQH/AagBgQFdAf8BqAGBAV0B/wGoAYEBXQH/AagBgQFdAf8BqAGBAV0B/wGoAYEBXQH/AaQBgQFX + AYIB/wPtAf8BgQFiAVAB/wGBAWIBUAH/AYEBYgFQAf8BgQFiAVAB/wGBAWIBUAH/AYEBYgFQAf8BgQFi + AVAB/wGBAWIBUAH/AYEBYgFQAf8D8AH/AYEBhAGCAf8sAAGBAYQBggH/AfcB8QHrAf8BpAGBAVYB/wGo + AYEBXAH/AagBgQFcAf8BqAGBAVwB/wGoAYEBXAH/AagBgQFcAf8BqAGBAVwB/wGoAYEBXAH/AaQBgQFW Af8B+AH0Ae4B/wGBAYQBggH/LAADUgGpA2IB6QGIAoEB/wGmAYQBgQH/AaUBhAGBAf8BpAGCAYEB/wGj AoEB/wGiAoEB/wGhAoEB/wGgAoEB/wGeAoEB/wGQAoEB/wNqAfkBgQGEAYIB/ygAA1IBqQNiAekBiAKB Af8BpgGEAYEB/wGlAYQBgQH/AaQBggGBAf8BowKBAf8BogKBAf8BoQKBAf8BoAKBAf8BngKBAf8BkAKB diff --git a/Sshfs/Sshfs/Program.cs b/Sshfs/Sshfs/Program.cs index 6fb645e..1717216 100644 --- a/Sshfs/Sshfs/Program.cs +++ b/Sshfs/Sshfs/Program.cs @@ -21,9 +21,10 @@ private static void Main(params string[] args ) #if DEBUG Debug.AutoFlush = true; - Debug.Listeners.Clear(); + //Debug.Listeners.Clear(); //Debug.Listeners.Add(new DelimitedListTraceListener(String.Format("{0}\\log{1:yyyy-MM-dd-HH-mm-ss}.txt",Environment.CurrentDirectory,DateTime.Now), "debug")); Debug.Listeners.Add(new DelimitedListTraceListener(Environment.CurrentDirectory+"\\last.log", "debug")); + //Debug.Listeners.Add(Console.Out); #endif SftpManagerApplication app = new SftpManagerApplication(); app.Run(args); diff --git a/Sshfs/Sshfs/SftpContext.cs b/Sshfs/Sshfs/SftpContext.cs index 2c40bfd..5567f8a 100644 --- a/Sshfs/Sshfs/SftpContext.cs +++ b/Sshfs/Sshfs/SftpContext.cs @@ -1,93 +1,94 @@ -// Copyright (c) 2012 Dragan Mladjenovic -// 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. -using System; -using System.IO; -using Renci.SshNet.Sftp; - -namespace Sshfs -{ - internal sealed class SftpContext : IDisposable - { - private SftpFileAttributes _attributes; - - private SftpContextStream _stream; - - public bool deleteOnCloseWorkaround = false; - - public SftpContext(SftpFileAttributes attributes) - { - _attributes = attributes; +// Copyright (c) 2012 Dragan Mladjenovic +// 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. +using System; +using System.IO; +using Renci.SshNet; +using Renci.SshNet.Sftp; + +namespace Sshfs +{ + internal sealed class SftpContext : IDisposable + { + private SftpFileAttributes _attributes; + private SftpFileStream _stream; + + public bool deleteOnCloseWorkaround = false; + + public SftpContext(SftpFileAttributes attributes) + { + _attributes = attributes; } public SftpContext(SftpFileAttributes attributes, bool aDeleteOnCloseWorkaround) { _attributes = attributes; this.deleteOnCloseWorkaround = aDeleteOnCloseWorkaround; - } - - public SftpContext(SftpSession session, string path, FileMode mode, FileAccess access, - SftpFileAttributes attributes) - { - _stream = new SftpContextStream(session, path, mode, access, attributes); - } - - public SftpFileAttributes Attributes - { - get { return _attributes ?? _stream.Attributes; } - } - - public Stream Stream - { - get { return _stream; } - } - - #region IDisposable Members - - public void Dispose() - { - _attributes = null; - - if (_stream != null) - { - _stream.Close(); - _stream = null; - } - - - GC.SuppressFinalize(this); - } - - #endregion - - public void Release() - { - _attributes = null; - - if (_stream != null) - { - _stream.Close(); - _stream = null; - } - GC.SuppressFinalize(this); - } - - public override string ToString() - { - return String.Format("[{0:x}]", this.GetHashCode()); - } - } + } + + public SftpContext(SftpClient client, string path, FileMode mode, FileAccess access, + SftpFileAttributes attributes) + { + _stream = client.Open(path, mode, access); + _attributes = attributes; + } + + public SftpFileAttributes Attributes + { + get { return _attributes; } + } + + public SftpFileStream Stream + { + get { return _stream; } + } + + #region IDisposable Members + + public void Dispose() + { + _attributes = null; + + if (_stream != null) + { + _stream.Close(); + _stream = null; + } + + + GC.SuppressFinalize(this); + } + + #endregion + + public void Release() + { + _attributes = null; + + if (_stream != null) + { + _stream.Close(); + _stream = null; + } + GC.SuppressFinalize(this); + } + + public override string ToString() + { + return String.Format("[{0:x}]", this.GetHashCode()); + } + } } \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs deleted file mode 100644 index b2018a2..0000000 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright (c) 2012 Dragan Mladjenovic -// 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. - -using System; -using System.Diagnostics; -using System.IO; -using System.Threading; -using Renci.SshNet.Sftp; - -namespace Sshfs -{ - internal sealed class SftpContextStream : Stream - { - - private const int WRITE_BUFFER_SIZE = 32 * 1024 - 38;// (1024*32 - 38)*4; - private const int READ_BUFFER_SIZE = 128*1024; - private readonly byte[] _writeBuffer; - private byte[] _readBuffer = new byte[0]; - - - private readonly SftpSession _session; - private SftpFileAttributes _attributes; - private byte[] _handle; - - private bool _writeMode; - private int _writeBufferPosition; - private int _readBufferPosition; - private long _position; - - internal SftpContextStream(SftpSession session, string path, FileMode mode, FileAccess access, - SftpFileAttributes attributes) - { - Flags flags = Flags.None; - - switch (access) - { - case FileAccess.Read: - flags = Flags.Read; - break; - case FileAccess.Write: - flags = Flags.Write; - break; - case FileAccess.ReadWrite: - flags = Flags.Read | Flags.Write; - break; - } - - switch (mode) - { - case FileMode.Append: - flags |= Flags.Append; - break; - case FileMode.Create: - if (attributes == null) - { - flags |= Flags.CreateNew; - } - else - { - flags |= Flags.Truncate; - } - break; - case FileMode.CreateNew: - flags |= Flags.CreateNew; - break; - case FileMode.Open: - break; - case FileMode.OpenOrCreate: - flags |= Flags.CreateNewOrOpen; - break; - case FileMode.Truncate: - flags |= Flags.Truncate; - break; - } - - _session = session; - - _handle = _session.RequestOpen(path, flags); - - _attributes = attributes ?? _session.RequestFStat(_handle); - - - if (access.HasFlag(FileAccess.Write)) - { - _writeBuffer = new byte[WRITE_BUFFER_SIZE]; - _writeMode = true; - } - - _position = mode != FileMode.Append ? 0 : _attributes.Size; - } - - - public SftpFileAttributes Attributes - { - get - { - lock (this) - { - if (_writeMode) - { - - - - - //FlushWriteBuffer(); - SetupRead(); - _attributes = _session.RequestFStat(_handle); - - } - } - return _attributes; - } - } - - - public override bool CanRead - { - get { throw new NotImplementedException(); } - } - - public override bool CanSeek - { - get { throw new NotImplementedException(); } - } - - public override bool CanWrite - { - get { throw new NotImplementedException(); } - } - - public override long Length - { - get { throw new NotImplementedException(); } - } - - - public override long Position - { - get { return _position; } - set - { - if (!_writeMode) - { - long newPosn = _position - _readBufferPosition; - if (value >= newPosn && value < - (newPosn + _readBuffer.Length)) - { - _readBufferPosition = (int) (value - newPosn); - } - else - { - _readBufferPosition = 0; - _readBuffer = new byte[0]; - } - } - else - { - // Console.WriteLine("Position:{0}=?{1}",value,_position); - if (_position != value) - { - FlushWriteBuffer(); - } - } - _position = value; - } - } - - - public override void Close() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - - public override void Flush() - { - lock (this) - { - if (_writeMode) - { - FlushWriteBuffer(); - } - else - { - /* if (_bufferPosn < _bufferLen) - { - _position -= _bufferPosn; - }*/ - _readBufferPosition = 0; - _readBuffer = new byte[0]; - } - } - } - - - public override int Read(byte[] buffer, int offset, int count) - { - int readLen = 0; - - - // Lock down the file stream while we do this. - - // Set up for the read operation. - SetupRead(); - - // Read data into the caller's buffer. - while (count > 0) - { - // How much data do we have available in the buffer? - int tempLen = _readBuffer.Length - _readBufferPosition; - if (tempLen <= 0) - { - _readBufferPosition = 0; - - _readBuffer = _session.RequestRead(_handle, (ulong) _position, READ_BUFFER_SIZE); - - - if (_readBuffer.Length > 0) - { - tempLen = _readBuffer.Length; - } - else - { - break; - } - } - - - // Don't read more than the caller wants. - if (tempLen > count) - { - tempLen = count; - } - - // Copy stream data to the caller's buffer. - Debug.WriteLine("Copy:{0},{1},{2},{3},{4}",_readBuffer,_readBufferPosition,buffer,offset,tempLen); - Buffer.BlockCopy(_readBuffer, _readBufferPosition, buffer, offset, tempLen); - - // Advance to the next buffer positions. - readLen += tempLen; - offset += tempLen; - count -= tempLen; - _readBufferPosition += tempLen; - _position += tempLen; - } - - - // Return the number of bytes that were read to the caller. - return readLen; - } - - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override void SetLength(long value) - { - lock (this) - { - // Lock down the file stream while we do this. - - // Setup this object for writing. - SetupWrite(); - - _attributes.Size = value; - - _session.RequestFSetStat(_handle, _attributes); - } - } - - - public override void Write(byte[] buffer, int offset, int count) - { - // Lock down the file stream while we do this. - - // Setup this object for writing. - SetupWrite(); - - // Write data to the file stream. - // while (count > 0) - // { - // Determine how many bytes we can write to the buffer. - int tempLen = WRITE_BUFFER_SIZE - _writeBufferPosition; - - /* if (tempLen <= 0) - { - - _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer); - - _writeBufferPosition = 0; - tempLen = WRITE_BUFFER_SIZE; - }*/ - - - if (tempLen >= count) //enought remaining space in writeBuffer - { - // No: copy the data to the write buffer first. - Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); - _writeBufferPosition += count; - } - else //writeBuffer space insufficient - { - FlushWriteBuffer(); - - - if (count > WRITE_BUFFER_SIZE) //writeBuffer size is still lower - { - //solves problem: max writtable count is WRITE_BUFFER_SIZE - int remainingcount = count; - int suboffset = 0; - while (remainingcount >= WRITE_BUFFER_SIZE)//fire whole blocks - { - int chunkcount = remainingcount <= WRITE_BUFFER_SIZE ? remainingcount : WRITE_BUFFER_SIZE; - Buffer.BlockCopy(buffer, offset+suboffset, _writeBuffer, _writeBufferPosition/*always zero*/, chunkcount); - _session.RequestWrite(_handle, (ulong)(_position+suboffset), _writeBuffer, null, null); - remainingcount -= chunkcount; - suboffset += chunkcount; - } - if (remainingcount > 0)//if something remains, do it standard way: - { - Buffer.BlockCopy(buffer, offset+suboffset, _writeBuffer, _writeBufferPosition/*shoud be 0*/, remainingcount); - _writeBufferPosition += remainingcount; - } - } - else - { - Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); - _writeBufferPosition += count; - } - } - // Advance the buffer and stream positions. - _position += count; - // offset += tempLen; - // count -= tempLen; - // } - - // If the buffer is full, then do a speculative flush now, - // rather than waiting for the next call to this method. - if (_writeBufferPosition == WRITE_BUFFER_SIZE) - { - - _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, null,null); - - - _writeBufferPosition = 0; - } - } - - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - - if (_handle != null) - { - if (_writeMode) - { - FlushWriteBuffer(); - } - - _session.RequestClose(_handle); - - _handle = null; - } - } - - - private void FlushWriteBuffer() - { - // Console.WriteLine("FLUSHHHH the water"); - if (_writeBufferPosition > 0) - { - // Console.WriteLine("Written:{0}",_writeBufferPosition); - var data = new byte[_writeBufferPosition]; - Buffer.BlockCopy(_writeBuffer, 0, data, 0, _writeBufferPosition); - - - - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, null,null); - - - _writeBufferPosition = 0; - } - } - -/* - private void FlushWriteBufferNoPipelining() - { - const int maximumDataSize = 1024 * 32 - 38; - Console.WriteLine("FLUSHHHH the water no pipe"); - if (_writeBufferPosition > 0) - { - Console.WriteLine("Written:{0}", _writeBufferPosition); - - int block = ((_writeBufferPosition - 1) / maximumDataSize) + 1; - for (int i = 0; i < block; i++) - { - var blockBufferSize = Math.Min(_writeBufferPosition - maximumDataSize * i, maximumDataSize); - var blockBuffer = new byte[blockBufferSize]; - - Buffer.BlockCopy(_writeBuffer, i*maximumDataSize, blockBuffer, 0, blockBufferSize); - - using (var wait = new AutoResetEvent(false)) - { - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition+i*maximumDataSize), blockBuffer, wait); - } - } - - _writeBufferPosition = 0; - } - } -*/ - - - private void SetupRead() - { - if (_writeMode) - { - FlushWriteBuffer(); - _writeMode = false; - } - } - - - private void SetupWrite() - { - if (_writeMode) return; - - /* if (_bufferPosn < _bufferLen) - { - _position -= _bufferPosn; - }*/ - _readBufferPosition = 0; - _writeBufferPosition = 0; - _readBuffer = new byte[0]; - _writeMode = true; - } - } -} \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index 72deb78..22aa024 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -1,81 +1,81 @@ -// Copyright (c) 2012 Dragan Mladjenovic -// 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. - - -#region - -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; -using DokanNet; -using Renci.SshNet; -using Sshfs.Properties; -using Renci.SshNet.Pageant; -#endregion - -namespace Sshfs -{ - [Serializable] - public class SftpDrive : IDisposable, ISerializable - { - - private CancellationTokenSource _mountCancel = new CancellationTokenSource(); - private readonly AutoResetEvent _pauseEvent = new AutoResetEvent(false); - private CancellationTokenSource _threadCancel = new CancellationTokenSource(); - private bool _exeptionThrown; - internal SftpFilesystem _filesystem; - - private Exception _lastExeption; - private Thread _mountThread; - - private string _connection; - - public string Name { get; set; } - - public char Letter { get; set; } - - public ConnectionType ConnectionType { get; set; } - - public string PrivateKey { get; set; } - - public string Password { get; set; } - - public string Passphrase { get; set; } - - public string Username { get; set; } - - public string Host { get; set; } - - - public int Port { get; set; } - - - public bool Automount { get; set; } - - - public string Root { get; set; } - - public object Tag { get; set; } - +// Copyright (c) 2012 Dragan Mladjenovic +// 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. + + +#region + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using DokanNet; +using Renci.SshNet; +using Sshfs.Properties; +using Renci.SshNet.Pageant; +#endregion + +namespace Sshfs +{ + [Serializable] + public class SftpDrive : IDisposable, ISerializable + { + + private CancellationTokenSource _mountCancel = new CancellationTokenSource(); + private AutoResetEvent _pauseEvent = new AutoResetEvent(false); + private CancellationTokenSource _threadCancel = new CancellationTokenSource(); + private bool _exeptionThrown; + internal SftpFilesystem _filesystem; + + private Exception _lastExeption; + private Thread _mountThread; + + private string _connection; + + public string Name { get; set; } + + public char Letter { get; set; } + + public ConnectionType ConnectionType { get; set; } + + public string PrivateKey { get; set; } + + public string Password { get; set; } + + public string Passphrase { get; set; } + + public string Username { get; set; } + + public string Host { get; set; } + + + public int Port { get; set; } + + + public bool Automount { get; set; } + + + public string Root { get; set; } + + public object Tag { get; set; } + public DriveStatus Status { get; private set; } public string MountPoint { get; set; } @@ -86,23 +86,23 @@ public class SftpDrive : IDisposable, ISerializable public string ProxyPass { get; set; } public int KeepAliveInterval { get; set; } - - public SftpDrive(){} - - private void OnStatusChanged(EventArgs args) - { - if (StatusChanged != null) - { - StatusChanged(this, args); - } - } - - public event EventHandler StatusChanged; - - - - private void SetupFilesystem() - { + + public SftpDrive(){} + + private void OnStatusChanged(EventArgs args) + { + if (StatusChanged != null) + { + StatusChanged(this, args); + } + } + + public event EventHandler StatusChanged; + + + + private void SetupFilesystem() + { Debug.WriteLine("SetupFilesystem {0},{1},{2},{3}",Host,Port,Username,ConnectionType.ToString()); ProxyTypes pt = ProxyTypes.None; @@ -121,28 +121,31 @@ private void SetupFilesystem() Int32.TryParse(s[1], out ProxyPort); Proxy = s[0]; } - } - + } + if(KeepAliveInterval <= 0) { KeepAliveInterval = 1; - } - - ConnectionInfo info; - switch (ConnectionType) - { + } + + ConnectionInfo info; + switch (ConnectionType) + { case ConnectionType.Pageant: var agent = new PageantProtocol(); - if (pt == ProxyTypes.None) { - info = new AgentConnectionInfo(Host, Port, Username, agent); + if (pt == ProxyTypes.None) + { + info = new AgentConnectionInfo(Host, Port, Username, agent); } - else if (ProxyUser.Length>0) { - info = new AgentConnectionInfo(Host, Port, Username, pt, Proxy, ProxyPort, ProxyUser, ProxyPass, agent); + else if (ProxyUser.Length > 0) + { + info = new AgentConnectionInfo(Host, Port, Username, pt, Proxy, ProxyPort, ProxyUser, ProxyPass, agent); } - else { - info = new AgentConnectionInfo(Host, Port, Username, pt, Proxy, ProxyPort, agent); + else + { + info = new AgentConnectionInfo(Host, Port, Username, pt, Proxy, ProxyPort, agent); } - break; + break; case ConnectionType.PrivateKey: if (pt == ProxyTypes.None) { info = new PrivateKeyConnectionInfo(Host, Port, Username, new PrivateKeyFile(PrivateKey, Passphrase)); @@ -152,8 +155,8 @@ private void SetupFilesystem() } else { info = new PrivateKeyConnectionInfo(Host, Port, Username, pt, Proxy, ProxyPort, new PrivateKeyFile(PrivateKey, Passphrase)); - } - break; + } + break; default: if (pt == ProxyTypes.None) { info = new PasswordConnectionInfo(Host, Port, Username, Password); @@ -163,13 +166,13 @@ private void SetupFilesystem() } else { info = new PasswordConnectionInfo(Host, Port, Username, Password, pt, Proxy, ProxyPort); - } - break; - } - - _connection = Settings.Default.UseNetworkDrive ? String.Format("\\\\{0}\\{1}\\{2}", info.Host, Root, info.Username) : Name; - - _filesystem = new SftpFilesystem(info, Root,_connection,Settings.Default.UseOfflineAttribute,false, (int) Settings.Default.AttributeCacheTimeout, (int) Settings.Default.DirContentCacheTimeout); + } + break; + } + + _connection = Settings.Default.UseNetworkDrive ? String.Format("\\\\{0}\\{1}\\{2}", info.Host, Root, info.Username) : Name; + + _filesystem = new SftpFilesystem(info, Root,_connection,Settings.Default.UseOfflineAttribute,false, (int) Settings.Default.AttributeCacheTimeout, (int) Settings.Default.DirContentCacheTimeout); Debug.WriteLine("Connecting..."); _filesystem.KeepAliveInterval = new TimeSpan(0, 0, KeepAliveInterval); _filesystem.Connect(); @@ -201,8 +204,8 @@ private void stopReconnect() this.reconnectThread = null; } - private void reconnectJob() - { + private void reconnectJob() + { this.Unmount(); while (this.Status != DriveStatus.Mounted) @@ -221,86 +224,88 @@ private void reconnectJob() if (this.Status != DriveStatus.Mounted){ Thread.Sleep(1000); } - } - } - - - - private void SetupMountThread() - { - if (_mountThread == null) - { - Debug.WriteLine("Thread:Created"); - _mountThread = new Thread(MountLoop) {IsBackground = true}; - _mountThread.Start(); - } - } - - private void MountLoop() - { - while (true) - { - Debug.WriteLine("Thread:Pause"); - - _pauseEvent.WaitOne(-1); - if (_threadCancel.IsCancellationRequested) - { - Debug.WriteLine("Thread:Cancel"); - break; - } - - Debug.WriteLine("Thread:Mount"); - - - try + } + } + + + + private void SetupMountThread() + { + _threadCancel = new CancellationTokenSource(); + _pauseEvent = new AutoResetEvent(false); + _mountCancel = new CancellationTokenSource(); + + Debug.WriteLine("Thread:Created"); + _mountThread = new Thread(MountLoop) {IsBackground = true}; + + _mountThread.Start(); + } + + private void MountLoop() + { + while (true) + { + Debug.WriteLine("Thread:Pause"); + + _pauseEvent.WaitOne(-1); + if (_threadCancel.IsCancellationRequested) + { + Debug.WriteLine("Thread:Cancel"); + break; + } + + Debug.WriteLine("Thread:Mount"); + + + try { int threadCount = 8; -#if DEBUG - threadCount=1; +#if DEBUG + threadCount=1; #endif _filesystem.Mount(String.Format("{0}:\\", Letter), - Settings.Default.UseNetworkDrive?DokanOptions.NetworkDrive|DokanOptions.KeepAlive: DokanOptions.RemovableDrive|DokanOptions.KeepAlive, threadCount); - } - catch (Exception e) - { - - _lastExeption = e; - _exeptionThrown = true; - _mountCancel.Cancel(); - } - Status = DriveStatus.Unmounted; - if (!_exeptionThrown) - { - - OnStatusChanged(EventArgs.Empty); - } - - } - } - - [MethodImpl(MethodImplOptions.Synchronized)] - public void Mount() - { - //Debug.WriteLine("Mount"); - - - if (Directory.GetLogicalDrives().Any(drive=>drive[0]==Letter)) - { - throw new Exception("Drive with the same letter exists"); - } - - - Status = DriveStatus.Mounting; - - try - { - SetupFilesystem(); - } - catch - { - - Status = DriveStatus.Unmounted; - throw; + Settings.Default.UseNetworkDrive?DokanOptions.NetworkDrive|DokanOptions.KeepAlive: DokanOptions.RemovableDrive|DokanOptions.KeepAlive, threadCount); + } + catch (Exception e) + { + + _lastExeption = e; + _exeptionThrown = true; + _mountCancel.Cancel(); + } + Status = DriveStatus.Unmounted; + if (!_exeptionThrown) + { + + OnStatusChanged(EventArgs.Empty); + } + + } + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Mount() + { + Debug.WriteLine("Mount"); + + + if (Directory.GetLogicalDrives().Any(drive=>drive[0]==Letter)) + { + throw new Exception("Drive with the same letter exists"); + } + + + Status = DriveStatus.Mounting; + + try + { + SetupFilesystem(); + } + catch + { + + Status = DriveStatus.Unmounted; + throw; } if (Letter != ' ') @@ -313,7 +318,7 @@ public void Mount() Directory.GetLogicalDrives().All( drive => drive[0] != Letter)) { - Thread.Sleep(200); + Thread.Sleep(200); } }, _mountCancel.Token); @@ -330,103 +335,103 @@ public void Mount() } if (Settings.Default.UseNetworkDrive) Utilities.SetNetworkDriveName(_connection, Name); - } - Status= DriveStatus.Mounted; - OnStatusChanged(EventArgs.Empty); - - - - } - - [MethodImpl(MethodImplOptions.Synchronized)] - public void Unmount() + } + Status= DriveStatus.Mounted; + OnStatusChanged(EventArgs.Empty); + + + + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void Unmount() { if (this.reconnectThread != Thread.CurrentThread) { this.stopReconnect(); - } - - Debug.WriteLine("Unmount"); - Status = DriveStatus.Unmounting; - try - { - // Dokan.Unmount(Letter); - Dokan.RemoveMountPoint(String.Format("{0}:\\", Letter)); - if (_filesystem != null) - { - - _filesystem.Dispose(); - - - - } - } - catch - { - //Status = DriveStatus.Unmounted; - // OnStatusChanged(EventArgs.Empty); - } - finally - { + } + + if (_threadCancel != null) _threadCancel.Cancel(); + if (_pauseEvent != null) _pauseEvent.Set(); + + Debug.WriteLine("Unmount"); + Status = DriveStatus.Unmounting; + try + { + Dokan.RemoveMountPoint(String.Format("{0}:\\", Letter)); + if (_filesystem != null) + { + _filesystem.Dispose(); + } + } + catch + { + //Status = DriveStatus.Unmounted; + // OnStatusChanged(EventArgs.Empty); + } + finally + { _filesystem = null; Status = DriveStatus.Unmounted; - OnStatusChanged(EventArgs.Empty); - } - - } - - public override string ToString() - { - return String.Format("{0}[{1}:]", Name, Letter); - } - #region Implementation of IDisposable - - public void Dispose() - { - Debug.WriteLine("Dispose"); - - - if (_threadCancel != null) _threadCancel.Cancel(); - if (_pauseEvent != null) _pauseEvent.Set(); - try - { - Dokan.RemoveMountPoint(String.Format("{0}:\\", Letter)); - if (_filesystem != null) - { - _filesystem.Dispose(); - - - _filesystem = null; - } - } - catch - { - Status = DriveStatus.Unmounted; - } - finally - { - _filesystem = null; - } - - - if (_mountCancel != null) {_mountCancel.Dispose();} - if (_threadCancel != null) {_threadCancel.Dispose();} - if (_pauseEvent != null) {_pauseEvent.Dispose();} - } - - #endregion - - #region Implementation of ISerializable - - public SftpDrive(SerializationInfo info, - StreamingContext context) - { - Name = info.GetString("name"); - Host = info.GetString("host"); - Port = info.GetInt32("port"); - Letter = info.GetChar("drive"); - Root = info.GetString("path"); - Automount = info.GetBoolean("mount"); + OnStatusChanged(EventArgs.Empty); + } + + } + + public override string ToString() + { + return String.Format("{0}[{1}:]", Name, Letter); + } + #region Implementation of IDisposable + + public void Dispose() + { + Debug.WriteLine("Dispose"); + + + if (_threadCancel != null) _threadCancel.Cancel(); + if (_pauseEvent != null) _pauseEvent.Set(); + + try + { + Dokan.RemoveMountPoint(String.Format("{0}:\\", Letter)); + if (_filesystem != null) + { + _filesystem.Dispose(); + + + _filesystem = null; + } + } + catch + { + if(Status != DriveStatus.Unmounted) + Status = DriveStatus.Unmounted; + } + finally + { + _filesystem = null; + } + + + if (_mountCancel != null) {_mountCancel.Dispose();} + if (_threadCancel != null) {_threadCancel.Dispose();} + if (_pauseEvent != null) {_pauseEvent.Dispose();} + } + + #endregion + + #region Implementation of ISerializable + + public SftpDrive(SerializationInfo info, + StreamingContext context) + { + Name = info.GetString("name"); + Host = info.GetString("host"); + Port = info.GetInt32("port"); + Letter = info.GetChar("drive"); + Root = info.GetString("path"); + Automount = info.GetBoolean("mount"); Username = info.GetString("user"); try { ProxyType = info.GetInt32("proxyType"); @@ -434,16 +439,24 @@ public SftpDrive(SerializationInfo info, ProxyUser = info.GetString("proxyUser"); ProxyPass = info.GetString("proxyPass"); } - catch { } - ConnectionType = (ConnectionType) info.GetByte("c"); - if (ConnectionType == ConnectionType.Password) - { - Password = Utilities.UnprotectString(info.GetString("p")); - } - else - { - Passphrase = Utilities.UnprotectString(info.GetString("p")); - PrivateKey = info.GetString("k"); + catch { } + try + { + KeepAliveInterval = info.GetInt16("keepAliveInterval"); + } + catch + { + KeepAliveInterval = 1; + } + ConnectionType = (ConnectionType) info.GetByte("c"); + if (ConnectionType == ConnectionType.Password) + { + Password = Utilities.UnprotectString(info.GetString("p")); + } + else + { + Passphrase = Utilities.UnprotectString(info.GetString("p")); + PrivateKey = info.GetString("k"); } try { @@ -452,38 +465,39 @@ public SftpDrive(SerializationInfo info, catch { MountPoint = Name;//default is name after version update - } - } - - - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("name", Name); - info.AddValue("host", Host); - info.AddValue("port", Port); - info.AddValue("drive", Letter); - info.AddValue("path",Root); - info.AddValue("mount", Automount); - info.AddValue("user", Username); + } + } + + + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("name", Name); + info.AddValue("host", Host); + info.AddValue("port", Port); + info.AddValue("drive", Letter); + info.AddValue("path",Root); + info.AddValue("mount", Automount); + info.AddValue("user", Username); info.AddValue("c", (byte)ConnectionType); info.AddValue("mountpoint", MountPoint); info.AddValue("proxyType", ProxyType); info.AddValue("proxyHost", ProxyHost); info.AddValue("proxyUser", ProxyUser); - info.AddValue("proxyPass", ProxyPass); - if (ConnectionType == ConnectionType.Password) - { - info.AddValue("p", Utilities.ProtectString(Password)); - } - else - { - info.AddValue("p", Utilities.ProtectString(Passphrase)); - info.AddValue("k", PrivateKey); - } - } - + info.AddValue("proxyPass", ProxyPass); + info.AddValue("keepAliveInterval", KeepAliveInterval); + if (ConnectionType == ConnectionType.Password) + { + info.AddValue("p", Utilities.ProtectString(Password)); + } + else + { + info.AddValue("p", Utilities.ProtectString(Passphrase)); + info.AddValue("k", PrivateKey); + } + } + #endregion - } + } } \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 57b0806..49b9612 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -25,6 +25,7 @@ using System.Security.AccessControl; using System.Security.Principal; using System.Text; +using System.Threading; using DokanNet; using Renci.SshNet; using Renci.SshNet.Common; @@ -35,29 +36,18 @@ namespace Sshfs { - internal sealed class SftpFilesystem : BaseClient, IDokanOperations + internal sealed class SftpFilesystem : SftpClient, IDokanOperations { #region Constants - // ReSharper disable InconsistentNaming - // private static readonly string[] _filter = { - // "desktop.ini", "Desktop.ini", "autorun.inf", - // "AutoRun.inf", //"Thumbs.db", - // }; - - // private static readonly Regex _dfregex = new Regex(@"^[a-z0-9/]+\s+(?[0-9]+)K\s+(?[0-9]+)K" - // , RegexOptions.Compiled); - - // ReSharper restore InconsistentNaming - #endregion #region Fields private readonly MemoryCache _cache = MemoryCache.Default; - private SftpSession _sftpSession; + private SshClient _sshClient; private readonly TimeSpan _operationTimeout = TimeSpan.FromSeconds(30);//new TimeSpan(0, 0, 0, 0, -1); private string _rootpath; @@ -71,9 +61,6 @@ internal sealed class SftpFilesystem : BaseClient, IDokanOperations private readonly int _attributeCacheTimeout; private readonly int _directoryCacheTimeout; - private bool _supportsPosixRename; - private bool _supportsStatVfs; - private readonly string _volumeLabel; #endregion @@ -83,7 +70,7 @@ internal sealed class SftpFilesystem : BaseClient, IDokanOperations public SftpFilesystem(ConnectionInfo connectionInfo, string rootpath, string label = null, bool useOfflineAttribute = false, bool debugMode = false, int attributeCacheTimeout = 5, int directoryCacheTimeout = 60) - : base(connectionInfo, true) + : base(connectionInfo) { _rootpath = rootpath; _directoryCacheTimeout = directoryCacheTimeout; @@ -101,10 +88,10 @@ protected override void OnConnected() { base.OnConnected(); - _sftpSession = new SftpSession(Session, _operationTimeout, Encoding.UTF8); + _sshClient = new SshClient(ConnectionInfo); this.Log("Connected %s", _volumeLabel); - _sftpSession.Connect(); + _sshClient.Connect(); _userId = GetUserId(); @@ -114,14 +101,8 @@ protected override void OnConnected() if (String.IsNullOrWhiteSpace(_rootpath)) { - _rootpath = _sftpSession.RequestRealPath(".").First().Key; + _rootpath = this.WorkingDirectory; } - - _supportsPosixRename = - _sftpSession._supportedExtensions.Contains(new KeyValuePair("posix-rename@openssh.com", "1")); - _supportsStatVfs = - _sftpSession._supportedExtensions.Contains(new KeyValuePair("statvfs@openssh.com", "2")); - } protected override void OnDisconnected() @@ -132,10 +113,10 @@ protected override void OnDisconnected() protected override void Dispose(bool disposing) { - if (_sftpSession != null) + if (_sshClient != null) { - _sftpSession.Dispose(); - _sftpSession = null; + _sshClient.Dispose(); + _sshClient = null; } base.Dispose(disposing); } @@ -240,7 +221,6 @@ private void CacheResetParent(string path) int index = path.LastIndexOf('/'); if (index > 0) { - //_cache.Remove(index != 0 ? fileName.Substring(0, index) : "\\"); this.CacheReset(path.Substring(0, index)); } else @@ -256,13 +236,12 @@ private void CacheResetParent(string path) private string GetUnixPath(string path) { - // return String.Concat(_rootpath, path.Replace('\\', '/')); return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/').Replace("//","/")); } private IEnumerable GetUserGroupsIds() { - using (var cmd = new SshCommand(Session, "id -G ")) + using (var cmd = _sshClient.CreateCommand("id -G ", Encoding.UTF8)) { cmd.Execute(); return cmd.Result.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries).Select(Int32.Parse); @@ -271,8 +250,8 @@ private IEnumerable GetUserGroupsIds() private int GetUserId() { - using (var cmd = new SshCommand(Session, "id -u ")) - // Thease commands seems to be POSIX so the only problem would be Windows enviroment + using (var cmd = _sshClient.CreateCommand("id -u ", Encoding.UTF8)) + // Thease commands seems to be POSIX so the only problem would be Windows enviroment { cmd.Execute(); return cmd.ExitStatus == 0 ? Int32.Parse(cmd.Result) : -1; @@ -307,17 +286,6 @@ private bool GroupRightsSameAsOwner(SftpFileAttributes attributes) && (attributes.GroupCanExecute == attributes.OwnerCanExecute); } - private SftpFileAttributes GetAttributes(string path) - { - var sftpLStatAttributes = _sftpSession.RequestLStat(path, true); - if (sftpLStatAttributes == null || !sftpLStatAttributes.IsSymbolicLink) - { - return sftpLStatAttributes; - } - var sftpStatAttributes = _sftpSession.RequestStat(path, true); - return sftpStatAttributes ?? sftpLStatAttributes; - } - #endregion #region DokanOperations @@ -336,25 +304,28 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS string path = GetUnixPath(fileName); - // var sftpFileAttributes = GetAttributes(path); - //var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; var sftpFileAttributes = this.CacheGetAttr(path); if (sftpFileAttributes == null) { //Log("cache miss"); - - sftpFileAttributes = GetAttributes(path); + try + { + sftpFileAttributes = GetAttributes(path); + } + catch(SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } + if (sftpFileAttributes != null) - //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); else { LogFSActionOther("OpenFile", fileName, (SftpContext)info.Context, "get attributes failed"); } } - /*Log("Open| Name:{0},\n Mode:{1},\n Share{2},\n Disp:{3},\n Flags{4},\n Attr:{5},\nPagingIO:{6} NoCache:{7} SynIO:{8}\n", fileName, access, - share, mode, options, attributes, info.PagingIo, info.NoCache, info.SynchronousIo);*/ switch (mode) { @@ -364,7 +335,6 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (((uint)access & 0xe0000027) == 0 || sftpFileAttributes.IsDirectory) //check if only wants to read attributes,security info or open directory { - //Log("JustInfo:{0},{1}", fileName, sftpFileAttributes.IsDirectory); info.IsDirectory = sftpFileAttributes.IsDirectory; if (options.HasFlag(FileOptions.DeleteOnClose)) @@ -401,10 +371,9 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS CacheResetParent(path); break; } - //Log("NotJustInfo:{0}-{1}", info.Context, mode); try { - info.Context = new SftpContext(_sftpSession, path, mode, + info.Context = new SftpContext(this, path, mode, ((ulong) access & 0x40010006) == 0 ? System.IO.FileAccess.Read : System.IO.FileAccess.ReadWrite, sftpFileAttributes); @@ -412,16 +381,21 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS catch (SshException ex) // Don't have access rights or try to read broken symlink { var ownerpath = path.Substring(0, path.LastIndexOf('/')); - //var sftpPathAttributes = _cache.Get(ownerpath) as SftpFileAttributes; var sftpPathAttributes = CacheGetAttr(ownerpath); if (sftpPathAttributes == null) { //Log("cache miss"); - - sftpPathAttributes = GetAttributes(ownerpath); + try + { + sftpFileAttributes = GetAttributes(ownerpath); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } if (sftpPathAttributes != null) - //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); else { @@ -440,24 +414,28 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { - //Log("OpenDir:{0}", fileName); LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context,""); string path = GetUnixPath(fileName); - // var sftpFileAttributes = GetAttributes(GetUnixPath(fileName)); - //var sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; var sftpFileAttributes = CacheGetAttr(path); if (sftpFileAttributes == null) { //Log("cache miss"); - - sftpFileAttributes = GetAttributes(path); + + try + { + sftpFileAttributes = GetAttributes(path); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } if (sftpFileAttributes != null) - //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } @@ -466,7 +444,6 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) if (sftpFileAttributes != null && sftpFileAttributes.IsDirectory) { - //??? if (!UserCanExecute(sftpFileAttributes) || !UserCanRead(sftpFileAttributes)) { return DokanError.ErrorAccessDenied; @@ -475,12 +452,10 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) info.IsDirectory = true; info.Context = new SftpContext(sftpFileAttributes); - - //var dircahe = _cache.Get(fileName) as Tuple>; + var dircahe = CacheGetDir(path); if (dircahe != null && dircahe.Item1 != sftpFileAttributes.LastWriteTime) { - //_cache.Remove(fileName); CacheReset(path); } LogFSActionSuccess("OpenDir", fileName, (SftpContext)info.Context,""); @@ -492,13 +467,12 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) { - //Log("CreateDir:{0}", fileName); LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context, ""); string path = GetUnixPath(fileName); try { - _sftpSession.RequestMkDir(path); + CreateDirectory(path); CacheResetParent(path); } catch (SftpPermissionDeniedException) @@ -517,7 +491,6 @@ DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { - //Log("Cleanup:{0},Delete:{1}", info.Context,info.DeleteOnClose); LogFSActionInit("Cleanup", fileName, (SftpContext)info.Context, ""); bool deleteOnCloseWorkAround = false; @@ -538,16 +511,17 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { try { - _sftpSession.RequestRmDir(path); + DeleteDirectory(path); } catch (SftpPathNotFoundException) //in case we are dealing with simbolic link { - _sftpSession.RequestRemove(path); + //This may cause an error + DeleteFile(path); } } else { - _sftpSession.RequestRemove(path); + DeleteFile(path); } CacheReset(path); CacheResetParent(path); @@ -559,7 +533,6 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) { - //Log("Close:{0}", info.Context); LogFSActionInit("CloseFile", fileName, (SftpContext)info.Context, ""); if (info.Context != null) @@ -573,7 +546,7 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) } - /* cache reset for dir close is not good idea, will read it verz soon probablz again, */ + /* cache reset for dir close is not good idea, will read it very soon again */ if (!info.IsDirectory) { CacheReset(GetUnixPath(fileName)); @@ -587,23 +560,27 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, DokanFileInfo info) { - //Log("ReadFile:{0}:{1}|lenght:[{2}]|offset:[{3}]", fileName,info.Context , buffer.Length, offset); LogFSActionInit("ReadFile", fileName, (SftpContext)info.Context, "BuffLen:{0} Offset:{1}", buffer.Length, offset); if (info.Context == null) { //called when file is read as memory memory mapeded file usualy notepad and stuff - var handle = _sftpSession.RequestOpen(GetUnixPath(fileName), Flags.Read); - var data = _sftpSession.RequestRead(handle, (ulong) offset, (uint) buffer.Length); - _sftpSession.RequestClose(handle); - Buffer.BlockCopy(data, 0, buffer, 0, data.Length); - bytesRead = data.Length; + SftpFileStream handle = Open(GetUnixPath(fileName), FileMode.Open); + if (offset == 0) + { + handle.Seek(offset, SeekOrigin.Begin); + } + else + { + handle.Seek(offset, SeekOrigin.Current); + } + bytesRead = handle.Read(buffer, 0, buffer.Length); + handle.Close(); LogFSActionOther("ReadFile", fileName, (SftpContext)info.Context, "NOCONTEXT BuffLen:{0} Offset:{1} Read:{2}", buffer.Length, offset,bytesRead); } else { - // var watch = Stopwatch.StartNew(); - var stream = (info.Context as SftpContext).Stream; + SftpFileStream stream = (info.Context as SftpContext).Stream; lock (stream) { stream.Position = offset; @@ -611,10 +588,7 @@ DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int byt LogFSActionOther("ReadFile", fileName, (SftpContext)info.Context, "BuffLen:{0} Offset:{1} Read:{2}", buffer.Length, offset, bytesRead); } - // watch.Stop(); - // Log("{0}",watch.ElapsedMilliseconds); } - //Log("END READ:{0},{1}",offset,info.Context); LogFSActionSuccess("ReadFile", fileName, (SftpContext)info.Context, ""); return DokanError.ErrorSuccess; } @@ -622,20 +596,14 @@ DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int byt DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, DokanFileInfo info) { - - - //Log("WriteFile:{0}:{1}|lenght:[{2}]|offset:[{3}]", fileName,info.Context, buffer.Length, offset); LogFSActionInit("WriteFile", fileName, (SftpContext)info.Context, "Ofs:{0} Len:{1}", offset, buffer.Length); if (info.Context == null) // who would guess { - var handle = _sftpSession.RequestOpen(GetUnixPath(fileName), Flags.Write); - // using (var wait = new AutoResetEvent(false)) - { - _sftpSession.RequestWrite(handle, (ulong) offset, buffer, null,null/*, wait*/); - } - _sftpSession.RequestClose(handle); + SftpFileStream handle = Open(GetUnixPath(fileName), FileMode.Create); + handle.Write(buffer, 0, buffer.Length); + handle.Close(); bytesWritten = buffer.Length; LogFSActionOther("WriteFile", fileName, (SftpContext)info.Context, "NOCONTEXT Ofs:{1} Len:{0} Written:{2}", buffer.Length, offset, bytesWritten); } @@ -646,7 +614,7 @@ DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int by Log("Data: {0}", Encoding.ASCII.GetString(buffer)); } - var stream = (info.Context as SftpContext).Stream; + SftpFileStream stream = (info.Context as SftpContext).Stream; lock (stream) { stream.Position = offset; @@ -657,7 +625,6 @@ DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int by // TODO there are still some apps that don't check disk free space before write } - // Log("END WRITE:{0},{1},{2}", offset,info.Context,watch.ElapsedMilliseconds); LogFSActionSuccess("WriteFile", fileName, (SftpContext)info.Context, "Ofs:{1} Len:{0} Written:{2}", buffer.Length, offset, bytesWritten); return DokanError.ErrorSuccess; } @@ -665,11 +632,10 @@ DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int by DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) { - //Log("FLUSH:{0}", fileName); LogFSActionInit("FlushFile", fileName, (SftpContext)info.Context,""); (info.Context as SftpContext).Stream.Flush(); //git use this - //_cache.Remove(fileName); + CacheReset(GetUnixPath(fileName)); LogFSActionSuccess("FlushFile", fileName, (SftpContext)info.Context, ""); @@ -679,7 +645,6 @@ DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, DokanFileInfo info) { - //Log("GetInfo:{0}:{1}", fileName,info.Context); LogFSActionInit("FileInfo", fileName, (SftpContext)info.Context, ""); var context = info.Context as SftpContext; @@ -692,32 +657,40 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat /* * Attributtes in streams are causing trouble with git. GetInfo returns wrong length if other context is writing. */ - //sftpFileAttributes = context.Attributes; - //test: if (context.Stream != null) - sftpFileAttributes = GetAttributes(path); + try + { + sftpFileAttributes = GetAttributes(path); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } else sftpFileAttributes = context.Attributes; } else { - - //sftpFileAttributes = _cache.Get(path) as SftpFileAttributes; sftpFileAttributes = CacheGetAttr(path); if (sftpFileAttributes == null) { - sftpFileAttributes = GetAttributes(path); + try + { + sftpFileAttributes = GetAttributes(path); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } if (sftpFileAttributes != null) - //_cache.Add(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } } if (sftpFileAttributes == null) { - //try again? - //sftpFileAttributes = GetAttributes(path); - LogFSActionError("FileInfo", fileName, (SftpContext)info.Context, "No such file - unable to get info"); fileInfo = new FileInformation(); return DokanError.ErrorFileNotFound; @@ -764,8 +737,6 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat { fileInfo.Attributes |= FileAttributes.ReadOnly; } - // Console.WriteLine(sftpattributes.UserId + "|" + sftpattributes.GroupId + "L" + - // sftpattributes.OthersCanExecute + "K" + sftpattributes.OwnerCanExecute); LogFSActionSuccess("FileInfo", fileName, (SftpContext)info.Context, "Length:{0} Attrs:{1}", fileInfo.Length, fileInfo.Attributes); @@ -777,20 +748,13 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList>; - if (dircache != null) - { - files = (dircache).Item2; - Log("CacheHit:{0}", fileName); - return DokanError.ErrorSuccess; - }*/ + //byte[] handle; + List sftpFiles; - - byte[] handle; try { - handle = _sftpSession.RequestOpenDir(GetUnixPath(fileName)); + sftpFiles = ListDirectory(GetUnixPath(fileName)).ToList(); + //handle = _sshClient.RequestOpenDir(GetUnixPath(fileName)); } catch (SftpPermissionDeniedException) { @@ -800,124 +764,104 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList(); - for (var sftpFiles = _sftpSession.RequestReadDir(handle); - sftpFiles != null; - sftpFiles = _sftpSession.RequestReadDir(handle)) - { - - - - (files as List).AddRange(sftpFiles.Select( - file => + (files as List).AddRange(sftpFiles.Select( + file => + { + var sftpFileAttributes = file.Attributes; + + var fileInformation = new FileInformation + { + Attributes = + FileAttributes.NotContentIndexed, + CreationTime + = + sftpFileAttributes + . + LastWriteTime, + FileName + = + file.Name + , + LastAccessTime + = + sftpFileAttributes + . + LastAccessTime, + LastWriteTime + = + sftpFileAttributes + . + LastWriteTime, + Length + = + sftpFileAttributes + . + Size + }; + if (sftpFileAttributes.IsSymbolicLink) { - var sftpFileAttributes = file.Value; - if (sftpFileAttributes.IsSymbolicLink) - { - sftpFileAttributes = _sftpSession.RequestStat( - GetUnixPath(String.Format("{0}\\{1}", fileName, file.Key)), true) ?? - file.Value; - } - - - var fileInformation = new FileInformation - { - Attributes = - FileAttributes.NotContentIndexed, - CreationTime - = - sftpFileAttributes - . - LastWriteTime, - FileName - = - file.Key - , - LastAccessTime - = - sftpFileAttributes - . - LastAccessTime, - LastWriteTime - = - sftpFileAttributes - . - LastWriteTime, - Length - = - sftpFileAttributes - . - Size - }; - if (sftpFileAttributes.IsSymbolicLink) - { - //fileInformation.Attributes |= FileAttributes.ReparsePoint; - //link? - } + //fileInformation.Attributes |= FileAttributes.ReparsePoint; + //link? + } - if (sftpFileAttributes.IsSocket) - { - fileInformation.Attributes - |= - FileAttributes.NoScrubData | FileAttributes.System | FileAttributes.Device; - }else if (sftpFileAttributes.IsDirectory) - { - fileInformation.Attributes - |= - FileAttributes. - Directory; - fileInformation.Length = 4096;//test - } - else - { - fileInformation.Attributes |= FileAttributes.Normal; - } + if (sftpFileAttributes.IsSocket) + { + fileInformation.Attributes + |= + FileAttributes.NoScrubData | FileAttributes.System | FileAttributes.Device; + }else if (sftpFileAttributes.IsDirectory) + { + fileInformation.Attributes + |= + FileAttributes. + Directory; + fileInformation.Length = 4096;//test + } + else + { + fileInformation.Attributes |= FileAttributes.Normal; + } - if (file.Key[0] == '.') - { - fileInformation.Attributes - |= - FileAttributes. - Hidden; - } + if (file.Name[0] == '.') + { + fileInformation.Attributes + |= + FileAttributes. + Hidden; + } - if (GroupRightsSameAsOwner(sftpFileAttributes)) - { - fileInformation.Attributes |= FileAttributes.Archive; - } - if (!this.UserCanWrite(sftpFileAttributes)) - { - fileInformation.Attributes |= FileAttributes.ReadOnly; - } - if (_useOfflineAttribute) - { - fileInformation.Attributes - |= - FileAttributes. - Offline; - } - return fileInformation; - })); + if (GroupRightsSameAsOwner(sftpFileAttributes)) + { + fileInformation.Attributes |= FileAttributes.Archive; + } + if (!this.UserCanWrite(sftpFileAttributes)) + { + fileInformation.Attributes |= FileAttributes.ReadOnly; + } + if (_useOfflineAttribute) + { + fileInformation.Attributes + |= + FileAttributes. + Offline; + } + return fileInformation; + })); - int timeout = Math.Max(_attributeCacheTimeout + 2, _attributeCacheTimeout + sftpFiles.Length / 10); + int timeout = Math.Max(_attributeCacheTimeout + 2, _attributeCacheTimeout + sftpFiles.Count / 10); - foreach ( - var file in - sftpFiles.Where( - pair => !pair.Value.IsSymbolicLink)) - { - /*_cache.Set(GetUnixPath(String.Format("{0}{1}", fileName, file.Key)), file.Value, - DateTimeOffset.UtcNow.AddSeconds(timeout));*/ - CacheAddAttr(GetUnixPath(String.Format("{0}\\{1}", fileName , file.Key)), file.Value, - DateTimeOffset.UtcNow.AddSeconds(timeout)); - } + foreach ( + var file in + sftpFiles.Where( + pair => !pair.IsSymbolicLink)) + { + CacheAddAttr(GetUnixPath(String.Format("{0}\\{1}", fileName , file.Name)), file.Attributes, + DateTimeOffset.UtcNow.AddSeconds(timeout)); } - - _sftpSession.RequestClose(handle); - try { CacheAddDir( GetUnixPath(fileName), new Tuple>( @@ -933,57 +877,23 @@ var file in return DokanError.ErrorSuccess; } - DokanError IDokanOperations.FindFilesWithPattern(string fileName, string searchPattern, out IList files, DokanFileInfo info) - { - /* SFTP does not support patterns, but we can use patterns other than '*' with different cache method - */ - //Log("FindFilesWithPattern:{0},{1}", fileName,searchPattern); - LogFSActionInit("FindFilesPat", fileName, (SftpContext)info.Context, "Pattern:{0}", searchPattern); - - //* -> list all without cache - if (searchPattern == "*") - { - return ((IDokanOperations)this).FindFiles(fileName, out files, info); - } - - //get files from cache || load them - var dircache = CacheGetDir(GetUnixPath(fileName)); - if (dircache != null) - { - files = (dircache).Item2; - //Log("CacheHit:{0}", fileName); - } - else - { - DokanError result = ((IDokanOperations)this).FindFiles(fileName, out files, info); - if (result != DokanError.ErrorSuccess) - return result; - } - - //apply pattern - List filteredfiles = new List(); - foreach(FileInformation fi in files){ - if (Dokan.IsNameInExpression(searchPattern, fi.FileName, true)) - { - filteredfiles.Add(fi); - LogFSActionOther("FindFilesPat", fileName, (SftpContext)info.Context, "Result:{0}", fi.FileName); - } - } - files = filteredfiles; - - LogFSActionSuccess("FindFilesPat", fileName, (SftpContext)info.Context, "Pattern:{0} Count:{1}", searchPattern, files.Count); - return DokanError.ErrorSuccess; - } - DokanError IDokanOperations.SetFileAttributes(string fileName, FileAttributes attributes, DokanFileInfo info) { LogFSActionError("SetFileAttr", fileName, (SftpContext)info.Context, "Attrs:{0}", attributes); //get actual attributes string path = GetUnixPath(fileName); - SftpFileAttributes currentattr = GetAttributes(path); + SftpFileAttributes currentattr; + try + { + currentattr = GetAttributes(path); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + currentattr = null; + } - //rules for changes: bool rightsupdate = false; if (attributes.HasFlag(FileAttributes.Archive) && !GroupRightsSameAsOwner(currentattr)) @@ -1012,7 +922,7 @@ DokanError IDokanOperations.SetFileAttributes(string fileName, FileAttributes at //apply and reset cache try { - _sftpSession.RequestSetStat(GetUnixPath(fileName), currentattr); + SetAttributes(GetUnixPath(fileName), currentattr); } catch(SftpPermissionDeniedException e) { @@ -1041,12 +951,21 @@ DokanError IDokanOperations.SetFileTime(string fileName, DateTime? creationTime, LogFSActionInit("SetFileTime", fileName, (SftpContext)info.Context, ""); var sftpattributes = (info.Context as SftpContext).Attributes; + SftpFileAttributes tempAttributes; + try + { + tempAttributes = GetAttributes(GetUnixPath(fileName)); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + tempAttributes = null; + } - var mtime = lastWriteTime ?? (creationTime ?? sftpattributes.LastWriteTime); - - var atime = lastAccessTime ?? sftpattributes.LastAccessTime; + tempAttributes.LastWriteTime = lastWriteTime ?? (creationTime ?? sftpattributes.LastWriteTime); + tempAttributes.LastAccessTime = lastAccessTime ?? sftpattributes.LastAccessTime; - _sftpSession.RequestSetStat(GetUnixPath(fileName), new SftpFileAttributes(atime, mtime, -1, -1, -1, 0, null)); + SetAttributes(GetUnixPath(fileName), tempAttributes); LogFSActionSuccess("SetFileTime", fileName, (SftpContext)info.Context, ""); return DokanError.ErrorSuccess; @@ -1063,7 +982,16 @@ DokanError IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) if (sftpFileAttributes == null) { - sftpFileAttributes = GetAttributes(parentPath); + + try + { + sftpFileAttributes = GetAttributes(parentPath); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } if (sftpFileAttributes != null) //_cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); CacheAddAttr(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); @@ -1090,7 +1018,16 @@ DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) if (sftpFileAttributes == null) { - sftpFileAttributes = GetAttributes(parentPath); + + try + { + sftpFileAttributes = GetAttributes(parentPath); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } if (sftpFileAttributes != null) CacheAddAttr(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } @@ -1117,19 +1054,17 @@ DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) return test ? DokanError.ErrorSuccess : DokanError.ErrorDirNotEmpty; } - var handle = _sftpSession.RequestOpenDir(GetUnixPath(fileName), true); + var dir = ListDirectory(GetUnixPath(fileName)).ToList(); - if (handle == null) + if (dir == null) { LogFSActionError("DeleteDir", fileName, (SftpContext)info.Context, "Open failed, access denied?"); return DokanError.ErrorAccessDenied; } - - var dir = _sftpSession.RequestReadDir(handle); - _sftpSession.RequestClose(handle); + // usualy there are two entries . and .. - bool test2 = dir.Length == 0 || dir.All(i => i.Key == "." || i.Key == ".."); + bool test2 = dir.Count == 0 || dir.All(i => i.Name == "." || i.Name == ".."); if (test2) LogFSActionSuccess("DeleteDir", fileName, (SftpContext)info.Context, ""); @@ -1141,25 +1076,29 @@ DokanError IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replace, DokanFileInfo info) { - //Log("MoveFile |Name:{0} ,NewName:{3},Replace:{4},IsDirectory:{1} ,Context:{2}",oldName, info.IsDirectory, info.Context, newName, replace); LogFSActionInit("MoveFile", oldName, (SftpContext)info.Context, "To:{0} Replace:{1}",newName, replace); string oldpath = GetUnixPath(oldName); - /* if (_generalSftpSession.RequestLStat(oldpath, true) == null) - return DokanError.ErrorPathNotFound; - if (oldName.Equals(newName)) - return DokanError.ErrorSuccess;*/ string newpath = GetUnixPath(newName); - - if (_sftpSession.RequestLStat(newpath, true) == null) + SftpFileAttributes sftpFileAttributes; + try + { + sftpFileAttributes = GetAttributes(newpath); + } + catch (SftpPathNotFoundException e) + { + Debug.WriteLine("File not found"); + sftpFileAttributes = null; + } + if (sftpFileAttributes == null) { (info.Context as SftpContext).Release(); info.Context = null; try { - _sftpSession.RequestRename(oldpath, newpath); + RenameFile(oldpath, newpath, false); CacheResetParent(oldpath); CacheResetParent(newpath); CacheReset(oldpath); @@ -1181,21 +1120,22 @@ DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replac try { - if (_supportsPosixRename) + try { - _sftpSession.RequestPosixRename(oldpath, newpath); + RenameFile(oldpath, newpath, true); } - else + catch (NotSupportedException) { if (!info.IsDirectory) - _sftpSession.RequestRemove(newpath); - _sftpSession.RequestRename(oldpath, newpath); + DeleteFile(newpath); + RenameFile(oldpath, newpath, false); } CacheReset(oldpath); CacheResetParent(oldpath); CacheResetParent(newpath); } + catch (SftpPermissionDeniedException) { LogFSActionError("MoveFile", oldName, (SftpContext)info.Context, "To:{0} Access denied", newName); @@ -1260,21 +1200,22 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, } else { - if (_supportsStatVfs) + try { - var information = _sftpSession.RequestStatVfs(_rootpath, true); - total = (long) (information.TotalBlocks*information.BlockSize); - free = (long) (information.FreeBlocks*information.BlockSize); - used = (long) (information.AvailableBlocks*information.BlockSize); + var information = GetStatus(_rootpath); + total = (long)(information.TotalBlocks * information.BlockSize); + free = (long)(information.FreeBlocks * information.BlockSize); + used = (long)(information.AvailableBlocks * information.BlockSize); } - else - using (var cmd = new SshCommand(Session, String.Format(" df -Pk {0}", _rootpath))) - // POSIX standard df + catch (NotSupportedException) + { + using (var cmd = _sshClient.CreateCommand(String.Format(" df -Pk {0}", _rootpath), Encoding.UTF8)) + // POSIX standard df { cmd.Execute(); if (cmd.ExitStatus == 0) { - var values = cmd.Result.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); + var values = cmd.Result.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); total = Int64.Parse(values[values.Length - 5]) << 10; used = Int64.Parse(values[values.Length - 4]) << 10; @@ -1287,6 +1228,7 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, free = 0xc80000000; } } + } CacheAddDiskInfo(new Tuple(free, total, used), DateTimeOffset.UtcNow.AddMinutes(3)); @@ -1298,7 +1240,6 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, DokanError IDokanOperations.GetVolumeInformation(out string volumeLabel, out FileSystemFeatures features, out string filesystemName, DokanFileInfo info) { - //Log("GetVolumeInformation"); LogFSActionInit("GetVolumeInformation", this._volumeLabel, (SftpContext)info.Context, ""); volumeLabel = _volumeLabel; @@ -1316,7 +1257,6 @@ DokanError IDokanOperations.GetVolumeInformation(out string volumeLabel, out Fil DokanError IDokanOperations.GetFileSecurity(string filename, out FileSystemSecurity security, AccessControlSections sections, DokanFileInfo info) { - //Log("GetSecurrityInfo:{0}:{1}", filename, sections); LogFSActionInit("GetFileSecurity", filename, (SftpContext)info.Context, "Sections:{0}",sections); @@ -1338,13 +1278,12 @@ DokanError IDokanOperations.GetFileSecurity(string filename, out FileSystemSecur rights |= FileSystemRights.Traverse; } security = info.IsDirectory ? new DirectorySecurity() as FileSystemSecurity : new FileSecurity(); - // if(sections.HasFlag(AccessControlSections.Access)) security.AddAccessRule(new FileSystemAccessRule("Everyone", rights, AccessControlType.Allow)); security.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl ^ rights, AccessControlType.Deny)); //not sure this works at all, needs testing // if (sections.HasFlag(AccessControlSections.Owner)) - security.SetOwner(new NTAccount("None")); + //security.SetOwner(new NTAccount("None")); // if (sections.HasFlag(AccessControlSections.Group)) security.SetGroup(new NTAccount("None")); @@ -1355,7 +1294,6 @@ DokanError IDokanOperations.GetFileSecurity(string filename, out FileSystemSecur DokanError IDokanOperations.SetFileSecurity(string filename, FileSystemSecurity security, AccessControlSections sections, DokanFileInfo info) { - //Log("TrySetSecurity:{0}", filename); LogFSActionError("SetFileSecurity", filename, (SftpContext)info.Context, "NI"); return DokanError.ErrorAccessDenied; @@ -1363,9 +1301,7 @@ DokanError IDokanOperations.SetFileSecurity(string filename, FileSystemSecurity DokanError IDokanOperations.Unmount(DokanFileInfo info) { - //Log("UNMOUNT"); LogFSActionError("Unmount", this._volumeLabel, (SftpContext)info.Context, "NI"); - // Disconnect(); return DokanError.ErrorSuccess; } diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 836e104..4d0b122 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -84,7 +84,28 @@ true + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + ..\packages\DokanNet.1.0.7.0\lib\net40\DokanNet.dll + True + @@ -115,7 +136,6 @@ - @@ -139,6 +159,7 @@ + PublicSettingsSingleFileGenerator Settings.Designer.cs @@ -150,16 +171,6 @@ - - - {A09B408A-26A0-4FF8-83D5-6E1B1DA5F63C} - DokanNet - - - {2F5F8C90-0BD1-424F-997C-7BC6280919D1} - Renci.SshNet - - @@ -218,12 +229,19 @@ true + + + {2f5f8c90-0bd1-424f-997c-7bc6280919d1} + Renci.SshNet + + - + + + + \ No newline at end of file diff --git a/Sshfs/Sshfs/Utilities.cs b/Sshfs/Sshfs/Utilities.cs index fda5624..e56b40d 100644 --- a/Sshfs/Sshfs/Utilities.cs +++ b/Sshfs/Sshfs/Utilities.cs @@ -53,7 +53,7 @@ public static T Load(this T obj, string file) where T : ISerializable } } - public static void Presist(this List list, string file, bool delete = false) where T : ISerializable + public static void Persist(this List list, string file, bool delete = false) where T : ISerializable { string filepath = datadir.FullName + "\\" + file; @@ -74,7 +74,7 @@ public static void Presist(this List list, string file, bool delete = fals } - public static void Presist(this T obj, string file, bool delete = false) where T : ISerializable + public static void Persist(this T obj, string file, bool delete = false) where T : ISerializable { string filepath = datadir.FullName + "\\" + file; if (delete) diff --git a/Sshfs/Sshfs/VirtualDrive.cs b/Sshfs/Sshfs/VirtualDrive.cs index 1fa7f27..5427baa 100644 --- a/Sshfs/Sshfs/VirtualDrive.cs +++ b/Sshfs/Sshfs/VirtualDrive.cs @@ -112,10 +112,17 @@ private void MountLoop() _filesystem = new VirtualFilesystem("WinSshFS spool"); foreach (SftpDrive drive in _drives) { - _filesystem.AddSubFS(drive); + if (drive.MountPoint != "") + { + _filesystem.AddSubFS(drive); + } } mountedLetter = Letter; - _filesystem.Mount(String.Format("{0}:\\", mountedLetter), Settings.Default.UseNetworkDrive ? DokanOptions.NetworkDrive | DokanOptions.KeepAlive : DokanOptions.RemovableDrive | DokanOptions.KeepAlive); + int threadCount = 8; +#if DEBUG + threadCount = 1; +#endif + _filesystem.Mount(String.Format("{0}:\\", mountedLetter), Settings.Default.UseNetworkDrive ? DokanOptions.NetworkDrive | DokanOptions.KeepAlive : DokanOptions.RemovableDrive | DokanOptions.KeepAlive, threadCount); } catch (Exception e) { @@ -195,15 +202,16 @@ public void Mount() [MethodImpl(MethodImplOptions.Synchronized)] public void Unmount() { - this._threadCancel.Cancel(); - this._pauseEvent.Set(); + if(this._threadCancel != null) + this._threadCancel.Cancel(); + if(this._pauseEvent != null) + this._pauseEvent.Set(); Debug.WriteLine("Unmount"); Status = DriveStatus.Unmounting; try { - // Dokan.Unmount(Letter); Dokan.RemoveMountPoint(String.Format("{0}:\\", mountedLetter)); } catch diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 21daade..c5dcde7 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -129,7 +129,7 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS string path = fileName.Substring(1); if (path == "") { - info.IsDirectory = true; + //info.IsDirectory = true; info.Context = null; LogFSActionSuccess("OpenFile", fileName, null, "VFS root"); return DokanError.ErrorSuccess; @@ -256,14 +256,17 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) return ops.OpenDirectory(fileName, info); } - info.IsDirectory = true; + if (fileName.Length == 1) //root dir { LogFSActionSuccess("OpenDir", fileName, drive, "Found, VFS root"); - return DokanError.ErrorSuccess; + return DokanError.ErrorPathNotFound; } - //root test shoud keet lastactive if drag and drop(win8) + + info.IsDirectory = true; + + //root test shoud keep lastactive if drag and drop(win8) lastActiveSubsytem = null; string path = fileName.Substring(1);//cut leading \ @@ -475,7 +478,7 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList file.FileName).Contains(mp)) + if (!files.Select(file => file.FileName).Contains(mp) && mp != "") { FileInformation fi = new FileInformation(); fi.FileName = mp; @@ -490,80 +493,6 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) - { - SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); - LogFSActionInit("FindFilesPat", fileName, drive, ""); - - if (drive != null) - { - LogFSActionSuccess("FindFilesPat", fileName, drive, "NonVFS"); - return GetSubSystemOperations(drive).FindFilesWithPattern(fileName, pattern, out files, info); - } - - - files = new List(); - - string path = fileName.Substring(1);//cut leading \ - foreach (SftpDrive subdrive in _subsytems) - { - string mp = subdrive.MountPoint; // mp1 || mp1\mp2 ... - if (mp.Length == 0) - continue; - - if (path.Length > 0) //not root dir - { - if (path == mp) //this shoud not happend, because is managed by drive - { - LogFSActionError("FindFilesPat", fileName, drive, "mountpoint not in drives?"); - break; - } - - if (mp.IndexOf(path + '\\') == 0) //path is part of mount point =>implies=> length of path>mp - { - mp = mp.Substring(path.Length + 1); //cut the path - } - else - { - continue; - } - } - - int cuttmp = mp.IndexOf('\\'); - if (cuttmp > 0) // have submountpoint like mp1\mp2 - { - mp = mp.Substring(0, cuttmp); - } - - if (!files.Select(file => file.FileName).Contains(mp)) - { - FileInformation fi = new FileInformation(); - fi.FileName = mp; - fi.Attributes = FileAttributes.NotContentIndexed | FileAttributes.Directory | FileAttributes.Offline | FileAttributes.System; - fi.CreationTime = DateTime.Now; - fi.LastWriteTime = DateTime.Now; - fi.LastAccessTime = DateTime.Now; - files.Add(fi); - } - } - - //apply pattern - List filteredfiles = new List(); - Regex repattern = new Regex("^" + Regex.Escape(pattern).Replace("\\*", ".*") + "$"); - foreach (FileInformation fi in files) - { - if (repattern.IsMatch(fi.FileName)) - { - filteredfiles.Add(fi); - LogFSActionOther("FindFilesPat", fileName, drive, "Result:{0}", fi.FileName); - } - } - files = filteredfiles; - - LogFSActionError("FindFilesPat", fileName, drive, "Pattern:{0} Count:{1}", pattern, files.Count); - return DokanError.ErrorSuccess; - } - DokanError IDokanOperations.SetFileAttributes(string fileName, FileAttributes attributes, DokanFileInfo info) { Log("VFS TrySetAttributes:{0}\n{1};", fileName, attributes); diff --git a/Sshfs/Sshfs/packages.config b/Sshfs/Sshfs/packages.config new file mode 100644 index 0000000..564d7a5 --- /dev/null +++ b/Sshfs/Sshfs/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From ba0a34f552a37798c38a4218f9d818442766e4b0 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 31 Oct 2015 13:33:46 +0100 Subject: [PATCH 081/134] bugfix spooldrive folders open in Total Commander --- Sshfs/Sshfs/VirtualFilesystem.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index c5dcde7..55fe57c 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -256,15 +256,14 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) return ops.OpenDirectory(fileName, info); } - - if (fileName.Length == 1) //root dir { LogFSActionSuccess("OpenDir", fileName, drive, "Found, VFS root"); - return DokanError.ErrorPathNotFound; + info.IsDirectory = true; + return DokanError.ErrorSuccess; } - info.IsDirectory = true; + //root test shoud keep lastactive if drag and drop(win8) lastActiveSubsytem = null; @@ -277,6 +276,7 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) if (path == mp) { info.Context = subdrive; + info.IsDirectory = true; LogFSActionSuccess("OpenDir", fileName, drive, "Found, final mountpoint"); return DokanError.ErrorSuccess; } @@ -284,6 +284,7 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) if (mp.IndexOf(path + '\\') == 0) { //path is part of mount point info.Context = subdrive; + info.IsDirectory = true; LogFSActionSuccess("OpenDir", fileName, drive, "Found, part of mountpoint"); return DokanError.ErrorSuccess; } From ebec85ae31d65445ac74d4076d79956c3916a421 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 31 Oct 2015 13:37:48 +0100 Subject: [PATCH 082/134] RC1 removed hash of commit from asm info --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 6 +++--- Sshfs/Sshfs/Sshfs.csproj | 4 ++-- version.hook.sh | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index a703927..5119ba4 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows ™")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("4every1 s.r.o.")] -[assembly: AssemblyProduct("WinSshFS 4every1 edition 1.5.12.8-0-ge23318a")] +[assembly: AssemblyProduct("WinSshFS 4every1 edition 1.5.12.9-0")] [assembly: AssemblyCopyright("Copyright © 4every1 s.r.o.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.5.12.8")] -[assembly: AssemblyVersion("1.5.12.8")] +// [assembly: AssemblyVersion("1.5.12.9")] +[assembly: AssemblyVersion("1.5.12.9")] [assembly: AssemblyFileVersion("1.5.12.8")] diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 4d0b122..fff5d5e 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -34,8 +34,8 @@ true index.html false - 8 - 1.5.12.8 + 9 + 1.5.12.9 true true true diff --git a/version.hook.sh b/version.hook.sh index 10dacb1..1e161d1 100644 --- a/version.hook.sh +++ b/version.hook.sh @@ -27,13 +27,13 @@ then IFS='-' read -ra COMMITS <<< "${PARTS[1]}" #echo "${COMMITS[0]}" - #This will be the version in the format ... + #This will be the version in the format .. (. remove revision, amend of version inside will be ok with this) version="${TAG[0]}"."${TAG[1]}"."${TAG[2]}"."${TAG[3]}" echo $version #Update the AssemblyVersion and AssemblyFileVersion attribute with the 'version' sed -i.bak "s/\AssemblyVersion(\".*\")/AssemblyVersion(\"$version\")/g" $AI 2>/dev/null sed -i.bak "s/\AssemblyFileVersion(\".*\")/AssemblyFileVersion(\"$version\")/g" $AI 2>/dev/null - sed -i.bak "s/AssemblyProduct(\".*\")/AssemblyProduct(\"$PRODUCT $tag\")/g" $AI 2>/dev/null + sed -i.bak "s/AssemblyProduct(\".*\")/AssemblyProduct(\"$PRODUCT $version-${COMMITS[0]}\")/g" $AI 2>/dev/null #cat $AI fi From 3ef2516b1697593c19f7e353c1364eee3cc4fdf1 Mon Sep 17 00:00:00 2001 From: nyz93 Date: Sun, 1 Nov 2015 14:56:52 +0100 Subject: [PATCH 083/134] android support --- README.md | 1 + Sshfs/Sshfs/SftpFilesystem.cs | 44 ++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a544f68..830942d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ I did some improvments for my needs. Current devel branch version seems to be ve There are several changes, main differences: * Windows 10 Support +* Support for Android hosts (tested with CyanogenMod 11 [Android 4.4], requires busybox to be installed) * current Renci SSH (2014.4.6beta) * solved few bugs like payload, 2 hosts and others * Puttyant (Pageant) support diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 49b9612..7e7c993 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -56,6 +56,8 @@ internal sealed class SftpFilesystem : SftpClient, IDokanOperations private int _userId; + private string _idCommand = "id"; + private string _dfCommand = "df"; private HashSet _userGroups; private readonly int _attributeCacheTimeout; @@ -92,7 +94,8 @@ protected override void OnConnected() this.Log("Connected %s", _volumeLabel); _sshClient.Connect(); - + + CheckAndroid(); _userId = GetUserId(); if (_userId != -1) @@ -239,18 +242,30 @@ private string GetUnixPath(string path) return String.Format("{0}{1}", _rootpath, path.Replace('\\', '/').Replace("//","/")); } + private void CheckAndroid() + { + using (var cmd = _sshClient.CreateCommand("test -f /system/build.prop", Encoding.UTF8)) + { + cmd.Execute(); + if (cmd.ExitStatus == 0) + { + _idCommand = "busybox id"; + _dfCommand = "busybox df"; + } + } + } private IEnumerable GetUserGroupsIds() { - using (var cmd = _sshClient.CreateCommand("id -G ", Encoding.UTF8)) + using (var cmd = _sshClient.CreateCommand(_idCommand + " -G ", Encoding.UTF8)) { cmd.Execute(); - return cmd.Result.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries).Select(Int32.Parse); + return cmd.Result.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(Int32.Parse); } } private int GetUserId() { - using (var cmd = _sshClient.CreateCommand("id -u ", Encoding.UTF8)) + using (var cmd = _sshClient.CreateCommand(_idCommand + " -u ", Encoding.UTF8)) // Thease commands seems to be POSIX so the only problem would be Windows enviroment { cmd.Execute(); @@ -1192,6 +1207,8 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, var diskSpaceInfo = CacheGetDiskInfo(); + bool dfCheck = false; + if (diskSpaceInfo != null) { free = diskSpaceInfo.Item1; @@ -1200,6 +1217,9 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, } else { + total = 0x1900000000; //100 GiB + used = 0xc80000000; // 50 Gib + free = 0xc80000000; try { var information = GetStatus(_rootpath); @@ -1209,7 +1229,15 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, } catch (NotSupportedException) { - using (var cmd = _sshClient.CreateCommand(String.Format(" df -Pk {0}", _rootpath), Encoding.UTF8)) + dfCheck = true; + } + catch (SshException) + { + dfCheck = true; + } + if(dfCheck) + { + using (var cmd = _sshClient.CreateCommand(String.Format(_dfCommand + " -Pk {0}", _rootpath), Encoding.UTF8)) // POSIX standard df { cmd.Execute(); @@ -1221,12 +1249,6 @@ DokanError IDokanOperations.GetDiskFreeSpace(out long free, out long total, used = Int64.Parse(values[values.Length - 4]) << 10; free = Int64.Parse(values[values.Length - 3]) << 10; //<======maybe to cache all this } - else - { - total = 0x1900000000; //100 GiB - used = 0xc80000000; // 50 Gib - free = 0xc80000000; - } } } From 94d9be208b2adf636284469d3c6ca7301d9cc9ec Mon Sep 17 00:00:00 2001 From: Tyler Gibson Date: Thu, 19 Nov 2015 17:22:46 -0800 Subject: [PATCH 084/134] Fixing symlink directory traversal --- Renci.SshNet/SftpClient.cs | 32 ++++++++++++++++++++++++++++++++ Sshfs/Sshfs/SftpFilesystem.cs | 20 ++++++++++++++------ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Renci.SshNet/SftpClient.cs b/Renci.SshNet/SftpClient.cs index 12ade58..aa56ba1 100644 --- a/Renci.SshNet/SftpClient.cs +++ b/Renci.SshNet/SftpClient.cs @@ -412,6 +412,38 @@ public void SymbolicLink(string path, string linkPath) this._sftpSession.RequestSymLink(fullPath, linkFullPath); } + /// + /// Creates a symbolic link from old path to new path. + /// + /// The old path. + /// Client is not connected. + /// is null. + /// There was a problem retrieving the symlink. + /// Client is not connected. + /// is null. + /// The method was called after the client was disposed. + public SftpFile GetSymbolicLinkTarget(string path) + { + CheckDisposed(); + + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var symTargetPaths = this._sftpSession.RequestReadLink(path, true); + + if (symTargetPaths == null) + { + throw new SftpPathNotFoundException("No valid symlink target"); + } + + SftpFileAttributes symTargetAttributes = GetAttributes(symTargetPaths[0].Key); + + return new SftpFile(this._sftpSession, symTargetPaths[0].Key, symTargetAttributes); + } + /// /// Retrieves list of files in remote directory. /// diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 49b9612..4894201 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -438,9 +438,6 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) if (sftpFileAttributes != null) CacheAddAttr(path, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } - - - if (sftpFileAttributes != null && sftpFileAttributes.IsDirectory) { @@ -453,8 +450,8 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) info.IsDirectory = true; info.Context = new SftpContext(sftpFileAttributes); - var dircahe = CacheGetDir(path); - if (dircahe != null && dircahe.Item1 != sftpFileAttributes.LastWriteTime) + var dircache = CacheGetDir(path); + if (dircache != null && dircache.Item1 != sftpFileAttributes.LastWriteTime) { CacheReset(path); } @@ -801,7 +798,18 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList Date: Thu, 11 Feb 2016 00:34:37 +0100 Subject: [PATCH 085/134] DokanError becomes NtStatus, KeepAlive Option no longer exists --- Sshfs/Sshfs/SftpDrive.cs | 6 +- Sshfs/Sshfs/SftpFilesystem.cs | 142 +++++++++++++++---------------- Sshfs/Sshfs/Sshfs.csproj | 4 +- Sshfs/Sshfs/VirtualDrive.cs | 2 +- Sshfs/Sshfs/VirtualFilesystem.cs | 128 ++++++++++++++-------------- Sshfs/Sshfs/packages.config | 2 +- 6 files changed, 141 insertions(+), 143 deletions(-) diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index 22aa024..b150991 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -131,7 +131,7 @@ private void SetupFilesystem() ConnectionInfo info; switch (ConnectionType) { - case ConnectionType.Pageant: + case ConnectionType.Pageant: var agent = new PageantProtocol(); if (pt == ProxyTypes.None) { @@ -144,7 +144,7 @@ private void SetupFilesystem() else { info = new AgentConnectionInfo(Host, Port, Username, pt, Proxy, ProxyPort, agent); - } + } break; case ConnectionType.PrivateKey: if (pt == ProxyTypes.None) { @@ -264,7 +264,7 @@ private void MountLoop() threadCount=1; #endif _filesystem.Mount(String.Format("{0}:\\", Letter), - Settings.Default.UseNetworkDrive?DokanOptions.NetworkDrive|DokanOptions.KeepAlive: DokanOptions.RemovableDrive|DokanOptions.KeepAlive, threadCount); + Settings.Default.UseNetworkDrive?DokanOptions.NetworkDrive: DokanOptions.RemovableDrive, threadCount); } catch (Exception e) { diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 49b9612..6a8f759 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -290,14 +290,14 @@ private bool GroupRightsSameAsOwner(SftpFileAttributes attributes) #region DokanOperations - DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, + NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, FileMode mode, FileOptions options, FileAttributes attributes, DokanFileInfo info) { if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith("autorun.inf", StringComparison.OrdinalIgnoreCase)) //.... { - return DokanError.ErrorFileNotFound; + return NtStatus.NoSuchFile; } LogFSActionInit("OpenFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1}", mode,options); @@ -339,29 +339,29 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (options.HasFlag(FileOptions.DeleteOnClose)) { - return DokanError.ErrorError;//this will result in calling DeleteFile in Windows Explorer + return NtStatus.Error;//this will result in calling DeleteFile in Windows Explorer } info.Context = new SftpContext(sftpFileAttributes, false); LogFSActionOther("OpenFile", fileName, (SftpContext)info.Context, "Dir open or get attrs"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } } else { LogFSActionError("OpenFile", fileName, (SftpContext)info.Context, "File not found"); - return DokanError.ErrorFileNotFound; + return NtStatus.NoSuchFile; } break; case FileMode.CreateNew: if (sftpFileAttributes != null) - return DokanError.ErrorAlreadyExists; + return NtStatus.ObjectNameCollision; CacheResetParent(path); break; case FileMode.Truncate: if (sftpFileAttributes == null) - return DokanError.ErrorFileNotFound; + return NtStatus.NoSuchFile; CacheResetParent(path); //_cache.Remove(path); this.CacheReset(path); @@ -401,18 +401,18 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS { //Log("Up directory must be created"); LogFSActionError("OpenFile", fileName, (SftpContext)info.Context, "Up directory mising:{0}", ownerpath); - return DokanError.ErrorPathNotFound; + return NtStatus.ObjectPathNotFound; } } LogFSActionError("OpenFile", fileName, (SftpContext)info.Context, "Access denied"); - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } LogFSActionSuccess("OpenFile", fileName, (SftpContext)info.Context, "Mode:{0}", mode); - return DokanError.ErrorSuccess; + return NtStatus.Success; } - DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) + NtStatus IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context,""); @@ -446,7 +446,7 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { if (!UserCanExecute(sftpFileAttributes) || !UserCanRead(sftpFileAttributes)) { - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } @@ -459,13 +459,13 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) CacheReset(path); } LogFSActionSuccess("OpenDir", fileName, (SftpContext)info.Context,""); - return DokanError.ErrorSuccess; + return NtStatus.Success; } LogFSActionError("OpenDir", fileName, (SftpContext)info.Context,"Path not found"); - return DokanError.ErrorPathNotFound; + return NtStatus.ObjectPathNotFound; } - DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) + NtStatus IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) { LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context, ""); @@ -478,18 +478,18 @@ DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) catch (SftpPermissionDeniedException) { LogFSActionError("OpenDir", fileName, (SftpContext)info.Context, "Access denied"); - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } catch (SshException) // operation should fail with generic error if file already exists { LogFSActionError("OpenDir", fileName, (SftpContext)info.Context, "Already exists"); - return DokanError.ErrorAlreadyExists; + return NtStatus.ObjectNameCollision; } LogFSActionSuccess("OpenDir", fileName, (SftpContext)info.Context,""); - return DokanError.ErrorSuccess; + return NtStatus.Success; } - DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) + void IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { LogFSActionInit("Cleanup", fileName, (SftpContext)info.Context, ""); @@ -528,10 +528,9 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) } LogFSActionSuccess("Cleanup", fileName, (SftpContext)info.Context, ""); - return DokanError.ErrorSuccess; } - DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) + void IDokanOperations.CloseFile(string fileName, DokanFileInfo info) { LogFSActionInit("CloseFile", fileName, (SftpContext)info.Context, ""); @@ -551,13 +550,10 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) { CacheReset(GetUnixPath(fileName)); } - - - return DokanError.ErrorSuccess; } - DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, + NtStatus IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, DokanFileInfo info) { LogFSActionInit("ReadFile", fileName, (SftpContext)info.Context, "BuffLen:{0} Offset:{1}", buffer.Length, offset); @@ -590,10 +586,10 @@ DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int byt } } LogFSActionSuccess("ReadFile", fileName, (SftpContext)info.Context, ""); - return DokanError.ErrorSuccess; + return NtStatus.Success; } - DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, + NtStatus IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, DokanFileInfo info) { LogFSActionInit("WriteFile", fileName, (SftpContext)info.Context, "Ofs:{0} Len:{1}", offset, buffer.Length); @@ -626,11 +622,11 @@ DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int by } LogFSActionSuccess("WriteFile", fileName, (SftpContext)info.Context, "Ofs:{1} Len:{0} Written:{2}", buffer.Length, offset, bytesWritten); - return DokanError.ErrorSuccess; + return NtStatus.Success; } - DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) + NtStatus IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) { LogFSActionInit("FlushFile", fileName, (SftpContext)info.Context,""); @@ -639,10 +635,10 @@ DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info CacheReset(GetUnixPath(fileName)); LogFSActionSuccess("FlushFile", fileName, (SftpContext)info.Context, ""); - return DokanError.ErrorSuccess; + return NtStatus.Success; } - DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, + NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, DokanFileInfo info) { LogFSActionInit("FileInfo", fileName, (SftpContext)info.Context, ""); @@ -693,7 +689,7 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat { LogFSActionError("FileInfo", fileName, (SftpContext)info.Context, "No such file - unable to get info"); fileInfo = new FileInformation(); - return DokanError.ErrorFileNotFound; + return NtStatus.NoSuchFile; } @@ -740,10 +736,10 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat LogFSActionSuccess("FileInfo", fileName, (SftpContext)info.Context, "Length:{0} Attrs:{1}", fileInfo.Length, fileInfo.Attributes); - return DokanError.ErrorSuccess; + return NtStatus.Success; } - DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) + NtStatus IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { //Log("FindFiles:{0}", fileName); LogFSActionInit("FindFiles", fileName, (SftpContext)info.Context, ""); @@ -759,7 +755,7 @@ DokanError IDokanOperations.FindFiles(string fileName, out IListMinimumRecommendedRules.ruleset - - ..\packages\DokanNet.1.0.7.0\lib\net40\DokanNet.dll + + ..\packages\DokanNet.1.0.8.0\lib\net40\DokanNet.dll True diff --git a/Sshfs/Sshfs/VirtualDrive.cs b/Sshfs/Sshfs/VirtualDrive.cs index 5427baa..ae52ab3 100644 --- a/Sshfs/Sshfs/VirtualDrive.cs +++ b/Sshfs/Sshfs/VirtualDrive.cs @@ -122,7 +122,7 @@ private void MountLoop() #if DEBUG threadCount = 1; #endif - _filesystem.Mount(String.Format("{0}:\\", mountedLetter), Settings.Default.UseNetworkDrive ? DokanOptions.NetworkDrive | DokanOptions.KeepAlive : DokanOptions.RemovableDrive | DokanOptions.KeepAlive, threadCount); + _filesystem.Mount(String.Format("{0}:\\", mountedLetter), Settings.Default.UseNetworkDrive ? DokanOptions.NetworkDrive : DokanOptions.RemovableDrive, threadCount); } catch (Exception e) { diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 55fe57c..cd42843 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -100,14 +100,14 @@ private void LogFSActionOther(String action, String path, SftpDrive subsystem, s #region DokanOperations - DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, + NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileShare share, FileMode mode, FileOptions options, FileAttributes attributes, DokanFileInfo info) { if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith("autorun.inf", StringComparison.OrdinalIgnoreCase)) //.... { - return DokanError.ErrorFileNotFound; + return NtStatus.NoSuchFile; } SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); @@ -119,7 +119,7 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS if (idops == null) { //this happens if mounting failed - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } return idops.CreateFile(fileName, access, share, mode, options, attributes, info); } @@ -132,7 +132,7 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS //info.IsDirectory = true; info.Context = null; LogFSActionSuccess("OpenFile", fileName, null, "VFS root"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } foreach (SftpDrive drive2 in this._subsytems) { @@ -143,7 +143,7 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS info.IsDirectory = true; info.Context = drive2; LogFSActionSuccess("OpenFile", fileName, drive2, "VFS (sub)mountpoint"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } } } @@ -151,7 +151,7 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS //pathnotfound detection? LogFSActionError("OpenFile", fileName, null, "File not found"); - return DokanError.ErrorFileNotFound; + return NtStatus.NoSuchFile; } private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) @@ -237,7 +237,7 @@ private IDokanOperations GetSubSystemOperations(SftpDrive drive) return ((IDokanOperations)drive._filesystem); } - DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) + NtStatus IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); LogFSActionInit("OpenDir", fileName, drive, ""); @@ -250,7 +250,7 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) if (ops == null) { LogFSActionError("OpenDir", fileName, drive, "Cannot open, mount failed?"); - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } LogFSActionSuccess("OpenDir", fileName, drive, "Found, subsytem"); return ops.OpenDirectory(fileName, info); @@ -260,7 +260,7 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) { LogFSActionSuccess("OpenDir", fileName, drive, "Found, VFS root"); info.IsDirectory = true; - return DokanError.ErrorSuccess; + return NtStatus.Success; } @@ -278,7 +278,7 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) info.Context = subdrive; info.IsDirectory = true; LogFSActionSuccess("OpenDir", fileName, drive, "Found, final mountpoint"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } if (mp.IndexOf(path + '\\') == 0) @@ -286,30 +286,31 @@ DokanError IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) info.Context = subdrive; info.IsDirectory = true; LogFSActionSuccess("OpenDir", fileName, drive, "Found, part of mountpoint"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } } LogFSActionError("OpenDir", fileName, drive, "Path not found"); - return DokanError.ErrorPathNotFound; + return NtStatus.ObjectPathNotFound; } - DokanError IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) + NtStatus IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); if (drive != null) return GetSubSystemOperations(drive).CreateDirectory(fileName, info); - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } - DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) + void IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); LogFSActionInit("Cleanup", fileName, drive, ""); if (drive != null) { LogFSActionSuccess("Cleanup", fileName, drive, "nonVFS clean"); - return GetSubSystemOperations(drive).Cleanup(fileName, info); + GetSubSystemOperations(drive).Cleanup(fileName, info); + return; } if (info.Context != null) @@ -319,17 +320,18 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) } LogFSActionSuccess("Cleanup", fileName, drive, "VFS clean"); - return DokanError.ErrorSuccess; + return; } - DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) + void IDokanOperations.CloseFile(string fileName, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); LogFSActionInit("CloseFile", fileName, drive, ""); if (drive != null) { LogFSActionSuccess("CloseFile", fileName, drive, "NonVFS close"); - return GetSubSystemOperations(drive).CloseFile(fileName, info); + GetSubSystemOperations(drive).CloseFile(fileName, info); + return; } if (info.Context != null) @@ -339,11 +341,11 @@ DokanError IDokanOperations.CloseFile(string fileName, DokanFileInfo info) } LogFSActionSuccess("CloseFile", fileName, drive, "VFS close"); - return DokanError.ErrorSuccess; + return; } - DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, + NtStatus IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytesRead, long offset, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); @@ -351,10 +353,10 @@ DokanError IDokanOperations.ReadFile(string fileName, byte[] buffer, out int byt return GetSubSystemOperations(drive).ReadFile(fileName, buffer, out bytesRead, offset, info); bytesRead = 0; - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } - DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, + NtStatus IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); @@ -362,20 +364,20 @@ DokanError IDokanOperations.WriteFile(string fileName, byte[] buffer, out int by return GetSubSystemOperations(drive).WriteFile(fileName, buffer, out bytesWritten, offset, info); bytesWritten = 0; - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } - DokanError IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) + NtStatus IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); if (drive != null) return GetSubSystemOperations(drive).FlushFileBuffers(fileName, info); - return DokanError.ErrorSuccess; + return NtStatus.Success; } - DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, + NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformation fileInfo, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); @@ -401,7 +403,7 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat if (fileName.Length == 1) { //root dir LogFSActionSuccess("FileInfo", fileName, drive, "root info"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } string path = fileName.Substring(1);//cut leading \ @@ -410,7 +412,7 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat { drive = info.Context as SftpDrive; LogFSActionSuccess("FileInfo", fileName, drive, "from context"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } foreach (SftpDrive subdrive in _subsytems) @@ -421,24 +423,24 @@ DokanError IDokanOperations.GetFileInformation(string fileName, out FileInformat info.Context = mp; //fileInfo.FileName = path.Substring(path.LastIndexOf("\\")+1); LogFSActionSuccess("FileInfo", fileName, drive, "final mountpoint"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } if (mp.IndexOf(path + '\\') == 0) { //path is part of mount point //fileInfo.FileName = path.Substring(path.LastIndexOf("\\") + 1); LogFSActionSuccess("FileInfo", fileName, drive, "part of mountpoint"); - return DokanError.ErrorSuccess; + return NtStatus.Success; } } LogFSActionError("FileInfo", fileName, drive, "path not found"); - return DokanError.ErrorPathNotFound; + return NtStatus.ObjectPathNotFound; } - DokanError IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) + NtStatus IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); LogFSActionError("FindFiles", fileName, drive, "!? not using FindFilesWithPattern !?"); @@ -491,49 +493,49 @@ DokanError IDokanOperations.FindFiles(string fileName, out IList - + \ No newline at end of file From ac28aba07092213badf08ba6a23fc2791fb06f7f Mon Sep 17 00:00:00 2001 From: marquis-de-muesli Date: Thu, 11 Feb 2016 00:40:04 +0100 Subject: [PATCH 086/134] Mounted, FindStreams added to Dokany Interface Unmount became Unmounted --- Sshfs/Sshfs/SftpFilesystem.cs | 17 +++++++++++++++-- Sshfs/Sshfs/VirtualFilesystem.cs | 17 ++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 6a8f759..b9b78a1 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -1295,12 +1295,25 @@ NtStatus IDokanOperations.SetFileSecurity(string filename, FileSystemSecurity se return NtStatus.AccessDenied; } - NtStatus IDokanOperations.Unmount(DokanFileInfo info) + NtStatus IDokanOperations.Unmounted(DokanFileInfo info) { - LogFSActionError("Unmount", this._volumeLabel, (SftpContext)info.Context, "NI"); + LogFSActionError("Unmounted", this._volumeLabel, (SftpContext)info.Context, "NI"); return NtStatus.Success; } + NtStatus IDokanOperations.Mounted(DokanFileInfo info) + { + LogFSActionError("Mounted", this._volumeLabel, (SftpContext)info.Context, "NI"); + return NtStatus.Success; + } + + NtStatus IDokanOperations.FindStreams(string fileName, out IList streams, DokanFileInfo info) + { + //Alternate Data Streams are NFTS-only feature, no need to handle + streams = new FileInformation[0]; + return NtStatus.NotImplemented; + } + #endregion #region Events diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index cd42843..11dc8ec 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -656,14 +656,25 @@ NtStatus IDokanOperations.SetFileSecurity(string fileName, FileSystemSecurity se return NtStatus.AccessDenied; } - NtStatus IDokanOperations.Unmount(DokanFileInfo info) + NtStatus IDokanOperations.Unmounted(DokanFileInfo info) { - Log("UNMOUNT"); - + Log("UNMOUNTED"); // Disconnect(); return NtStatus.Success; } + NtStatus IDokanOperations.Mounted(DokanFileInfo info) + { + Log("MOUNTED"); + return NtStatus.Success; + } + + NtStatus IDokanOperations.FindStreams(string fileName, out IList streams, DokanFileInfo info) + { + streams = new FileInformation[0]; + return NtStatus.NotImplemented; + } + #endregion /* From 139643fe166cb0d798c70dcee9f8f0c35cc5afae Mon Sep 17 00:00:00 2001 From: marquis-de-muesli Date: Thu, 11 Feb 2016 14:05:57 +0100 Subject: [PATCH 087/134] IDokanOperations CreateDirectory and OpenDirectory got folded into CreateFile --- Sshfs/Sshfs/SftpFilesystem.cs | 17 +++++++++++++++-- Sshfs/Sshfs/VirtualFilesystem.cs | 18 ++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index b9b78a1..a3c3059 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -294,6 +294,19 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha FileMode mode, FileOptions options, FileAttributes attributes, DokanFileInfo info) { + //Split into four methods? + LogFSActionInit("CreateFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1} IsDirectory:{3}", mode, options, info.IsDirectory); + + if (info.IsDirectory) + { + if (mode == FileMode.Open) + return OpenDirectory(fileName, info); + if (mode == FileMode.CreateNew) + return CreateDirectory(fileName, info); + + return NtStatus.NotImplemented; + } + if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith("autorun.inf", StringComparison.OrdinalIgnoreCase)) //.... { @@ -412,7 +425,7 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha return NtStatus.Success; } - NtStatus IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) + private NtStatus OpenDirectory(string fileName, DokanFileInfo info) { LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context,""); @@ -465,7 +478,7 @@ NtStatus IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) return NtStatus.ObjectPathNotFound; } - NtStatus IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) + private NtStatus CreateDirectory(string fileName, DokanFileInfo info) { LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context, ""); diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 11dc8ec..841db10 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -104,6 +104,16 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha FileMode mode, FileOptions options, FileAttributes attributes, DokanFileInfo info) { + if (info.IsDirectory) + { + if (mode == FileMode.Open) + return OpenDirectory(fileName, info); + if (mode == FileMode.CreateNew) + return CreateDirectory(fileName, info); + + return NtStatus.NotImplemented; + } + if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith("autorun.inf", StringComparison.OrdinalIgnoreCase)) //.... { @@ -237,7 +247,7 @@ private IDokanOperations GetSubSystemOperations(SftpDrive drive) return ((IDokanOperations)drive._filesystem); } - NtStatus IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) + private NtStatus OpenDirectory(string fileName, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); LogFSActionInit("OpenDir", fileName, drive, ""); @@ -253,7 +263,7 @@ NtStatus IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) return NtStatus.AccessDenied; } LogFSActionSuccess("OpenDir", fileName, drive, "Found, subsytem"); - return ops.OpenDirectory(fileName, info); + return ops.CreateFile(fileName, FileAccess.GenericRead, FileShare.None, FileMode.Open, FileOptions.None, FileAttributes.Directory, info); } if (fileName.Length == 1) //root dir @@ -293,11 +303,11 @@ NtStatus IDokanOperations.OpenDirectory(string fileName, DokanFileInfo info) return NtStatus.ObjectPathNotFound; } - NtStatus IDokanOperations.CreateDirectory(string fileName, DokanFileInfo info) + private NtStatus CreateDirectory(string fileName, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); if (drive != null) - return GetSubSystemOperations(drive).CreateDirectory(fileName, info); + return GetSubSystemOperations(drive).CreateFile(fileName, FileAccess.GenericRead, FileShare.None, FileMode.CreateNew, FileOptions.None, FileAttributes.Directory, info); return NtStatus.AccessDenied; } From bc9c4e3f5a764773747ae297af2e2eccd44a49a1 Mon Sep 17 00:00:00 2001 From: marquis-de-muesli Date: Thu, 11 Feb 2016 16:53:00 +0100 Subject: [PATCH 088/134] Sign updated DokanNet --- Sshfs/Sshfs/Sshfs.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 00d0431..154d337 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -242,6 +242,6 @@ --> - + \ No newline at end of file From 5a6f4e34ac527fd99277178019509bbf848cc37d Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Thu, 11 Feb 2016 22:08:45 +0100 Subject: [PATCH 089/134] fix debug bug --- Sshfs/Sshfs/SftpFilesystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index a3c3059..44be180 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -295,7 +295,7 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha FileAttributes attributes, DokanFileInfo info) { //Split into four methods? - LogFSActionInit("CreateFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1} IsDirectory:{3}", mode, options, info.IsDirectory); + LogFSActionInit("CreateFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1} IsDirectory:{2}", mode, options, info.IsDirectory); if (info.IsDirectory) { From e40b2d0445d8142bd00c8bc57b9b2beed3f07f69 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 15 Feb 2016 20:19:41 +0100 Subject: [PATCH 090/134] Fix delete non existing file --- Sshfs/Sshfs/SftpFilesystem.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 49b9612..f7c8f6b 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -521,7 +521,14 @@ DokanError IDokanOperations.Cleanup(string fileName, DokanFileInfo info) } else { - DeleteFile(path); + try + { + DeleteFile(path); + } + catch (SftpPathNotFoundException) + { + //not existing file + } } CacheReset(path); CacheResetParent(path); From 55b373ce636b14bbd3feefb34cb74e9c3d95e622 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 15 Feb 2016 23:23:29 +0100 Subject: [PATCH 091/134] add missing SFtpFileSystemInformation --- .../Sftp/SftpFileSystemInformation.cs | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 Renci.SshNet/Sftp/SftpFileSystemInformation.cs diff --git a/Renci.SshNet/Sftp/SftpFileSystemInformation.cs b/Renci.SshNet/Sftp/SftpFileSystemInformation.cs new file mode 100644 index 0000000..e5bb503 --- /dev/null +++ b/Renci.SshNet/Sftp/SftpFileSystemInformation.cs @@ -0,0 +1,136 @@ +namespace Renci.SshNet.Sftp +{ + /// + /// Contains File system information exposed by statvfs@openssh.com request. + /// + public class SftpFileSytemInformation + { + private readonly ulong _flag; + + private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; + + private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; + + /// + /// Gets the size of the block. + /// + /// + /// The size of the block. + /// + public ulong BlockSize { get; private set; } + + /// + /// Gets the total blocks. + /// + /// + /// The total blocks. + /// + public ulong TotalBlocks { get; private set; } + + /// + /// Gets the free blocks. + /// + /// + /// The free blocks. + /// + public ulong FreeBlocks { get; private set; } + + /// + /// Gets the available blocks. + /// + /// + /// The available blocks. + /// + public ulong AvailableBlocks { get; private set; } + + /// + /// Gets the total nodes. + /// + /// + /// The total nodes. + /// + public ulong TotalNodes { get; private set; } + + /// + /// Gets the free nodes. + /// + /// + /// The free nodes. + /// + public ulong FreeNodes { get; private set; } + + /// + /// Gets the available nodes. + /// + /// + /// The available nodes. + /// + public ulong AvailableNodes { get; private set; } + + /// + /// Gets the sid. + /// + /// + /// The sid. + /// + public ulong Sid { get; private set; } + + /// + /// Gets a value indicating whether this instance is read only. + /// + /// + /// true if this instance is read only; otherwise, false. + /// + public bool IsReadOnly + { + get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } + } + + /// + /// Gets a value indicating whether [supports set uid]. + /// + /// + /// true if [supports set uid]; otherwise, false. + /// + public bool SupportsSetUid + { + get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } + } + + /// + /// Gets the max name lenght. + /// + /// + /// The max name lenght. + /// + public ulong MaxNameLenght { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The bsize. + /// The frsize. + /// The blocks. + /// The bfree. + /// The bavail. + /// The files. + /// The ffree. + /// The favail. + /// The sid. + /// The flag. + /// The namemax. + internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) + { + this.BlockSize = frsize; + this.TotalBlocks = blocks; + this.FreeBlocks = bfree; + this.AvailableBlocks = bavail; + this.TotalNodes = files; + this.FreeNodes = ffree; + this.AvailableNodes = favail; + this.Sid = sid; + this._flag = flag; + this.MaxNameLenght = namemax; + } + } +} \ No newline at end of file From a3b115d34b5ba255c46580f48eba7757e15f4eb4 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 16 Feb 2016 00:39:28 +0100 Subject: [PATCH 092/134] errors repair --- Sshfs/Sshfs/SftpFilesystem.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index f7c8f6b..2b7b96b 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -301,7 +301,7 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS } LogFSActionInit("OpenFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1}", mode,options); - + string path = GetUnixPath(fileName); var sftpFileAttributes = this.CacheGetAttr(path); @@ -350,6 +350,10 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS else { LogFSActionError("OpenFile", fileName, (SftpContext)info.Context, "File not found"); + /*if (share.HasFlag(FileShare.Delete) ) + { + return DokanError.ErrorAccessDenied;//t08/3 + }*/ return DokanError.ErrorFileNotFound; } break; @@ -377,6 +381,10 @@ DokanError IDokanOperations.CreateFile(string fileName, FileAccess access, FileS ((ulong) access & 0x40010006) == 0 ? System.IO.FileAccess.Read : System.IO.FileAccess.ReadWrite, sftpFileAttributes); + if (sftpFileAttributes != null) + { + return DokanError.ErrorAlreadyExists; + } } catch (SshException ex) // Don't have access rights or try to read broken symlink { @@ -1124,6 +1132,10 @@ DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replac info.Context = null; + if (sftpFileAttributes.IsDirectory) + { + return DokanError.ErrorAccessDenied; + } try { @@ -1137,6 +1149,10 @@ DokanError IDokanOperations.MoveFile(string oldName, string newName, bool replac DeleteFile(newpath); RenameFile(oldpath, newpath, false); } + catch (SftpPathNotFoundException) + { + return DokanError.ErrorAccessDenied; + } CacheReset(oldpath); CacheResetParent(oldpath); From 3d678d6770ea39c03369984db0950c884fdbd733 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 16 Feb 2016 01:47:23 +0100 Subject: [PATCH 093/134] Dokan 1.1rc1, send lasterror with already exists in CreateFile --- Sshfs/Sshfs/SftpFilesystem.cs | 21 +++++++++++++++------ Sshfs/Sshfs/Sshfs.csproj | 10 +++++++--- Sshfs/Sshfs/packages.config | 8 ++++---- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index c68967f..a38a284 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -32,13 +32,21 @@ using Renci.SshNet.Sftp; using FileAccess = DokanNet.FileAccess; using System.Text.RegularExpressions; - +using System.Runtime.InteropServices; namespace Sshfs { + internal sealed class SftpFilesystem : SftpClient, IDokanOperations { - + + /// + /// Sets the last-error code for the calling thread. + /// + /// The last-error code for the thread. + [DllImport("kernel32.dll", SetLastError = true)] + static extern void SetLastError(uint dwErrorCode); + #region Constants #endregion @@ -390,9 +398,10 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha ((ulong) access & 0x40010006) == 0 ? System.IO.FileAccess.Read : System.IO.FileAccess.ReadWrite, sftpFileAttributes); - if (sftpFileAttributes != null) + if ( sftpFileAttributes != null) { - return DokanError.ErrorAlreadyExists; + + SetLastError(183); //ERROR_ALREADY_EXISTS } } catch (SshException ex) // Don't have access rights or try to read broken symlink @@ -1139,7 +1148,7 @@ NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, if (sftpFileAttributes.IsDirectory) { - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } try @@ -1156,7 +1165,7 @@ NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, } catch (SftpPathNotFoundException) { - return DokanError.ErrorAccessDenied; + return NtStatus.AccessDenied; } CacheReset(oldpath); diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 154d337..b87aa65 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -102,8 +102,8 @@ MinimumRecommendedRules.ruleset - - ..\packages\DokanNet.1.0.8.0\lib\net40\DokanNet.dll + + ..\packages\DokanNet.1.1.0-rc1\lib\net40\DokanNet.dll True @@ -242,6 +242,10 @@ --> - + + + + + \ No newline at end of file diff --git a/Sshfs/Sshfs/packages.config b/Sshfs/Sshfs/packages.config index 121d76a..6e5fe3d 100644 --- a/Sshfs/Sshfs/packages.config +++ b/Sshfs/Sshfs/packages.config @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file From 5172bacef1188edfd2d8f986505530e56dc1ea78 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 16 Feb 2016 02:08:19 +0100 Subject: [PATCH 094/134] attributes revision --- Sshfs/Sshfs/SftpFilesystem.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index a38a284..60dab9e 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -728,9 +728,7 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio fileInfo = new FileInformation - { - Attributes = - FileAttributes.NotContentIndexed, + { FileName = Path.GetFileName(fileName), //String.Empty, // GetInfo info doesn't use it maybe for sorting . CreationTime = sftpFileAttributes.LastWriteTime, @@ -743,20 +741,16 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio fileInfo.Attributes |= FileAttributes.Directory; fileInfo.Length = 0; // Windows directories use length of 0 } - else - { - fileInfo.Attributes |= FileAttributes.Normal; - } if (fileName.Length != 1 && fileName[fileName.LastIndexOf('\\') + 1] == '.') //aditional check if filename isn't \\ { fileInfo.Attributes |= FileAttributes.Hidden; } - if (GroupRightsSameAsOwner(sftpFileAttributes)) + /*if (GroupRightsSameAsOwner(sftpFileAttributes)) { fileInfo.Attributes |= FileAttributes.Archive; - } + }*/ if (_useOfflineAttribute) { fileInfo.Attributes |= FileAttributes.Offline; @@ -767,6 +761,11 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio fileInfo.Attributes |= FileAttributes.ReadOnly; } + if (fileInfo.Attributes == 0) + { + fileInfo.Attributes = FileAttributes.Normal;//can be only alone + } + LogFSActionSuccess("FileInfo", fileName, (SftpContext)info.Context, "Length:{0} Attrs:{1}", fileInfo.Length, fileInfo.Attributes); return NtStatus.Success; From 7f9e86948c6b766199b18edb860e2993296289e9 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 16 Feb 2016 03:10:36 +0100 Subject: [PATCH 095/134] Deleteing dirs vs files fixies --- Sshfs/Sshfs/SftpFilesystem.cs | 38 +++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 60dab9e..e765fa9 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -308,7 +308,24 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha if (info.IsDirectory) { if (mode == FileMode.Open) - return OpenDirectory(fileName, info); + { + NtStatus status = OpenDirectory(fileName, info); + + try + { + if (status == NtStatus.ObjectNameNotFound) + { + GetAttributes(fileName); + //no expception -> its file + return (NtStatus)0xC0000103L; //STATUS_NOT_A_DIRECTORY + } + } + catch (SftpPathNotFoundException e) + { + } + return status; + + } if (mode == FileMode.CreateNew) return CreateDirectory(fileName, info); @@ -456,9 +473,9 @@ private NtStatus OpenDirectory(string fileName, DokanFileInfo info) { sftpFileAttributes = GetAttributes(path); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { - Debug.WriteLine("File not found"); + Debug.WriteLine("Dir not found"); sftpFileAttributes = null; } if (sftpFileAttributes != null) @@ -468,8 +485,12 @@ private NtStatus OpenDirectory(string fileName, DokanFileInfo info) - if (sftpFileAttributes != null && sftpFileAttributes.IsDirectory) + if (sftpFileAttributes != null) { + if (!sftpFileAttributes.IsDirectory) + { + return (NtStatus)0xC0000103L; //STATUS_NOT_A_DIRECTORY + } if (!UserCanExecute(sftpFileAttributes) || !UserCanRead(sftpFileAttributes)) { return NtStatus.AccessDenied; @@ -488,7 +509,8 @@ private NtStatus OpenDirectory(string fileName, DokanFileInfo info) return NtStatus.Success; } LogFSActionError("OpenDir", fileName, (SftpContext)info.Context,"Path not found"); - return NtStatus.ObjectPathNotFound; + //return NtStatus.ObjectPathNotFound; + return NtStatus.ObjectNameNotFound; } private NtStatus CreateDirectory(string fileName, DokanFileInfo info) @@ -1024,7 +1046,11 @@ NtStatus IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) //_cache.Add(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); CacheAddAttr(parentPath, sftpFileAttributes, DateTimeOffset.UtcNow.AddSeconds(_attributeCacheTimeout)); } - + /* shoud be tested, but performance... + if (IsDirectory) + { + return NtStatus.AccessDenied; + }*/ LogFSActionSuccess("DeleteFile", fileName, (SftpContext)info.Context, "Success:{0}", UserCanWrite(sftpFileAttributes)); return From 6f14b5138ba331450813f0235a9fb6968fdcfc71 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 17 Feb 2016 23:23:49 +0100 Subject: [PATCH 096/134] Hotfix - SftpContextStream vs SftpFileStream. --- Renci.SshNet/SftpClient.cs | 13 +++++++++++++ Sshfs/Sshfs/SftpContext.cs | 7 ++++--- Sshfs/Sshfs/SftpFilesystem.cs | 6 +++--- Sshfs/Sshfs/Sshfs.csproj | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Renci.SshNet/SftpClient.cs b/Renci.SshNet/SftpClient.cs index aa56ba1..ccb4889 100644 --- a/Renci.SshNet/SftpClient.cs +++ b/Renci.SshNet/SftpClient.cs @@ -8,6 +8,10 @@ using System.Globalization; using System.Threading; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + + +[assembly: InternalsVisibleTo("WinSshFS, PublicKey=00240000048000009400000006020000002400005253413100040000010001005337866700b92e3a2a5d5be0292cdb0f2f6daa283526126b30169255b3c522f51593d15b5db31da4ddbe3e6ef5d9b80a05ddf4d1b1bca1c67ca62bf0b0c4d1b2ea3d4242027a2052b3c3cb17b98077a5c9f08143617ec3a1143c97c48bf27a378a9ec250220fb899f25c084599f477e36f699ec74aa452a3fd9e90007648a397")] namespace Renci.SshNet { @@ -22,6 +26,15 @@ public partial class SftpClient : BaseClient /// private SftpSession _sftpSession; + /// + /// test + /// + /// SftpSession + internal SftpSession getSftpSession() + { + return this._sftpSession; + } + /// /// Holds the operation timeout. /// diff --git a/Sshfs/Sshfs/SftpContext.cs b/Sshfs/Sshfs/SftpContext.cs index 5567f8a..91627bd 100644 --- a/Sshfs/Sshfs/SftpContext.cs +++ b/Sshfs/Sshfs/SftpContext.cs @@ -24,7 +24,7 @@ namespace Sshfs internal sealed class SftpContext : IDisposable { private SftpFileAttributes _attributes; - private SftpFileStream _stream; + private SftpContextStream _stream; public bool deleteOnCloseWorkaround = false; @@ -42,7 +42,8 @@ public SftpContext(SftpFileAttributes attributes, bool aDeleteOnCloseWorkaround) public SftpContext(SftpClient client, string path, FileMode mode, FileAccess access, SftpFileAttributes attributes) { - _stream = client.Open(path, mode, access); + //_stream = client.Open(path, mode, access); + _stream = new SftpContextStream(client.getSftpSession(), path, mode, access, attributes); _attributes = attributes; } @@ -51,7 +52,7 @@ public SftpFileAttributes Attributes get { return _attributes; } } - public SftpFileStream Stream + public SftpContextStream Stream { get { return _stream; } } diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index e0cbebb..345c452 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -628,7 +628,7 @@ NtStatus IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytes } else { - SftpFileStream stream = (info.Context as SftpContext).Stream; + SftpContextStream stream = (info.Context as SftpContext).Stream; lock (stream) { stream.Position = offset; @@ -661,8 +661,8 @@ NtStatus IDokanOperations.WriteFile(string fileName, byte[] buffer, out int byte { Log("Data: {0}", Encoding.ASCII.GetString(buffer)); } - - SftpFileStream stream = (info.Context as SftpContext).Stream; + + SftpContextStream stream = (info.Context as SftpContext).Stream; lock (stream) { stream.Position = offset; diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index b87aa65..d67daf9 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -125,6 +125,7 @@ + From af6f7ff33c62034ad2dd61062645190d72c5b365 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 17 Feb 2016 23:23:49 +0100 Subject: [PATCH 097/134] Hotfix - SftpContextStream vs SftpFileStream. --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 +- Sshfs/Sshfs/SftpContextStream.cs | 459 +++++++++++++++++++++++++ 2 files changed, 463 insertions(+), 4 deletions(-) create mode 100644 Sshfs/Sshfs/SftpContextStream.cs diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 5119ba4..4d90f4c 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows ™")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("4every1 s.r.o.")] -[assembly: AssemblyProduct("WinSshFS 4every1 edition 1.5.12.9-0")] +[assembly: AssemblyProduct("WinSshFS 4every1 edition 1.6.0.0-0")] [assembly: AssemblyCopyright("Copyright © 4every1 s.r.o.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.5.12.9")] -[assembly: AssemblyVersion("1.5.12.9")] -[assembly: AssemblyFileVersion("1.5.12.8")] +// [assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyFileVersion("1.6.0.0")] diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs new file mode 100644 index 0000000..5ef1c48 --- /dev/null +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -0,0 +1,459 @@ +// Copyright (c) 2012 Dragan Mladjenovic +// 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. + +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using Renci.SshNet.Sftp; + +namespace Sshfs +{ + internal sealed class SftpContextStream : Stream + { + + private int WRITE_BUFFER_SIZE; + private int READ_BUFFER_SIZE; + private readonly byte[] _writeBuffer; + private byte[] _readBuffer = new byte[0]; + + + private readonly SftpSession _session; + private SftpFileAttributes _attributes; + private byte[] _handle; + + private bool _writeMode; + private int _writeBufferPosition; + private int _readBufferPosition; + private long _position; + + internal SftpContextStream(SftpSession session, string path, FileMode mode, FileAccess access, + SftpFileAttributes attributes) + { + Flags flags = Flags.None; + + switch (access) + { + case FileAccess.Read: + flags = Flags.Read; + break; + case FileAccess.Write: + flags = Flags.Write; + break; + case FileAccess.ReadWrite: + flags = Flags.Read | Flags.Write; + break; + } + + switch (mode) + { + case FileMode.Append: + flags |= Flags.Append; + break; + case FileMode.Create: + if (attributes == null) + { + flags |= Flags.CreateNew; + } + else + { + flags |= Flags.Truncate; + } + break; + case FileMode.CreateNew: + flags |= Flags.CreateNew; + break; + case FileMode.Open: + break; + case FileMode.OpenOrCreate: + flags |= Flags.CreateNewOrOpen; + break; + case FileMode.Truncate: + flags |= Flags.Truncate; + break; + } + + _session = session; + + _handle = _session.RequestOpen(path, flags); + + _attributes = attributes ?? _session.RequestFStat(_handle); + + WRITE_BUFFER_SIZE = (int)session.CalculateOptimalWriteLength(65536, _handle); + READ_BUFFER_SIZE = (int)session.CalculateOptimalReadLength(65536); + + if (access.HasFlag(FileAccess.Write)) + { + _writeBuffer = new byte[WRITE_BUFFER_SIZE]; + _writeMode = true; + } + + _position = mode != FileMode.Append ? 0 : _attributes.Size; + } + + + public SftpFileAttributes Attributes + { + get + { + lock (this) + { + if (_writeMode) + { + + + + + //FlushWriteBuffer(); + SetupRead(); + _attributes = _session.RequestFStat(_handle); + + } + } + return _attributes; + } + } + + + public override bool CanRead + { + get { throw new NotImplementedException(); } + } + + public override bool CanSeek + { + get { throw new NotImplementedException(); } + } + + public override bool CanWrite + { + get { throw new NotImplementedException(); } + } + + public override long Length + { + get { throw new NotImplementedException(); } + } + + + public override long Position + { + get { return _position; } + set + { + if (!_writeMode) + { + long newPosn = _position - _readBufferPosition; + if (value >= newPosn && value < + (newPosn + _readBuffer.Length)) + { + _readBufferPosition = (int) (value - newPosn); + } + else + { + _readBufferPosition = 0; + _readBuffer = new byte[0]; + } + } + else + { + // Console.WriteLine("Position:{0}=?{1}",value,_position); + if (_position != value) + { + FlushWriteBuffer(); + } + } + _position = value; + } + } + + + public override void Close() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + + public override void Flush() + { + lock (this) + { + if (_writeMode) + { + FlushWriteBuffer(); + } + else + { + /* if (_bufferPosn < _bufferLen) + { + _position -= _bufferPosn; + }*/ + _readBufferPosition = 0; + _readBuffer = new byte[0]; + } + } + } + + + public override int Read(byte[] buffer, int offset, int count) + { + int readLen = 0; + + + // Lock down the file stream while we do this. + + // Set up for the read operation. + SetupRead(); + + // Read data into the caller's buffer. + while (count > 0) + { + // How much data do we have available in the buffer? + int tempLen = _readBuffer.Length - _readBufferPosition; + if (tempLen <= 0) + { + _readBufferPosition = 0; + + _readBuffer = _session.RequestRead(_handle, (ulong) _position, checked((uint)READ_BUFFER_SIZE)); + + + if (_readBuffer.Length > 0) + { + tempLen = _readBuffer.Length; + } + else + { + break; + } + } + + + // Don't read more than the caller wants. + if (tempLen > count) + { + tempLen = count; + } + + // Copy stream data to the caller's buffer. + Debug.WriteLine("Copy:{0},{1},{2},{3},{4}",_readBuffer,_readBufferPosition,buffer,offset,tempLen); + Buffer.BlockCopy(_readBuffer, _readBufferPosition, buffer, offset, tempLen); + + // Advance to the next buffer positions. + readLen += tempLen; + offset += tempLen; + count -= tempLen; + _readBufferPosition += tempLen; + _position += tempLen; + } + + + // Return the number of bytes that were read to the caller. + return readLen; + } + + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + lock (this) + { + // Lock down the file stream while we do this. + + // Setup this object for writing. + SetupWrite(); + + _attributes.Size = value; + + _session.RequestFSetStat(_handle, _attributes); + } + } + + + public override void Write(byte[] buffer, int offset, int count) + { + // Lock down the file stream while we do this. + + // Setup this object for writing. + SetupWrite(); + + // Write data to the file stream. + // while (count > 0) + // { + // Determine how many bytes we can write to the buffer. + int tempLen = WRITE_BUFFER_SIZE - _writeBufferPosition; + + /* if (tempLen <= 0) + { + + _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer); + + _writeBufferPosition = 0; + tempLen = WRITE_BUFFER_SIZE; + }*/ + + + if (tempLen >= count) //enought remaining space in writeBuffer + { + // No: copy the data to the write buffer first. + Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); + _writeBufferPosition += count; + } + else //writeBuffer space insufficient + { + FlushWriteBuffer(); + + + if (count > WRITE_BUFFER_SIZE) //writeBuffer size is still lower + { + //solves problem: max writtable count is WRITE_BUFFER_SIZE + int remainingcount = count; + int suboffset = 0; + while (remainingcount >= WRITE_BUFFER_SIZE)//fire whole blocks + { + int chunkcount = remainingcount <= WRITE_BUFFER_SIZE ? remainingcount : WRITE_BUFFER_SIZE; + Buffer.BlockCopy(buffer, offset+suboffset, _writeBuffer, _writeBufferPosition/*always zero*/, chunkcount); + _session.RequestWrite(_handle, (ulong)(_position+suboffset), _writeBuffer, null, null); + remainingcount -= chunkcount; + suboffset += chunkcount; + } + if (remainingcount > 0)//if something remains, do it standard way: + { + Buffer.BlockCopy(buffer, offset+suboffset, _writeBuffer, _writeBufferPosition/*shoud be 0*/, remainingcount); + _writeBufferPosition += remainingcount; + } + } + else + { + Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeBufferPosition, count); + _writeBufferPosition += count; + } + } + // Advance the buffer and stream positions. + _position += count; + // offset += tempLen; + // count -= tempLen; + // } + + // If the buffer is full, then do a speculative flush now, + // rather than waiting for the next call to this method. + if (_writeBufferPosition == WRITE_BUFFER_SIZE) + { + + _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, null,null); + + + _writeBufferPosition = 0; + } + } + + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + + if (_handle != null) + { + if (_writeMode) + { + FlushWriteBuffer(); + } + + _session.RequestClose(_handle); + + _handle = null; + } + } + + + private void FlushWriteBuffer() + { + // Console.WriteLine("FLUSHHHH the water"); + if (_writeBufferPosition > 0) + { + // Console.WriteLine("Written:{0}",_writeBufferPosition); + var data = new byte[_writeBufferPosition]; + Buffer.BlockCopy(_writeBuffer, 0, data, 0, _writeBufferPosition); + + + + _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, null,null); + + + _writeBufferPosition = 0; + } + } + +/* + private void FlushWriteBufferNoPipelining() + { + const int maximumDataSize = 1024 * 32 - 38; + Console.WriteLine("FLUSHHHH the water no pipe"); + if (_writeBufferPosition > 0) + { + Console.WriteLine("Written:{0}", _writeBufferPosition); + + int block = ((_writeBufferPosition - 1) / maximumDataSize) + 1; + for (int i = 0; i < block; i++) + { + var blockBufferSize = Math.Min(_writeBufferPosition - maximumDataSize * i, maximumDataSize); + var blockBuffer = new byte[blockBufferSize]; + + Buffer.BlockCopy(_writeBuffer, i*maximumDataSize, blockBuffer, 0, blockBufferSize); + + using (var wait = new AutoResetEvent(false)) + { + _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition+i*maximumDataSize), blockBuffer, wait); + } + } + + _writeBufferPosition = 0; + } + } +*/ + + + private void SetupRead() + { + if (_writeMode) + { + FlushWriteBuffer(); + _writeMode = false; + } + } + + + private void SetupWrite() + { + if (_writeMode) return; + + /* if (_bufferPosn < _bufferLen) + { + _position -= _bufferPosn; + }*/ + _readBufferPosition = 0; + _writeBufferPosition = 0; + _readBuffer = new byte[0]; + _writeMode = true; + } + } +} \ No newline at end of file From a6f9d2884b29e1a10eec516e48b649d1c7190e1b Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 21 Feb 2016 01:44:25 +0100 Subject: [PATCH 098/134] version with commit --- version.hook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.hook.sh b/version.hook.sh index 1e161d1..9383d35 100644 --- a/version.hook.sh +++ b/version.hook.sh @@ -28,7 +28,7 @@ then #echo "${COMMITS[0]}" #This will be the version in the format .. (. remove revision, amend of version inside will be ok with this) - version="${TAG[0]}"."${TAG[1]}"."${TAG[2]}"."${TAG[3]}" + version="${TAG[0]}"."${TAG[1]}"."${TAG[2]}"."${COMMITS[0]}" echo $version #Update the AssemblyVersion and AssemblyFileVersion attribute with the 'version' From 2a49bcce8c28a94348194a5d459a4ded5d512451 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 21 Feb 2016 01:42:35 +0100 Subject: [PATCH 099/134] Stream read aync experiment --- .gitignore | 3 ++ Renci.SshNet/Sftp/SftpSession.cs | 41 +++++++++++++++ Sshfs/Sshfs/SftpContextStream.cs | 86 +++++++++++++++----------------- 3 files changed, 84 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index 11eeadf..38b7db2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ TestResults /Dokan /bluescreenview /Sshfs/Sshfs/Properties/AssemblyInfo.cs.bak +/Sshfs/.nuget +/Sshfs/Packages.dgml +/Sshfs/packages diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs index f0b5954..b7f0a74 100644 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ b/Renci.SshNet/Sftp/SftpSession.cs @@ -360,6 +360,47 @@ internal byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length) return data; } + /// + /// Performs SSH_FXP_READ request. + /// + /// The handle. + /// The offset. + /// The length. + /// data array; null if EOF + internal bool RequestReadAsync(byte[] handle, UInt64 offset, UInt32 length, EventWaitHandle wait, Action readCompleted) + { + SshException exception = null; + + var request = new SftpReadRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, length, + (response) =>//data + { + if (readCompleted != null) + { + readCompleted(response); + } + if (wait != null) + wait.Set(); + }, + (response) =>//status + { + exception = this.GetSftpException(response); + if (wait != null) + wait.Set(); + }); + + this.SendRequest(request); + + //if (wait != null) + // this.WaitOnHandle(wait, this._operationTimeout); + + if (exception != null) + { + throw exception; + } + + return true; + } + /// /// Performs SSH_FXP_WRITE request. /// diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs index 5ef1c48..01feaa3 100644 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -16,10 +16,11 @@ // THE SOFTWARE. using System; -using System.Diagnostics; using System.IO; using System.Threading; using Renci.SshNet.Sftp; +using Renci.SshNet.Common; +using System.Collections.Generic; namespace Sshfs { @@ -157,7 +158,7 @@ public override long Position { if (!_writeMode) { - long newPosn = _position - _readBufferPosition; + /* long newPosn = _position - _readBufferPosition; if (value >= newPosn && value < (newPosn + _readBuffer.Length)) { @@ -167,7 +168,7 @@ public override long Position { _readBufferPosition = 0; _readBuffer = new byte[0]; - } + }*/ } else { @@ -209,61 +210,54 @@ public override void Flush() } } - - public override int Read(byte[] buffer, int offset, int count) + private void ReadRequestSend( + long position, int count, + byte[] buffer, int bufferOffset, + EventWaitHandle wait, Action Received) { - int readLen = 0; - + _session.RequestReadAsync(_handle, (ulong)(position), checked((uint)count), wait, + response => { + Buffer.BlockCopy(response.Data, 0, buffer, bufferOffset, response.Data.Length); + Received(response.Data.Length); + wait.Set(); + } + ); + } - // Lock down the file stream while we do this. + public override int Read(byte[] buffer, int bufferOffset, int bufferCount) + { // Set up for the read operation. SetupRead(); - // Read data into the caller's buffer. - while (count > 0) - { - // How much data do we have available in the buffer? - int tempLen = _readBuffer.Length - _readBufferPosition; - if (tempLen <= 0) - { - _readBufferPosition = 0; - - _readBuffer = _session.RequestRead(_handle, (ulong) _position, checked((uint)READ_BUFFER_SIZE)); - - - if (_readBuffer.Length > 0) - { - tempLen = _readBuffer.Length; - } - else - { - break; - } - } + List waits = new List(); + int readCount = bufferCount; + int winOffset = 0; + int receivedTotal = 0; + + while (readCount > 0) + { + int winSize = readCount > READ_BUFFER_SIZE ? READ_BUFFER_SIZE : readCount; - // Don't read more than the caller wants. - if (tempLen > count) - { - tempLen = count; - } + EventWaitHandle wait = new AutoResetEvent(false); + waits.Add(wait); - // Copy stream data to the caller's buffer. - Debug.WriteLine("Copy:{0},{1},{2},{3},{4}",_readBuffer,_readBufferPosition,buffer,offset,tempLen); - Buffer.BlockCopy(_readBuffer, _readBufferPosition, buffer, offset, tempLen); + this.ReadRequestSend( + _position + winOffset, winSize, + buffer, bufferOffset + winOffset, + wait, received => { receivedTotal += received; } + ); - // Advance to the next buffer positions. - readLen += tempLen; - offset += tempLen; - count -= tempLen; - _readBufferPosition += tempLen; - _position += tempLen; + winOffset += winSize; + readCount -= winSize; } - - // Return the number of bytes that were read to the caller. - return readLen; + if (!WaitHandle.WaitAll(waits.ToArray(), this.ReadTimeout)) { + throw new SshOperationTimeoutException("Timeout on wait"); + } + _position = _position + receivedTotal; + return receivedTotal; } From cbaf1dc62222e979513ef89ba5e433eeb5d98e4d Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 22 Feb 2016 01:34:04 +0100 Subject: [PATCH 100/134] add missing SftpContextStream --- Sshfs/Sshfs/SftpContextStream.cs | 82 ++++++-------------------------- 1 file changed, 15 insertions(+), 67 deletions(-) diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs index 01feaa3..c3576ab 100644 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -28,10 +28,8 @@ internal sealed class SftpContextStream : Stream { private int WRITE_BUFFER_SIZE; - private int READ_BUFFER_SIZE; + //private int READ_BUFFER_SIZE; private readonly byte[] _writeBuffer; - private byte[] _readBuffer = new byte[0]; - private readonly SftpSession _session; private SftpFileAttributes _attributes; @@ -39,7 +37,6 @@ internal sealed class SftpContextStream : Stream private bool _writeMode; private int _writeBufferPosition; - private int _readBufferPosition; private long _position; internal SftpContextStream(SftpSession session, string path, FileMode mode, FileAccess access, @@ -95,7 +92,7 @@ internal SftpContextStream(SftpSession session, string path, FileMode mode, File _attributes = attributes ?? _session.RequestFStat(_handle); WRITE_BUFFER_SIZE = (int)session.CalculateOptimalWriteLength(65536, _handle); - READ_BUFFER_SIZE = (int)session.CalculateOptimalReadLength(65536); + //READ_BUFFER_SIZE = (int)session.CalculateOptimalReadLength(65536); if (access.HasFlag(FileAccess.Write)) { @@ -115,10 +112,6 @@ public SftpFileAttributes Attributes { if (_writeMode) { - - - - //FlushWriteBuffer(); SetupRead(); _attributes = _session.RequestFStat(_handle); @@ -158,17 +151,7 @@ public override long Position { if (!_writeMode) { - /* long newPosn = _position - _readBufferPosition; - if (value >= newPosn && value < - (newPosn + _readBuffer.Length)) - { - _readBufferPosition = (int) (value - newPosn); - } - else - { - _readBufferPosition = 0; - _readBuffer = new byte[0]; - }*/ + } else { @@ -200,26 +183,23 @@ public override void Flush() } else { - /* if (_bufferPosn < _bufferLen) - { - _position -= _bufferPosn; - }*/ - _readBufferPosition = 0; - _readBuffer = new byte[0]; } } } - private void ReadRequestSend( + private void ReadToBufferAsync( long position, int count, byte[] buffer, int bufferOffset, EventWaitHandle wait, Action Received) { _session.RequestReadAsync(_handle, (ulong)(position), checked((uint)count), wait, response => { - Buffer.BlockCopy(response.Data, 0, buffer, bufferOffset, response.Data.Length); + lock (buffer) + { + Buffer.BlockCopy(response.Data, 0, buffer, bufferOffset, response.Data.Length); + } Received(response.Data.Length); - wait.Set(); + //wait.Set(); } ); } @@ -231,6 +211,7 @@ public override int Read(byte[] buffer, int bufferOffset, int bufferCount) SetupRead(); List waits = new List(); + int READ_BUFFER_SIZE = checked((int)this._session.CalculateOptimalReadLength(65536-13)); int readCount = bufferCount; int winOffset = 0; @@ -241,21 +222,23 @@ public override int Read(byte[] buffer, int bufferOffset, int bufferCount) int winSize = readCount > READ_BUFFER_SIZE ? READ_BUFFER_SIZE : readCount; EventWaitHandle wait = new AutoResetEvent(false); + wait.Reset(); waits.Add(wait); - this.ReadRequestSend( + this.ReadToBufferAsync( _position + winOffset, winSize, buffer, bufferOffset + winOffset, - wait, received => { receivedTotal += received; } + wait, received => { Interlocked.Add(ref receivedTotal, received); } ); winOffset += winSize; readCount -= winSize; } - if (!WaitHandle.WaitAll(waits.ToArray(), this.ReadTimeout)) { + if (!WaitHandle.WaitAll(waits.ToArray(), 20000)) { throw new SshOperationTimeoutException("Timeout on wait"); } + _position = _position + receivedTotal; return receivedTotal; } @@ -397,35 +380,6 @@ private void FlushWriteBuffer() } } -/* - private void FlushWriteBufferNoPipelining() - { - const int maximumDataSize = 1024 * 32 - 38; - Console.WriteLine("FLUSHHHH the water no pipe"); - if (_writeBufferPosition > 0) - { - Console.WriteLine("Written:{0}", _writeBufferPosition); - - int block = ((_writeBufferPosition - 1) / maximumDataSize) + 1; - for (int i = 0; i < block; i++) - { - var blockBufferSize = Math.Min(_writeBufferPosition - maximumDataSize * i, maximumDataSize); - var blockBuffer = new byte[blockBufferSize]; - - Buffer.BlockCopy(_writeBuffer, i*maximumDataSize, blockBuffer, 0, blockBufferSize); - - using (var wait = new AutoResetEvent(false)) - { - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition+i*maximumDataSize), blockBuffer, wait); - } - } - - _writeBufferPosition = 0; - } - } -*/ - - private void SetupRead() { if (_writeMode) @@ -440,13 +394,7 @@ private void SetupWrite() { if (_writeMode) return; - /* if (_bufferPosn < _bufferLen) - { - _position -= _bufferPosn; - }*/ - _readBufferPosition = 0; _writeBufferPosition = 0; - _readBuffer = new byte[0]; _writeMode = true; } } From 29ce99c5ee7ddaaafe44cc53bc98cef3eb0992cd Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 22 Feb 2016 01:35:24 +0100 Subject: [PATCH 101/134] add simple FSTester --- Sshfs/FSTester/App.config | 6 + Sshfs/FSTester/FSTester.csproj | 62 ++++++ Sshfs/FSTester/Program.cs | 46 +++++ Sshfs/FSTester/Properties/AssemblyInfo.cs | 36 ++++ Sshfs/FSTester/Test.cs | 44 +++++ Sshfs/FSTester/TestWriteLines.cs | 47 +++++ Sshfs/Sshfs.sln | 224 +++++++++++++--------- 7 files changed, 370 insertions(+), 95 deletions(-) create mode 100644 Sshfs/FSTester/App.config create mode 100644 Sshfs/FSTester/FSTester.csproj create mode 100644 Sshfs/FSTester/Program.cs create mode 100644 Sshfs/FSTester/Properties/AssemblyInfo.cs create mode 100644 Sshfs/FSTester/Test.cs create mode 100644 Sshfs/FSTester/TestWriteLines.cs diff --git a/Sshfs/FSTester/App.config b/Sshfs/FSTester/App.config new file mode 100644 index 0000000..d740e88 --- /dev/null +++ b/Sshfs/FSTester/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Sshfs/FSTester/FSTester.csproj b/Sshfs/FSTester/FSTester.csproj new file mode 100644 index 0000000..8ee9726 --- /dev/null +++ b/Sshfs/FSTester/FSTester.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B} + Exe + Properties + FSTester + FSTester + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sshfs/FSTester/Program.cs b/Sshfs/FSTester/Program.cs new file mode 100644 index 0000000..d6075d2 --- /dev/null +++ b/Sshfs/FSTester/Program.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FSTester +{ + class Program + { + + static void Main(string[] args) + { + if (args.Length == 0) + { + Console.WriteLine("You need help. right?"); + return; + } + else + { + string workingPath = args[0]; + + Collection tests = new Collection(); + tests.Add(new TestWriteLines(workingPath)); + + foreach (Test test in tests) + { + Console.Write("Test X: "); + if (test.Go()) + { + Console.WriteLine("Success"); + } + else + { + Console.Write("Fail"); + Console.WriteLine(" >>> " + test.getLastError()); + } + + } + } + + Console.ReadKey(); + } + } +} diff --git a/Sshfs/FSTester/Properties/AssemblyInfo.cs b/Sshfs/FSTester/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..bbceb19 --- /dev/null +++ b/Sshfs/FSTester/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FSTester")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FSTester")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1b5533b8-dfcd-4f19-9eab-438633e7c00b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Sshfs/FSTester/Test.cs b/Sshfs/FSTester/Test.cs new file mode 100644 index 0000000..d2c2b6e --- /dev/null +++ b/Sshfs/FSTester/Test.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FSTester +{ + public class Test + { + protected string workingPath; + protected string lastError; + + public Test(string workPath) + { + this.workingPath = workPath; + } + + virtual public bool Go() + { + if (!Directory.Exists(this.workingPath)) + { + this.lastError = "Working dir not found('" + this.workingPath + "')"; + return false; + } + return true; + } + + public string getLastError() + { + return this.lastError; + } + + protected void Log(string s) + { + Console.Write(s); + } + protected void LogLine(string line) + { + Console.WriteLine( line ); + } + } +} diff --git a/Sshfs/FSTester/TestWriteLines.cs b/Sshfs/FSTester/TestWriteLines.cs new file mode 100644 index 0000000..7bf1318 --- /dev/null +++ b/Sshfs/FSTester/TestWriteLines.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace FSTester +{ + public class TestWriteLines : Test + { + public TestWriteLines(string workPath) : base(workPath) {} + + override public bool Go() + { + if (!base.Go()) + { + return false; + } + + string tmp = Guid.NewGuid().ToString() + ".tmp"; + + FileStream f = File.Open(this.workingPath + " \\ " + tmp, FileMode.OpenOrCreate); + string s = "ABCD" + "\n"; + byte[] sb = ASCIIEncoding.UTF8.GetBytes(s); + f.Write(sb, 0, sb.Length); + f.Seek(1, SeekOrigin.Begin); + f.Write(sb, 0, sb.Length); + + f.Flush(); + f.Seek(0, SeekOrigin.Begin); + byte[] data = new byte[4096]; + int received = f.Read(data, 0, data.Length); + string ret = ASCIIEncoding.UTF8.GetString(data, 0, received); + + if (ret == "A" + s) + { + return true; + } + + this.lastError = "Got "+ret+", waited for "+ "A"+s; + return false; + } + + + } +} diff --git a/Sshfs/Sshfs.sln b/Sshfs/Sshfs.sln index 5812a1f..dacacc3 100644 --- a/Sshfs/Sshfs.sln +++ b/Sshfs/Sshfs.sln @@ -1,95 +1,129 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sshfs", "Sshfs\Sshfs.csproj", "{FF4FC8BB-91A3-45FF-8449-650647610394}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet", "..\Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - DebugNoTimeout|Any CPU = DebugNoTimeout|Any CPU - DebugNoTimeout|Mixed Platforms = DebugNoTimeout|Mixed Platforms - DebugNoTimeout|x64 = DebugNoTimeout|x64 - DebugNoTimeout|x86 = DebugNoTimeout|x86 - Release|Any CPU = Release|Any CPU - Release|Mixed Platforms = Release|Mixed Platforms - Release|x64 = Release|x64 - Release|x86 = Release|x86 - ReleaseFW2|Any CPU = ReleaseFW2|Any CPU - ReleaseFW2|Mixed Platforms = ReleaseFW2|Mixed Platforms - ReleaseFW2|x64 = ReleaseFW2|x64 - ReleaseFW2|x86 = ReleaseFW2|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Any CPU.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Any CPU.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x64.ActiveCfg = Debug|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x64.Build.0 = Debug|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x64.ActiveCfg = Debug|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x64.Build.0 = Debug|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.ActiveCfg = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.Build.0 = Debug|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Any CPU.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.Build.0 = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x64.ActiveCfg = Release|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x64.Build.0 = Release|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.Build.0 = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Any CPU.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.Build.0 = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x64.ActiveCfg = Release|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x64.Build.0 = Release|x64 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.ActiveCfg = Release|x86 - {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.Build.0 = Release|x86 - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sshfs", "Sshfs\Sshfs.csproj", "{FF4FC8BB-91A3-45FF-8449-650647610394}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet", "..\Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSTester", "FSTester\FSTester.csproj", "{1B5533B8-DFCD-4F19-9EAB-438633E7C00B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + DebugNoTimeout|Any CPU = DebugNoTimeout|Any CPU + DebugNoTimeout|Mixed Platforms = DebugNoTimeout|Mixed Platforms + DebugNoTimeout|x64 = DebugNoTimeout|x64 + DebugNoTimeout|x86 = DebugNoTimeout|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x64 = Release|x64 + Release|x86 = Release|x86 + ReleaseFW2|Any CPU = ReleaseFW2|Any CPU + ReleaseFW2|Mixed Platforms = ReleaseFW2|Mixed Platforms + ReleaseFW2|x64 = ReleaseFW2|x64 + ReleaseFW2|x86 = ReleaseFW2|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Any CPU.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x64.ActiveCfg = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x64.Build.0 = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Debug|x86.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x64.ActiveCfg = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x64.Build.0 = Debug|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.ActiveCfg = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.DebugNoTimeout|x86.Build.0 = Debug|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Any CPU.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|Mixed Platforms.Build.0 = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x64.ActiveCfg = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x64.Build.0 = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.Release|x86.Build.0 = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Any CPU.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|Mixed Platforms.Build.0 = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x64.ActiveCfg = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x64.Build.0 = Release|x64 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.ActiveCfg = Release|x86 + {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.Build.0 = Release|x86 + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|x64.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|x64.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|x86.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|x86.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|Any CPU.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|x64.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|x64.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|x86.ActiveCfg = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.DebugNoTimeout|x86.Build.0 = Debug|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|Any CPU.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|x64.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|x64.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|x86.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Release|x86.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|Any CPU.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|Any CPU.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|Mixed Platforms.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|x64.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|x64.Build.0 = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU + {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From 3bc325157f368d6768e66c99e810f0fe92ef4e9a Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 23 Feb 2016 21:17:36 +0100 Subject: [PATCH 102/134] read buffering refactorized --- Renci.SshNet/Sftp/SftpSession.cs | 23 +--- Sshfs/Sshfs/SftpContextStream.cs | 202 ++++++++++++++++++++++++++----- 2 files changed, 180 insertions(+), 45 deletions(-) diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs index b7f0a74..dc1a735 100644 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ b/Renci.SshNet/Sftp/SftpSession.cs @@ -367,37 +367,24 @@ internal byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length) /// The offset. /// The length. /// data array; null if EOF - internal bool RequestReadAsync(byte[] handle, UInt64 offset, UInt32 length, EventWaitHandle wait, Action readCompleted) + internal bool RequestReadAsync(byte[] handle, UInt64 offset, UInt32 length, Action readCompleted) { SshException exception = null; var request = new SftpReadRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, length, (response) =>//data { - if (readCompleted != null) - { readCompleted(response); - } - if (wait != null) - wait.Set(); }, - (response) =>//status + (response) =>//status, eof, no data in area { - exception = this.GetSftpException(response); - if (wait != null) - wait.Set(); + SftpDataResponse dataResponse = new SftpDataResponse(response.ProtocolVersion); + dataResponse.Data = null; + readCompleted(dataResponse); }); this.SendRequest(request); - //if (wait != null) - // this.WaitOnHandle(wait, this._operationTimeout); - - if (exception != null) - { - throw exception; - } - return true; } diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs index c3576ab..299d59f 100644 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -21,14 +21,30 @@ using Renci.SshNet.Sftp; using Renci.SshNet.Common; using System.Collections.Generic; +using System.Diagnostics; namespace Sshfs { internal sealed class SftpContextStream : Stream { + /// + /// Effective size of readRequest. + /// + private int optimalReadRequestSize; + private byte[] readBuffer; + /// + /// Position of readBuffer data in source stream + /// + private long readBufferPosition; + /// + /// Valid count of data in read buffer + /// + private int readBufferCount; + private bool readBufferIsAtEOF; private int WRITE_BUFFER_SIZE; - //private int READ_BUFFER_SIZE; + + private readonly byte[] _writeBuffer; private readonly SftpSession _session; @@ -91,8 +107,11 @@ internal SftpContextStream(SftpSession session, string path, FileMode mode, File _attributes = attributes ?? _session.RequestFStat(_handle); - WRITE_BUFFER_SIZE = (int)session.CalculateOptimalWriteLength(65536, _handle); - //READ_BUFFER_SIZE = (int)session.CalculateOptimalReadLength(65536); + this.optimalReadRequestSize = checked((int)this._session.CalculateOptimalReadLength(uint.MaxValue)); + this.readBuffer = new byte[this.optimalReadRequestSize]; + this.readBufferCount = 0; + + WRITE_BUFFER_SIZE = (int)_session.CalculateOptimalWriteLength(65536, _handle); if (access.HasFlag(FileAccess.Write)) { @@ -187,60 +206,189 @@ public override void Flush() } } - private void ReadToBufferAsync( - long position, int count, - byte[] buffer, int bufferOffset, - EventWaitHandle wait, Action Received) + /// + /// Asynchronous read, will copy data to buffer and call Received. + /// + /// + /// + /// + /// + /// + private void ReadAsync(long position, int count, byte[] buffer, int bufferOffset, Action Received) { - _session.RequestReadAsync(_handle, (ulong)(position), checked((uint)count), wait, +#if DEBUG + Debug.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + " Sending Async read for offset " + bufferOffset); +#endif + this._session.RequestReadAsync(_handle, (ulong)(position), (uint)count, response => { - lock (buffer) +#if DEBUG + Debug.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\t" + " Got data for offset " + bufferOffset); +#endif + if (response.Data != null) { - Buffer.BlockCopy(response.Data, 0, buffer, bufferOffset, response.Data.Length); + lock (buffer) + { + Buffer.BlockCopy(response.Data, 0, buffer, bufferOffset, response.Data.Length); + Received(response.Data.Length); + } + } + else + { + Received(0); } - Received(response.Data.Length); - //wait.Set(); } ); } + /// + /// Normal read + /// + /// + /// + /// + /// + /// Length of received data + private int ReadSync(long position, int count, byte[] buffer, int bufferOffset) + { + byte[] data = this._session.RequestRead(this._handle, (ulong)position, (uint)count); + Buffer.BlockCopy(data, 0, buffer, bufferOffset, data.Length); + return data.Length; + } - public override int Read(byte[] buffer, int bufferOffset, int bufferCount) + /// + /// Update read buffer state with data at position + /// + /// + private void ReadDataToBufferSync(long position) { - // Set up for the read operation. - SetupRead(); + this.readBufferCount = this.ReadSync(position, this.optimalReadRequestSize, this.readBuffer, 0); + this.readBufferPosition = position; + this.readBufferIsAtEOF = this.readBufferCount != this.optimalReadRequestSize || this.readBufferCount == 0; + } + + private void ReadDataToBufferASync(long position) + { + this.ReadAsync(position, this.readBuffer.Length, this.readBuffer, 0, + received => { + this.readBufferPosition = position; + this.readBufferCount = received; + }); + } + + /// + /// Tryes to satisfy request from readBuffer. Returns count of bytes that hits the buffer. + /// + /// + /// + /// + /// + /// true if buffer war read to the end and its also EOF + /// + private int getDataFromBuffer(long position, int count, byte[] dst, int dstOffset, ref bool isEOF) + { + if ((position >= this.readBufferPosition) && + (position < this.readBufferPosition + this.readBufferCount)) //atleast partial hit + { + int hitPosition = (int)(position - this.readBufferPosition); + int hitLength = Math.Min(count, this.readBufferCount - hitPosition); + Buffer.BlockCopy(this.readBuffer, hitPosition, dst, dstOffset, hitLength); + + isEOF = (this.readBufferIsAtEOF) && (hitPosition + hitLength == this.readBufferCount); + return hitLength; + } + return 0; + } + + /// + /// + /// + /// Position to read from + /// Count of bytes, unlimited + /// Destination buffer + /// Offset in destination buffer to write from + /// Count of bytes readed to dst + public int getDataAt(long position, int count, byte[] dst, int dstOffset = 0) + { + int received = 0; + + bool isEOF = false; + int hitLength = this.getDataFromBuffer(position, count, dst, dstOffset, ref isEOF); + if (hitLength > 0) + { +#if DEBUG + Console.WriteLine("Readbuffer hit " + hitLength); +#endif + received += hitLength; + count -= hitLength; + position += hitLength; + dstOffset += hitLength; + if (isEOF || count == 0) //nothing remains + { + _position += received; + return received; + } + } + + //small request, we want to load more data than requested and store them in buffer: + if (count < this.optimalReadRequestSize) + { + this.ReadDataToBufferSync(position); + hitLength = this.getDataFromBuffer(position, count, dst, dstOffset, ref isEOF); + + _position = position + received + hitLength; + return received + hitLength; + } + + + //request with big buffers remains: List waits = new List(); - int READ_BUFFER_SIZE = checked((int)this._session.CalculateOptimalReadLength(65536-13)); - int readCount = bufferCount; + int readCount = count; int winOffset = 0; int receivedTotal = 0; - +#if DEBUG + DateTime startTime = DateTime.Now; +#endif while (readCount > 0) { - int winSize = readCount > READ_BUFFER_SIZE ? READ_BUFFER_SIZE : readCount; + int winSize = readCount > this.optimalReadRequestSize ? this.optimalReadRequestSize : readCount; EventWaitHandle wait = new AutoResetEvent(false); wait.Reset(); waits.Add(wait); - this.ReadToBufferAsync( - _position + winOffset, winSize, - buffer, bufferOffset + winOffset, - wait, received => { Interlocked.Add(ref receivedTotal, received); } + this.ReadAsync( + position + winOffset, winSize, + dst, dstOffset + winOffset, + receivedStatus => { + Interlocked.Add(ref receivedTotal, receivedStatus); + wait.Set(); + } ); winOffset += winSize; readCount -= winSize; } - if (!WaitHandle.WaitAll(waits.ToArray(), 20000)) { + if (!WaitHandle.WaitAll(waits.ToArray(), 20000)) + { throw new SshOperationTimeoutException("Timeout on wait"); } - - _position = _position + receivedTotal; - return receivedTotal; +#if DEBUG + int rrTime = (DateTime.Now - startTime).Milliseconds; + Console.WriteLine(rrTime+" with "+waits.Count.ToString()); +#endif + _position = _position + received + receivedTotal; + return received + receivedTotal; + } + + + public override int Read(byte[] buffer, int bufferOffset, int bufferCount) + { + // Set up for the read operation. + SetupRead(); + return this.getDataAt(this._position, bufferCount, buffer, bufferOffset); } From d729c0008923d827ae959b2767f12d3e9610640f Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 23 Feb 2016 22:40:07 +0100 Subject: [PATCH 103/134] fix dokan version parsing --- Sshfs/Sshfs/AboutForm.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/AboutForm.cs b/Sshfs/Sshfs/AboutForm.cs index 9c228a6..1c043a6 100644 --- a/Sshfs/Sshfs/AboutForm.cs +++ b/Sshfs/Sshfs/AboutForm.cs @@ -10,7 +10,10 @@ namespace Sshfs public partial class AboutForm : Form { private static readonly string _sshfsText = String.Format("Sshfs {0}", Assembly.GetEntryAssembly().GetName().Version); - private static readonly string _dokanText = String.Format("Dokan {0}.{1}.{2}.{3}",DokanNet.Dokan.Version / 1000, (DokanNet.Dokan.Version%1000) / 100, (DokanNet.Dokan.Version%100) / 10, DokanNet.Dokan.Version % 10); + private static readonly string _dokanText = + (DokanNet.Dokan.Version < 600) + ? String.Format("Dokan {0}.{1}.{2}", DokanNet.Dokan.Version / 100, (DokanNet.Dokan.Version % 100) / 10, DokanNet.Dokan.Version % 10) + : String.Format("Dokan {0}.{1}.{2}.{3}", DokanNet.Dokan.Version / 1000, (DokanNet.Dokan.Version % 1000) / 100, (DokanNet.Dokan.Version % 100) / 10, DokanNet.Dokan.Version % 10); private static readonly string _sshnetText = String.Format("SSH.NET {0}", Assembly.GetAssembly(typeof (SshClient)).GetName().Version); public AboutForm() From 00a7fc858afa7927eabe57d14de48f5888adfc94 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 23 Feb 2016 22:43:51 +0100 Subject: [PATCH 104/134] release 1.6.0 RC2 * read speed improvement, significant with higher latency networks --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 ++++---- version.hook.sh | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 4d90f4c..4fc529f 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows ™")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("4every1 s.r.o.")] -[assembly: AssemblyProduct("WinSshFS 4every1 edition 1.6.0.0-0")] +[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.0.7-devel")] [assembly: AssemblyCopyright("Copyright © 4every1 s.r.o.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.0.0")] -[assembly: AssemblyVersion("1.6.0.0")] -[assembly: AssemblyFileVersion("1.6.0.0")] +// [assembly: AssemblyVersion("1.6.0.7")] +[assembly: AssemblyVersion("1.6.0.7")] +[assembly: AssemblyFileVersion("1.6.0.7")] diff --git a/version.hook.sh b/version.hook.sh index 9383d35..331cca7 100644 --- a/version.hook.sh +++ b/version.hook.sh @@ -5,10 +5,11 @@ #This file needs to be place in the .git/hooks/ folder and only works when a git pull is #made which contains changes in the remote repo. -PRODUCT="WinSshFS 4every1 edition" +PRODUCT="WinSshFS Foreveryone edition" #get the latest tag info. The 'always' flag will give you a shortened SHA1 if no tag exists. tag=$(git describe --tags --long) +BRANCH=$(git rev-parse --abbrev-ref HEAD) #tag="A.B.C.D-X-hash" echo $tag @@ -34,6 +35,6 @@ then #Update the AssemblyVersion and AssemblyFileVersion attribute with the 'version' sed -i.bak "s/\AssemblyVersion(\".*\")/AssemblyVersion(\"$version\")/g" $AI 2>/dev/null sed -i.bak "s/\AssemblyFileVersion(\".*\")/AssemblyFileVersion(\"$version\")/g" $AI 2>/dev/null - sed -i.bak "s/AssemblyProduct(\".*\")/AssemblyProduct(\"$PRODUCT $version-${COMMITS[0]}\")/g" $AI 2>/dev/null + sed -i.bak "s/AssemblyProduct(\".*\")/AssemblyProduct(\"$PRODUCT $version-${BRANCH}\")/g" $AI 2>/dev/null #cat $AI fi From 91514cbb37a8f8f31dfa4b4b4c3f61609f5a6a19 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Tue, 23 Feb 2016 23:44:03 +0100 Subject: [PATCH 105/134] Report wrong config files, create backup config files --- Sshfs/Sshfs/MainForm.cs | 16 +++++++++++++--- Sshfs/Sshfs/Utilities.cs | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index 691055d..3bb2fba 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -117,8 +117,12 @@ protected override void OnLoad(EventArgs e) // _drives.Persist("config.xml",true); - - virtualDrive = virtualDrive.Load("vfs.xml"); + try { + virtualDrive = virtualDrive.Load("vfs.xml"); + } + catch { + MessageBox.Show("Unable to load virtual drive info xml file."); + } if (virtualDrive == null) { virtualDrive = new VirtualDrive @@ -133,7 +137,13 @@ protected override void OnLoad(EventArgs e) buttonVFSupdate(); - _drives.Load("config.xml"); + try { + _drives.Load("config.xml"); + } + catch + { + MessageBox.Show("Unable to load config file."); + } driveListView.BeginUpdate(); diff --git a/Sshfs/Sshfs/Utilities.cs b/Sshfs/Sshfs/Utilities.cs index e56b40d..6a9b5af 100644 --- a/Sshfs/Sshfs/Utilities.cs +++ b/Sshfs/Sshfs/Utilities.cs @@ -53,6 +53,21 @@ public static T Load(this T obj, string file) where T : ISerializable } } + private static void doBackups(string filepath) + { + if (File.Exists(filepath)) + { + string bak = filepath + "~bak"; + if (!File.Exists(bak)) + { + File.Move(filepath, bak); + } + else { + File.Replace(filepath, bak, bak + "Prev", true); + } + } + } + public static void Persist(this List list, string file, bool delete = false) where T : ISerializable { @@ -63,6 +78,8 @@ public static void Persist(this List list, string file, bool delete = fals } else { + doBackups(filepath); + var xmlSerializer = new DataContractSerializer(typeof (List)); using ( var stream = File.Open(filepath, FileMode.Create, @@ -83,12 +100,15 @@ public static void Persist(this T obj, string file, bool delete = false) wher } else { + doBackups(filepath); + var xmlSerializer = new DataContractSerializer(typeof(List)); using ( var stream = File.Open(filepath, FileMode.Create, FileAccess.Write)) { xmlSerializer.WriteObject(stream, obj); + stream.Close(); } } } From 6035e8bc0e510bcb32fa0dcab6d98ddfb25214fe Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 24 Feb 2016 01:38:17 +0100 Subject: [PATCH 106/134] appveyor versioning --- Sshfs/appveyor.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Sshfs/appveyor.yml diff --git a/Sshfs/appveyor.yml b/Sshfs/appveyor.yml new file mode 100644 index 0000000..47c9187 --- /dev/null +++ b/Sshfs/appveyor.yml @@ -0,0 +1,9 @@ +version: 1.6.0.{build} + +# enable patching of AssemblyInfo.* files +assembly_info: + patch: true + file: AssemblyInfo.* + assembly_version: "1.6.0.{build}" + assembly_file_version: "{version}" + assembly_informational_version: "{version}" \ No newline at end of file From 9a6d90d9c14367f52538fd540718dbe8a9c2254f Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 1 Aug 2016 22:01:35 +0200 Subject: [PATCH 107/134] FSTester blind --- Sshfs/FSTester/FSTester.csproj | 5 +- Sshfs/FSTester/Lowlevel.cs | 61 ++++++++++++++++++ Sshfs/FSTester/Program.cs | 5 +- Sshfs/FSTester/Test.cs | 5 ++ .../FSTester/{ => Tests/IO}/TestWriteLines.cs | 4 +- .../FSTester/Tests/Others/BackupAndShared.cs | 64 +++++++++++++++++++ Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 +-- 7 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 Sshfs/FSTester/Lowlevel.cs rename Sshfs/FSTester/{ => Tests/IO}/TestWriteLines.cs (89%) create mode 100644 Sshfs/FSTester/Tests/Others/BackupAndShared.cs diff --git a/Sshfs/FSTester/FSTester.csproj b/Sshfs/FSTester/FSTester.csproj index 8ee9726..653d172 100644 --- a/Sshfs/FSTester/FSTester.csproj +++ b/Sshfs/FSTester/FSTester.csproj @@ -43,14 +43,17 @@ + - + + + - Debug - AnyCPU - 2.0 - {f4b5a704-a7e7-4719-a343-ede32568762b} - 1.9.5.0 - - Documentation - Documentation - Documentation - - .\Help\ - SshNet.Help - en-US - Standard - Blank - False - VS2010 - False - Guid - SSH .NET Client Library Documentation - AboveNamespaces - - - - OnlyWarningsAndErrors - HtmlHelp1 - False - .NET Framework 4.0 - True - False - False - True - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Renci.SshNet/ExpectAction.cs b/Renci.SshNet/ExpectAction.cs deleted file mode 100644 index 10f5fd1..0000000 --- a/Renci.SshNet/ExpectAction.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace Renci.SshNet -{ - /// - /// Specifies behavior for expected expression - /// - public class ExpectAction - { - /// - /// Gets the expected regular expression. - /// - public Regex Expect { get; private set; } - - /// - /// Gets the action to perform when expected expression is found. - /// - public Action Action { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The expect regular expression. - /// The action to perform. - /// or is null. - public ExpectAction(Regex expect, Action action) - { - if (expect == null) - throw new ArgumentNullException("expect"); - - if (action == null) - throw new ArgumentNullException("action"); - - this.Expect = expect; - this.Action = action; - } - - /// - /// Initializes a new instance of the class. - /// - /// The expect expression. - /// The action to perform. - /// or is null. - public ExpectAction(string expect, Action action) - { - if (expect == null) - throw new ArgumentNullException("expect"); - - if (action == null) - throw new ArgumentNullException("action"); - - this.Expect = new Regex(Regex.Escape(expect)); - this.Action = action; - } - } -} diff --git a/Renci.SshNet/ExpectAsyncResult.cs b/Renci.SshNet/ExpectAsyncResult.cs deleted file mode 100644 index 9d9c6d4..0000000 --- a/Renci.SshNet/ExpectAsyncResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Renci.SshNet.Common; -using System; - -namespace Renci.SshNet -{ - /// - /// Provides additional information for asynchronous command execution - /// - public class ExpectAsyncResult : AsyncResult - { - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - internal ExpectAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - } - } -} diff --git a/Renci.SshNet/ForwardedPort.cs b/Renci.SshNet/ForwardedPort.cs deleted file mode 100644 index 45ae6c8..0000000 --- a/Renci.SshNet/ForwardedPort.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using Renci.SshNet.Common; - -namespace Renci.SshNet -{ - /// - /// Base class for port forwarding functionality. - /// - public abstract class ForwardedPort - { - /// - /// Gets or sets the session. - /// - /// - /// The session. - /// - internal Session Session { get; set; } - - /// - /// Gets or sets a value indicating whether port forwarding started. - /// - /// - /// true if port forwarding started; otherwise, false. - /// - public bool IsStarted { get; protected set; } - - /// - /// Occurs when exception is thrown. - /// - public event EventHandler Exception; - - /// - /// Occurs when port forwarding request received. - /// - public event EventHandler RequestReceived; - - /// - /// Starts port forwarding. - /// - public virtual void Start() - { - if (this.Session == null) - { - throw new InvalidOperationException("Session property is null."); - } - - if (!this.Session.IsConnected) - { - throw new SshConnectionException("Not connected."); - } - - this.Session.ErrorOccured += Session_ErrorOccured; - } - - /// - /// Stops port forwarding. - /// - public virtual void Stop() - { - if (this.Session != null) - { - this.Session.ErrorOccured -= Session_ErrorOccured; - } - } - - /// - /// Raises event. - /// - /// The exception. - protected void RaiseExceptionEvent(Exception execption) - { - if (this.Exception != null) - { - this.Exception(this, new ExceptionEventArgs(execption)); - } - } - - /// - /// Raises event. - /// - /// Request originator host. - /// Request originator port. - protected void RaiseRequestReceived(string host, uint port) - { - if (this.RequestReceived != null) - { - this.RequestReceived(this, new PortForwardEventArgs(host, port)); - } - } - - /// - /// Handles session ErrorOccured event. - /// - /// The source of the event. - /// The instance containing the event data. - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - this.RaiseExceptionEvent(e.Exception); - } - } -} diff --git a/Renci.SshNet/ForwardedPortDynamic.NET.cs b/Renci.SshNet/ForwardedPortDynamic.NET.cs deleted file mode 100644 index 3e44e6e..0000000 --- a/Renci.SshNet/ForwardedPortDynamic.NET.cs +++ /dev/null @@ -1,281 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; - -namespace Renci.SshNet -{ - public partial class ForwardedPortDynamic - { - private TcpListener _listener; - private readonly object _listenerLocker = new object(); - - partial void InternalStart() - { - // If port already started don't start it again - if (this.IsStarted) - return; - - var ip = IPAddress.Any; - if (!string.IsNullOrEmpty(this.BoundHost)) - { - ip = this.BoundHost.GetIPAddress(); - } - - var ep = new IPEndPoint(ip, (int)this.BoundPort); - - this._listener = new TcpListener(ep); - this._listener.Start(); - - this._listenerTaskCompleted = new ManualResetEvent(false); - this.ExecuteThread(() => - { - try - { - while (true) - { - lock (this._listenerLocker) - { - if (this._listener == null) - break; - } - - var socket = this._listener.AcceptSocket(); - - this.ExecuteThread(() => - { - try - { - using (var channel = this.Session.CreateClientChannel()) - { - var version = new byte[1]; - - socket.Receive(version); - - if (version[0] == 4) - { - this.HandleSocks4(socket, channel); - } - else if (version[0] == 5) - { - this.HandleSocks5(socket, channel); - } - else - { - throw new NotSupportedException(string.Format("SOCKS version {0} is not supported.", version)); - } - - channel.Bind(); - - channel.Close(); - } - } - catch (Exception exp) - { - this.RaiseExceptionEvent(exp); - } - }); - } - } - catch (SocketException exp) - { - if (!(exp.SocketErrorCode == SocketError.Interrupted)) - { - this.RaiseExceptionEvent(exp); - } - } - catch (Exception exp) - { - this.RaiseExceptionEvent(exp); - } - finally - { - this._listenerTaskCompleted.Set(); - } - }); - - this.IsStarted = true; - } - - partial void InternalStop() - { - // If port not started you cant stop it - if (!this.IsStarted) - return; - - lock (this._listenerLocker) - { - this._listener.Stop(); - this._listener = null; - } - this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout); - this._listenerTaskCompleted.Dispose(); - this._listenerTaskCompleted = null; - this.IsStarted = false; - } - - private void HandleSocks4(Socket socket, ChannelDirectTcpip channel) - { - using (var stream = new NetworkStream(socket)) - { - var commandCode = stream.ReadByte(); - // TODO: See what need to be done depends on the code - - var portBuffer = new byte[2]; - stream.Read(portBuffer, 0, portBuffer.Length); - var port = (uint)(portBuffer[0] * 256 + portBuffer[1]); - - var ipBuffer = new byte[4]; - stream.Read(ipBuffer, 0, ipBuffer.Length); - var ipAddress = new IPAddress(ipBuffer); - - var username = ReadString(stream); - - var host = ipAddress.ToString(); - - this.RaiseRequestReceived(host, port); - - channel.Open(host, port, socket); - - stream.WriteByte(0x00); - - if (channel.IsOpen) - { - stream.WriteByte(0x5a); - } - else - { - stream.WriteByte(0x5b); - } - - stream.Write(portBuffer, 0, portBuffer.Length); - stream.Write(ipBuffer, 0, ipBuffer.Length); - } - } - - private void HandleSocks5(Socket socket, ChannelDirectTcpip channel) - { - using (var stream = new NetworkStream(socket)) - { - var authenticationMethodsCount = stream.ReadByte(); - - var authenticationMethods = new byte[authenticationMethodsCount]; - stream.Read(authenticationMethods, 0, authenticationMethods.Length); - - stream.WriteByte(0x05); - - if (authenticationMethods.Min() == 0) - { - stream.WriteByte(0x00); - } - else - { - stream.WriteByte(0xFF); - } - - var version = stream.ReadByte(); - - if (version != 5) - throw new ProxyException("SOCKS5: Version 5 is expected."); - - var commandCode = stream.ReadByte(); - - if (stream.ReadByte() != 0) - { - throw new ProxyException("SOCKS5: 0 is expected."); - } - - var addressType = stream.ReadByte(); - - IPAddress ipAddress; - byte[] addressBuffer; - switch (addressType) - { - case 0x01: - { - addressBuffer = new byte[4]; - stream.Read(addressBuffer, 0, 4); - - ipAddress = new IPAddress(addressBuffer); - } - break; - case 0x03: - { - var length = stream.ReadByte(); - addressBuffer = new byte[length]; - stream.Read(addressBuffer, 0, addressBuffer.Length); - - ipAddress = IPAddress.Parse(new Common.ASCIIEncoding().GetString(addressBuffer)); - } - break; - case 0x04: - { - addressBuffer = new byte[16]; - stream.Read(addressBuffer, 0, 16); - - ipAddress = new IPAddress(addressBuffer); - } - break; - default: - throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType)); - } - - var portBuffer = new byte[2]; - stream.Read(portBuffer, 0, portBuffer.Length); - var port = (uint)(portBuffer[0] * 256 + portBuffer[1]); - var host = ipAddress.ToString(); - - this.RaiseRequestReceived(host, port); - - channel.Open(host, port, socket); - - stream.WriteByte(0x05); - - if (channel.IsOpen) - { - stream.WriteByte(0x00); - } - else - { - stream.WriteByte(0x01); - } - - stream.WriteByte(0x00); - - var buffer = ipAddress.GetAddressBytes(); - - if (ipAddress.AddressFamily == AddressFamily.InterNetwork) - { - stream.WriteByte(0x01); - } - else if (ipAddress.AddressFamily == AddressFamily.InterNetwork) - { - stream.WriteByte(0x04); - } - else - { - throw new NotSupportedException("Not supported address family."); - } - - stream.Write(buffer, 0, buffer.Length); - stream.Write(portBuffer, 0, portBuffer.Length); - } - } - - private static string ReadString(NetworkStream stream) - { - StringBuilder text = new StringBuilder(); - var aa = (char)stream.ReadByte(); - while (aa != 0) - { - text.Append(aa); - aa = (char)stream.ReadByte(); - } - return text.ToString(); - } - } -} diff --git a/Renci.SshNet/ForwardedPortDynamic.NET40.cs b/Renci.SshNet/ForwardedPortDynamic.NET40.cs deleted file mode 100644 index 3c1f952..0000000 --- a/Renci.SshNet/ForwardedPortDynamic.NET40.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - public partial class ForwardedPortDynamic - { - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/ForwardedPortDynamic.cs b/Renci.SshNet/ForwardedPortDynamic.cs deleted file mode 100644 index a8038b0..0000000 --- a/Renci.SshNet/ForwardedPortDynamic.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for dynamic port forwarding - /// - public partial class ForwardedPortDynamic : ForwardedPort, IDisposable - { - private EventWaitHandle _listenerTaskCompleted; - - /// - /// Gets the bound host. - /// - public string BoundHost { get; protected set; } - - /// - /// Gets the bound port. - /// - public uint BoundPort { get; protected set; } - - /// - /// Initializes a new instance of the class. - /// - /// The port. - public ForwardedPortDynamic(uint port) - : this(string.Empty, port) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The host. - /// The port. - public ForwardedPortDynamic(string host, uint port) - { - this.BoundHost = host; - this.BoundPort = port; - } - - /// - /// Starts local port forwarding. - /// - public override void Start() - { - this.InternalStart(); - } - - /// - /// Stops local port forwarding. - /// - public override void Stop() - { - base.Stop(); - - this.InternalStop(); - } - - partial void InternalStart(); - - partial void InternalStop(); - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - this.InternalStop(); - - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._listenerTaskCompleted != null) - { - this._listenerTaskCompleted.Dispose(); - this._listenerTaskCompleted = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~ForwardedPortDynamic() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/ForwardedPortLocal.NET.cs b/Renci.SshNet/ForwardedPortLocal.NET.cs deleted file mode 100644 index db709f1..0000000 --- a/Renci.SshNet/ForwardedPortLocal.NET.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Net.Sockets; -using System.Net; -using System.Threading; -using Renci.SshNet.Channels; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for local port forwarding - /// - public partial class ForwardedPortLocal - { - private TcpListener _listener; - private readonly object _listenerLocker = new object(); - - partial void InternalStart() - { - // If port already started don't start it again - if (this.IsStarted) - return; - - IPAddress addr = this.BoundHost.GetIPAddress(); - var ep = new IPEndPoint(addr, (int)this.BoundPort); - - this._listener = new TcpListener(ep); - this._listener.Start(); - // Update bound port if original was passed as zero - this.BoundPort = (uint)((IPEndPoint)_listener.LocalEndpoint).Port; - - this.Session.ErrorOccured += Session_ErrorOccured; - this.Session.Disconnected += Session_Disconnected; - - this._listenerTaskCompleted = new ManualResetEvent(false); - this.ExecuteThread(() => - { - try - { - while (true) - { - lock (this._listenerLocker) - { - if (this._listener == null) - break; - } - - var socket = this._listener.AcceptSocket(); - - this.ExecuteThread(() => - { - try - { - IPEndPoint originatorEndPoint = socket.RemoteEndPoint as IPEndPoint; - - this.RaiseRequestReceived(originatorEndPoint.Address.ToString(), (uint)originatorEndPoint.Port); - - using (var channel = this.Session.CreateClientChannel()) - { - channel.Open(this.Host, this.Port, socket); - - channel.Bind(); - - channel.Close(); - } - } - catch (Exception exp) - { - this.RaiseExceptionEvent(exp); - } - }); - } - } - catch (SocketException exp) - { - if (!(exp.SocketErrorCode == SocketError.Interrupted)) - { - this.RaiseExceptionEvent(exp); - } - } - catch (Exception exp) - { - this.RaiseExceptionEvent(exp); - } - finally - { - this._listenerTaskCompleted.Set(); - } - }); - - this.IsStarted = true; - } - - partial void InternalStop() - { - // If port not started you cant stop it - if (!this.IsStarted) - return; - - this.Session.Disconnected -= Session_Disconnected; - this.Session.ErrorOccured -= Session_ErrorOccured; - - this.StopListener(); - - this._listenerTaskCompleted.WaitOne(this.Session.ConnectionInfo.Timeout); - this._listenerTaskCompleted.Dispose(); - this._listenerTaskCompleted = null; - - this.IsStarted = false; - } - - private void StopListener() - { - lock (this._listenerLocker) - { - if (this._listener != null) - { - this._listener.Stop(); - this._listener = null; - } - } - } - - private void Session_ErrorOccured(object sender, Common.ExceptionEventArgs e) - { - this.StopListener(); - } - - private void Session_Disconnected(object sender, EventArgs e) - { - this.StopListener(); - } - } -} diff --git a/Renci.SshNet/ForwardedPortLocal.NET40.cs b/Renci.SshNet/ForwardedPortLocal.NET40.cs deleted file mode 100644 index 08234d3..0000000 --- a/Renci.SshNet/ForwardedPortLocal.NET40.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for local port forwarding - /// - public partial class ForwardedPortLocal - { - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/ForwardedPortLocal.cs b/Renci.SshNet/ForwardedPortLocal.cs deleted file mode 100644 index 3a7c51d..0000000 --- a/Renci.SshNet/ForwardedPortLocal.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for local port forwarding - /// - public partial class ForwardedPortLocal : ForwardedPort, IDisposable - { - private EventWaitHandle _listenerTaskCompleted; - - /// - /// Gets the bound host. - /// - public string BoundHost { get; protected set; } - - /// - /// Gets the bound port. - /// - public uint BoundPort { get; protected set; } - - /// - /// Gets the forwarded host. - /// - public string Host { get; protected set; } - - /// - /// Gets the forwarded port. - /// - public uint Port { get; protected set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bound port. - /// The host. - /// The port. - /// - /// - /// - public ForwardedPortLocal(uint boundPort, string host, uint port) - : this(string.Empty, boundPort, host, port) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The bound host. - /// The host. - /// The port. - public ForwardedPortLocal(string boundHost, string host, uint port) - : this(boundHost, 0, host, port) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The bound host. - /// The bound port. - /// The host. - /// The port. - public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint port) - { - if (boundHost == null) - throw new ArgumentNullException("boundHost"); - - if (host == null) - throw new ArgumentNullException("host"); - - if (!boundHost.IsValidHost()) - throw new ArgumentException("boundHost"); - - if (!boundPort.IsValidPort()) - throw new ArgumentOutOfRangeException("boundPort"); - - if (!host.IsValidHost()) - throw new ArgumentException("host"); - - if (!port.IsValidPort()) - throw new ArgumentOutOfRangeException("port"); - - this.BoundHost = boundHost; - this.BoundPort = boundPort; - this.Host = host; - this.Port = port; - } - - /// - /// Starts local port forwarding. - /// - public override void Start() - { - this.InternalStart(); - } - - /// - /// Stops local port forwarding. - /// - public override void Stop() - { - base.Stop(); - - this.InternalStop(); - } - - partial void InternalStart(); - - partial void InternalStop(); - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - this.InternalStop(); - - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._listenerTaskCompleted != null) - { - this._listenerTaskCompleted.Dispose(); - this._listenerTaskCompleted = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~ForwardedPortLocal() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/ForwardedPortRemote.NET.cs b/Renci.SshNet/ForwardedPortRemote.NET.cs deleted file mode 100644 index 2582239..0000000 --- a/Renci.SshNet/ForwardedPortRemote.NET.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Net; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for remote port forwarding - /// - public partial class ForwardedPortRemote - { - /// - /// Initializes a new instance of the class. - /// - /// The bound port. - /// The host. - /// The port. - /// - /// - /// - public ForwardedPortRemote(uint boundPort, string host, uint port) - : this(string.Empty, boundPort, host, port) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The bound host. - /// The bound port. - /// The host. - /// The port. - public ForwardedPortRemote(string boundHost, uint boundPort, string host, uint port) - : this(Dns.GetHostEntry(boundHost).AddressList[0], boundPort, Dns.GetHostEntry(host).AddressList[0], port) - { - } - } -} diff --git a/Renci.SshNet/ForwardedPortRemote.NET40.cs b/Renci.SshNet/ForwardedPortRemote.NET40.cs deleted file mode 100644 index 6f66a85..0000000 --- a/Renci.SshNet/ForwardedPortRemote.NET40.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for remote port forwarding - /// - public partial class ForwardedPortRemote - { - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/ForwardedPortRemote.cs b/Renci.SshNet/ForwardedPortRemote.cs deleted file mode 100644 index 4fb0f87..0000000 --- a/Renci.SshNet/ForwardedPortRemote.cs +++ /dev/null @@ -1,249 +0,0 @@ -using System; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Messages.Connection; -using Renci.SshNet.Common; -using System.Globalization; -using System.Net; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for remote port forwarding - /// - public partial class ForwardedPortRemote : ForwardedPort, IDisposable - { - private bool _requestStatus; - - private EventWaitHandle _globalRequestResponse = new AutoResetEvent(false); - - /// - /// Gets the bound host. - /// - public IPAddress BoundHostAddress { get; protected set; } - - /// - /// Gets the bound host. - /// - public string BoundHost - { - get - { - return this.BoundHostAddress.ToString(); - } - } - - /// - /// Gets the bound port. - /// - public uint BoundPort { get; protected set; } - - /// - /// Gets the forwarded host. - /// - public IPAddress HostAddress { get; protected set; } - - /// - /// Gets the forwarded host. - /// - public string Host - { - get - { - return this.HostAddress.ToString(); - } - } - - /// - /// Gets the forwarded port. - /// - public uint Port { get; protected set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bound host address. - /// The bound port. - /// The host address. - /// The port. - /// is null. - /// is null. - /// is not a valid port. - /// is not a valid port. - public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress hostAddress, uint port) - { - if (boundHostAddress == null) - throw new ArgumentNullException("boundHostAddress"); - if (hostAddress == null) - throw new ArgumentNullException("hostAddress"); - if (!boundPort.IsValidPort()) - throw new ArgumentOutOfRangeException("boundPort"); - if (!port.IsValidPort()) - throw new ArgumentOutOfRangeException("port"); - - this.BoundHostAddress = boundHostAddress; - this.BoundPort = boundPort; - this.HostAddress = hostAddress; - this.Port = port; - } - - /// - /// Starts remote port forwarding. - /// - public override void Start() - { - base.Start(); - - // If port already started don't start it again - if (this.IsStarted) - return; - - this.Session.RegisterMessage("SSH_MSG_REQUEST_FAILURE"); - this.Session.RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); - this.Session.RegisterMessage("SSH_MSG_CHANNEL_OPEN"); - - this.Session.RequestSuccessReceived += Session_RequestSuccess; - this.Session.RequestFailureReceived += Session_RequestFailure; - this.Session.ChannelOpenReceived += Session_ChannelOpening; - - // Send global request to start direct tcpip - this.Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.TcpIpForward, true, this.BoundHost, this.BoundPort)); - - this.Session.WaitOnHandle(this._globalRequestResponse); - - if (!this._requestStatus) - { - // If request failed don't handle channel opening for this request - this.Session.ChannelOpenReceived -= Session_ChannelOpening; - - throw new SshException(string.Format(CultureInfo.CurrentCulture, "Port forwarding for '{0}' port '{1}' failed to start.", this.Host, this.Port)); - } - this.IsStarted = true; - } - - /// - /// Stops remote port forwarding. - /// - public override void Stop() - { - base.Stop(); - - // If port not started you cant stop it - if (!this.IsStarted) - return; - - // Send global request to cancel direct tcpip - this.Session.SendMessage(new GlobalRequestMessage(GlobalRequestName.CancelTcpIpForward, true, this.BoundHost, this.BoundPort)); - - this.Session.WaitOnHandle(this._globalRequestResponse); - - this.Session.RequestSuccessReceived -= Session_RequestSuccess; - this.Session.RequestFailureReceived -= Session_RequestFailure; - this.Session.ChannelOpenReceived -= Session_ChannelOpening; - - this.IsStarted = false; - } - - private void Session_ChannelOpening(object sender, MessageEventArgs e) - { - var channelOpenMessage = e.Message; - var info = channelOpenMessage.Info as ForwardedTcpipChannelInfo; - if (info != null) - { - // Ensure this is the corresponding request - if (info.ConnectedAddress == this.BoundHost && info.ConnectedPort == this.BoundPort) - { - this.ExecuteThread(() => - { - try - { - this.RaiseRequestReceived(info.OriginatorAddress, info.OriginatorPort); - - var channel = this.Session.CreateServerChannel( - channelOpenMessage.LocalChannelNumber, channelOpenMessage.InitialWindowSize, - channelOpenMessage.MaximumPacketSize); - channel.Bind(this.HostAddress, this.Port); - } - catch (Exception exp) - { - this.RaiseExceptionEvent(exp); - } - }); - } - } - } - - private void Session_RequestFailure(object sender, EventArgs e) - { - this._requestStatus = false; - this._globalRequestResponse.Set(); - } - - private void Session_RequestSuccess(object sender, MessageEventArgs e) - { - this._requestStatus = true; - if (this.BoundPort == 0) - { - this.BoundPort = (e.Message.BoundPort == null) ? 0 : e.Message.BoundPort.Value; - } - - this._globalRequestResponse.Set(); - } - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._globalRequestResponse != null) - { - this._globalRequestResponse.Dispose(); - this._globalRequestResponse = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~ForwardedPortRemote() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/HashInfo.cs b/Renci.SshNet/HashInfo.cs deleted file mode 100644 index 6405def..0000000 --- a/Renci.SshNet/HashInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Linq; -using System.Security.Cryptography; - -namespace Renci.SshNet -{ - /// - /// Holds information about key size and cipher to use - /// - public class HashInfo - { - /// - /// Gets the size of the key. - /// - /// - /// The size of the key. - /// - public int KeySize { get; private set; } - - /// - /// Gets the cipher. - /// - public Func HashAlgorithm { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Size of the key. - /// The hash algorithm to use for a given key. - public HashInfo(int keySize, Func hash) - { - this.KeySize = keySize; - this.HashAlgorithm = key => (hash(key.Take(this.KeySize / 8).ToArray())); - } - } -} diff --git a/Renci.SshNet/IAgentProtocol.cs b/Renci.SshNet/IAgentProtocol.cs deleted file mode 100644 index bdbce20..0000000 --- a/Renci.SshNet/IAgentProtocol.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Renci.SshNet -{ - public interface IAgentProtocol - { - IEnumerable GetIdentities(); - - byte[] SignData(IdentityReference identity, byte[] data); - } -} \ No newline at end of file diff --git a/Renci.SshNet/IdentityReference.cs b/Renci.SshNet/IdentityReference.cs deleted file mode 100644 index bf6a4f6..0000000 --- a/Renci.SshNet/IdentityReference.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Renci.SshNet -{ - public class IdentityReference - { - public string Type { get; private set; } - public byte[] Blob { get; private set; } - public string Comment { get; private set; } - - public IdentityReference(string type,byte[] blob,string comment ) - { - this.Type = type; - this.Blob = blob; - this.Comment = comment; - } - - } -} \ No newline at end of file diff --git a/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.NET40.cs b/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.NET40.cs deleted file mode 100644 index e64429b..0000000 --- a/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.NET40.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - public partial class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod - { - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs b/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs deleted file mode 100644 index 7e23766..0000000 --- a/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Common; - -namespace Renci.SshNet -{ - /// - /// Provides functionality to perform keyboard interactive authentication. - /// - public partial class KeyboardInteractiveAuthenticationMethod : AuthenticationMethod, IDisposable - { - private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - - private Session _session; - - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - - private Exception _exception; - - private readonly RequestMessage _requestMessage; - - /// - /// Gets authentication method name - /// - public override string Name - { - get { return this._requestMessage.MethodName; } - } - - /// - /// Occurs when server prompts for more authentication information. - /// - public event EventHandler AuthenticationPrompt; - - /// - /// Initializes a new instance of the class. - /// - /// The username. - /// is whitespace or null. - public KeyboardInteractiveAuthenticationMethod(string username) - : base(username) - { - this._requestMessage = new RequestMessageKeyboardInteractive(ServiceName.Connection, username); - } - - /// - /// Authenticates the specified session. - /// - /// The session to authenticate. - /// Result of authentication process. - public override AuthenticationResult Authenticate(Session session) - { - this._session = session; - - session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; - session.MessageReceived += Session_MessageReceived; - - session.RegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); - - session.SendMessage(this._requestMessage); - - session.WaitOnHandle(this._authenticationCompleted); - - session.UnRegisterMessage("SSH_MSG_USERAUTH_INFO_REQUEST"); - - - session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; - session.MessageReceived -= Session_MessageReceived; - - - if (this._exception != null) - { - throw this._exception; - } - - return this._authenticationResult; - } - - private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) - { - this._authenticationResult = AuthenticationResult.Success; - - this._authenticationCompleted.Set(); - } - - private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) - { - if (e.Message.PartialSuccess) - this._authenticationResult = AuthenticationResult.PartialSuccess; - else - this._authenticationResult = AuthenticationResult.Failure; - - // Copy allowed authentication methods - this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); - - this._authenticationCompleted.Set(); - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var informationRequestMessage = e.Message as InformationRequestMessage; - if (informationRequestMessage != null) - { - var eventArgs = new AuthenticationPromptEventArgs(this.Username, informationRequestMessage.Instruction, informationRequestMessage.Language, informationRequestMessage.Prompts); - - this.ExecuteThread(() => - { - try - { - if (this.AuthenticationPrompt != null) - { - this.AuthenticationPrompt(this, eventArgs); - } - - var informationResponse = new InformationResponseMessage(); - - foreach (var response in from r in eventArgs.Prompts orderby r.Id ascending select r.Response) - { - informationResponse.Responses.Add(response); - } - - // Send information response message - this._session.SendMessage(informationResponse); - } - catch (Exception exp) - { - this._exception = exp; - this._authenticationCompleted.Set(); - } - }); - } - } - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._authenticationCompleted != null) - { - this._authenticationCompleted.Dispose(); - this._authenticationCompleted = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~KeyboardInteractiveAuthenticationMethod() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - } -} diff --git a/Renci.SshNet/KeyboardInteractiveConnectionInfo.NET40.cs b/Renci.SshNet/KeyboardInteractiveConnectionInfo.NET40.cs deleted file mode 100644 index 271c008..0000000 --- a/Renci.SshNet/KeyboardInteractiveConnectionInfo.NET40.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using System; - -namespace Renci.SshNet -{ - /// - /// Provides connection information when keyboard interactive authentication method is used - /// - public partial class KeyboardInteractiveConnectionInfo - { - partial void ExecuteThread(Action action) - { - Task.Factory.StartNew(action); - } - } -} diff --git a/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs b/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs deleted file mode 100644 index f31cb84..0000000 --- a/Renci.SshNet/KeyboardInteractiveConnectionInfo.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Linq; -using Renci.SshNet.Common; - -namespace Renci.SshNet -{ - /// - /// Provides connection information when keyboard interactive authentication method is used - /// - /// - /// - /// - public class KeyboardInteractiveConnectionInfo : ConnectionInfo, IDisposable - { - /// - /// Occurs when server prompts for more authentication information. - /// - /// - /// - /// - public event EventHandler AuthenticationPrompt; - - // TODO: DOCS Add exception documentation for this class. - - /// - /// Initializes a new instance of the class. - /// - /// The host. - /// The username. - public KeyboardInteractiveConnectionInfo(string host, string username) - : this(host, ConnectionInfo.DEFAULT_PORT, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The host. - /// The port. - /// The username. - public KeyboardInteractiveConnectionInfo(string host, int port, string username) - : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - public KeyboardInteractiveConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) - : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - public KeyboardInteractiveConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) - : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new KeyboardInteractiveAuthenticationMethod(username)) - { - foreach (var authenticationMethod in this.AuthenticationMethods.OfType()) - { - authenticationMethod.AuthenticationPrompt += AuthenticationMethod_AuthenticationPrompt; - } - - } - - private void AuthenticationMethod_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e) - { - if (this.AuthenticationPrompt != null) - { - this.AuthenticationPrompt(sender, e); - } - } - - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this.AuthenticationMethods != null) - { - foreach (var authenticationMethods in this.AuthenticationMethods.OfType()) - { - authenticationMethods.Dispose(); - } - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~KeyboardInteractiveConnectionInfo() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/MessageEventArgs.cs b/Renci.SshNet/MessageEventArgs.cs deleted file mode 100644 index 84248a2..0000000 --- a/Renci.SshNet/MessageEventArgs.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace Renci.SshNet -{ - /// - /// Provides data for message events. - /// - /// Message type - public class MessageEventArgs : EventArgs - { - /// - /// Gets the message. - /// - public T Message { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// is null. - public MessageEventArgs(T message) - { - if (message == null) - throw new ArgumentNullException("message"); - - this.Message = message; - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/BannerMessage.cs b/Renci.SshNet/Messages/Authentication/BannerMessage.cs deleted file mode 100644 index ca87762..0000000 --- a/Renci.SshNet/Messages/Authentication/BannerMessage.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_BANNER message. - /// - [Message("SSH_MSG_USERAUTH_BANNER", 53)] - public class BannerMessage : Message - { - /// - /// Gets banner message. - /// - public string Message { get; private set; } - - /// - /// Gets banner language. - /// - public string Language { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.Message = this.ReadString(); - this.Language = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.Message); - this.Write(this.Language); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/FailureMessage.cs b/Renci.SshNet/Messages/Authentication/FailureMessage.cs deleted file mode 100644 index 513e442..0000000 --- a/Renci.SshNet/Messages/Authentication/FailureMessage.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_FAILURE message. - /// - [Message("SSH_MSG_USERAUTH_FAILURE", 51)] - public class FailureMessage : Message - { - /// - /// Gets or sets the allowed authentications if available. - /// - /// - /// The allowed authentications. - /// - public string[] AllowedAuthentications { get; set; } - - /// - /// Gets failure message. - /// - public string Message { get; private set; } - - /// - /// Gets a value indicating whether authentication is partially successful. - /// - /// - /// true if partially successful; otherwise, false. - /// - public bool PartialSuccess { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.AllowedAuthentications = this.ReadNamesList(); - this.PartialSuccess = this.ReadBoolean(); - if (this.PartialSuccess) - { - this.Message = string.Join(",", this.AllowedAuthentications); - } - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - throw new NotImplementedException(); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs b/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs deleted file mode 100644 index 7c1bde3..0000000 --- a/Renci.SshNet/Messages/Authentication/InformationRequestMessage.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_INFO_REQUEST message. - /// - [Message("SSH_MSG_USERAUTH_INFO_REQUEST", 60)] - internal class InformationRequestMessage : Message - { - /// - /// Gets information request name. - /// - public string Name { get; private set; } - - /// - /// Gets information request instruction. - /// - public string Instruction { get; private set; } - - /// - /// Gets information request language. - /// - public string Language { get; private set; } - - /// - /// Gets information request prompts. - /// - public IEnumerable Prompts { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.Name = this.ReadString(); - this.Instruction = this.ReadString(); - this.Language = this.ReadString(); - - var numOfPrompts = this.ReadUInt32(); - var prompts = new List(); - - for (int i = 0; i < numOfPrompts; i++) - { - var prompt = this.ReadString(); - var echo = this.ReadBoolean(); - prompts.Add(new AuthenticationPrompt(i, echo, prompt)); - } - - this.Prompts = prompts; - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - throw new NotImplementedException(); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs b/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs deleted file mode 100644 index b1c19a9..0000000 --- a/Renci.SshNet/Messages/Authentication/InformationResponseMessage.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_INFO_RESPONSE message. - /// - [Message("SSH_MSG_USERAUTH_INFO_RESPONSE", 61)] - internal class InformationResponseMessage : Message - { - /// - /// Gets authentication responses. - /// - public IList Responses { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public InformationResponseMessage() - { - this.Responses = new List(); - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - throw new NotImplementedException(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write((UInt32)this.Responses.Count); - foreach (var response in this.Responses) - { - this.Write(response); - } - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/PasswordChangeRequiredMessage.cs b/Renci.SshNet/Messages/Authentication/PasswordChangeRequiredMessage.cs deleted file mode 100644 index 8a4d8f6..0000000 --- a/Renci.SshNet/Messages/Authentication/PasswordChangeRequiredMessage.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message. - /// - [Message("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", 60)] - internal class PasswordChangeRequiredMessage : Message - { - /// - /// Gets password change request message. - /// - public string Message { get; private set; } - - /// - /// Gets message language. - /// - public string Language { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.Message = this.ReadString(); - this.Language = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.Message); - this.Write(this.Language); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs b/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs deleted file mode 100644 index 8ca725c..0000000 --- a/Renci.SshNet/Messages/Authentication/PublicKeyMessage.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_PK_OK message. - /// - [Message("SSH_MSG_USERAUTH_PK_OK", 60)] - internal class PublicKeyMessage : Message - { - /// - /// Gets the name of the public key algorithm. - /// - /// - /// The name of the public key algorithm. - /// - public string PublicKeyAlgorithmName { get; private set; } - - /// - /// Gets the public key data. - /// - public byte[] PublicKeyData { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.PublicKeyAlgorithmName = this.ReadAsciiString(); - this.PublicKeyData = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.WriteAscii(this.PublicKeyAlgorithmName); - this.WriteBinaryString(this.PublicKeyData); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/RequestMessage.cs b/Renci.SshNet/Messages/Authentication/RequestMessage.cs deleted file mode 100644 index 1d9c042..0000000 --- a/Renci.SshNet/Messages/Authentication/RequestMessage.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_REQUEST message. Server as a base message for other user authentication requests. - /// - [Message("SSH_MSG_USERAUTH_REQUEST", 50)] - public class RequestMessage : Message - { - /// - /// Gets authentication username. - /// - public string Username { get; private set; } - - /// - /// Gets the name of the service. - /// - /// - /// The name of the service. - /// - public ServiceName ServiceName { get; private set; } - - /// - /// Gets the name of the authentication method. - /// - /// - /// The name of the method. - /// - public virtual string MethodName { get { return "none"; } } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - public RequestMessage(ServiceName serviceName, string username) - { - this.ServiceName = serviceName; - this.Username = username; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - throw new InvalidOperationException("Load data is not supported."); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.Username); - switch (this.ServiceName) - { - case ServiceName.UserAuthentication: - this.WriteAscii("ssh-userauth"); - break; - case ServiceName.Connection: - this.WriteAscii("ssh-connection"); - break; - default: - throw new NotSupportedException("Not supported service name"); - } - this.WriteAscii(this.MethodName); - } - } -} - diff --git a/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs b/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs deleted file mode 100644 index 08af7ac..0000000 --- a/Renci.SshNet/Messages/Authentication/RequestMessageHost.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents "hostbased" SSH_MSG_USERAUTH_REQUEST message. - /// - internal class RequestMessageHost : RequestMessage - { - /// - /// Gets the name of the authentication method. - /// - /// - /// The name of the method. - /// - public override string MethodName - { - get - { - return "hostbased"; - } - } - - /// - /// Gets the public key algorithm for host key - /// - public string PublicKeyAlgorithm { get; private set; } - - /// - /// Gets or sets the public host key and certificates for client host. - /// - /// - /// The public host key. - /// - public byte[] PublicHostKey { get; private set; } - - /// - /// Gets or sets the name of the client host. - /// - /// - /// The name of the client host. - /// - public string ClientHostName { get; private set; } - - /// - /// Gets or sets the client username on the client host - /// - /// - /// The client username. - /// - public string ClientUsername { get; private set; } - - /// - /// Gets or sets the signature. - /// - /// - /// The signature. - /// - public byte[] Signature { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - /// The public key algorithm. - /// The public host key. - /// Name of the client host. - /// The client username. - public RequestMessageHost(ServiceName serviceName, string username, string publicKeyAlgorithm, byte[] publicHostKey, string clientHostName, string clientUsername) - : base(serviceName, username) - { - this.PublicKeyAlgorithm = publicKeyAlgorithm; - this.PublicHostKey = publicHostKey; - this.ClientHostName = clientHostName; - this.ClientUsername = clientUsername; - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.WriteAscii(this.PublicKeyAlgorithm); - this.WriteBinaryString(this.PublicHostKey); - this.Write(this.ClientHostName); - this.Write(this.ClientUsername); - - if (this.Signature != null) - this.WriteBinaryString(this.Signature); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs b/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs deleted file mode 100644 index f08d00d..0000000 --- a/Renci.SshNet/Messages/Authentication/RequestMessageKeyboardInteractive.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents "keyboard-interactive" SSH_MSG_USERAUTH_REQUEST message. - /// - internal class RequestMessageKeyboardInteractive : RequestMessage - { - /// - /// Gets the name of the authentication method. - /// - /// - /// The name of the method. - /// - public override string MethodName - { - get - { - return "keyboard-interactive"; - } - } - - /// - /// Gets message language. - /// - public string Language { get; private set; } - - /// - /// Gets authentication sub methods. - /// - public string SubMethods { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - public RequestMessageKeyboardInteractive(ServiceName serviceName, string username) - : base(serviceName, username) - { - this.Language = string.Empty; - this.SubMethods = string.Empty; - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.Language); - - this.Write(this.SubMethods); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs b/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs deleted file mode 100644 index a4bef4c..0000000 --- a/Renci.SshNet/Messages/Authentication/RequestMessageNone.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents "none" SSH_MSG_USERAUTH_REQUEST message. - /// - internal class RequestMessageNone : RequestMessage - { - /// - /// Gets the name of the authentication method. - /// - /// - /// The name of the method. - /// - public override string MethodName - { - get - { - return "none"; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - public RequestMessageNone(ServiceName serviceName, string username) - : base(serviceName, username) - { - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs b/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs deleted file mode 100644 index e490a7c..0000000 --- a/Renci.SshNet/Messages/Authentication/RequestMessagePassword.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents "password" SSH_MSG_USERAUTH_REQUEST message. - /// - internal class RequestMessagePassword : RequestMessage - { - /// - /// Gets the name of the authentication method. - /// - /// - /// The name of the method. - /// - public override string MethodName - { - get - { - return "password"; - } - } - - /// - /// Gets authentication password. - /// - public byte[] Password { get; private set; } - - /// - /// Gets new authentication password. - /// - public byte[] NewPassword { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - /// Authentication password. - public RequestMessagePassword(ServiceName serviceName, string username, byte[] password) - : base(serviceName, username) - { - this.Password = password; - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - /// Authentication password. - /// New authentication password. - public RequestMessagePassword(ServiceName serviceName, string username, byte[] password, byte[] newPassword) - : this(serviceName, username, password) - { - this.NewPassword = newPassword; - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.NewPassword != null); - - this.Write((uint)this.Password.Length); - this.Write(this.Password); - - if (this.NewPassword != null) - { - this.Write((uint)this.NewPassword.Length); - this.Write(this.NewPassword); - } - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs b/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs deleted file mode 100644 index a5a74aa..0000000 --- a/Renci.SshNet/Messages/Authentication/RequestMessagePublicKey.cs +++ /dev/null @@ -1,92 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents "publickey" SSH_MSG_USERAUTH_REQUEST message. - /// - public class RequestMessagePublicKey : RequestMessage - { - /// - /// Gets the name of the authentication method. - /// - /// - /// The name of the method. - /// - public override string MethodName - { - get - { - return "publickey"; - } - } - - /// - /// Gets the name of the public key algorithm. - /// - /// - /// The name of the public key algorithm. - /// - public string PublicKeyAlgorithmName { get; private set; } - - /// - /// Gets the public key data. - /// - public byte[] PublicKeyData { get; private set; } - - /// - /// Gets or sets public key signature. - /// - /// - /// The signature. - /// - public byte[] Signature { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - /// Name of private key algorithm. - /// Private key data. - public RequestMessagePublicKey(ServiceName serviceName, string username, string keyAlgorithmName, byte[] keyData) - : base(serviceName, username) - { - this.PublicKeyAlgorithmName = keyAlgorithmName; - this.PublicKeyData = keyData; - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - /// Authentication username. - /// Name of private key algorithm. - /// Private key data. - /// Private key signature. - public RequestMessagePublicKey(ServiceName serviceName, string username, string keyAlgorithmName, byte[] keyData, byte[] signature) - : this(serviceName, username, keyAlgorithmName, keyData) - { - this.Signature = signature; - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - if (this.Signature == null) - { - this.Write(false); - } - else - { - this.Write(true); - } - this.WriteAscii(this.PublicKeyAlgorithmName); - this.WriteBinaryString(this.PublicKeyData); - if (this.Signature != null) - this.WriteBinaryString(this.Signature); - } - } -} diff --git a/Renci.SshNet/Messages/Authentication/SuccessMessage.cs b/Renci.SshNet/Messages/Authentication/SuccessMessage.cs deleted file mode 100644 index cd6a267..0000000 --- a/Renci.SshNet/Messages/Authentication/SuccessMessage.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Renci.SshNet.Messages.Authentication -{ - /// - /// Represents SSH_MSG_USERAUTH_SUCCESS message. - /// - [Message("SSH_MSG_USERAUTH_SUCCESS", 52)] - public class SuccessMessage : Message - { - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelCloseMessage.cs b/Renci.SshNet/Messages/Connection/ChannelCloseMessage.cs deleted file mode 100644 index a494185..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelCloseMessage.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_CLOSE message. - /// - [Message("SSH_MSG_CHANNEL_CLOSE", 97)] - public class ChannelCloseMessage : ChannelMessage - { - /// - /// Initializes a new instance of the class. - /// - public ChannelCloseMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - public ChannelCloseMessage(uint localChannelNumber) - { - LocalChannelNumber = localChannelNumber; - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs b/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs deleted file mode 100644 index b6d6718..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelDataMessage.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_DATA message. - /// - [Message("SSH_MSG_CHANNEL_DATA", 94)] - public class ChannelDataMessage : ChannelMessage - { - /// - /// Gets or sets message data. - /// - /// - /// The data. - /// - public byte[] Data { get; protected set; } - - /// - /// Initializes a new instance of the class. - /// - public ChannelDataMessage() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - /// Message data. - public ChannelDataMessage(uint localChannelNumber, byte[] data) - { - this.LocalChannelNumber = localChannelNumber; - this.Data = data; - } - - /// - /// Loads the data. - /// - protected override void LoadData() - { - base.LoadData(); - this.Data = this.ReadBinaryString(); - } - - /// - /// Saves the data. - /// - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Data); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelEofMessage.cs b/Renci.SshNet/Messages/Connection/ChannelEofMessage.cs deleted file mode 100644 index 64a5ee0..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelEofMessage.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_EOF message. - /// - [Message("SSH_MSG_CHANNEL_EOF", 96)] - public class ChannelEofMessage : ChannelMessage - { - /// - /// Initializes a new instance of the class. - /// - public ChannelEofMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - public ChannelEofMessage(uint localChannelNumber) - { - this.LocalChannelNumber = localChannelNumber; - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelExtendedDataMessage.cs b/Renci.SshNet/Messages/Connection/ChannelExtendedDataMessage.cs deleted file mode 100644 index 7f67cee..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelExtendedDataMessage.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_EXTENDED_DATA message. - /// - [Message("SSH_MSG_CHANNEL_EXTENDED_DATA", 95)] - public class ChannelExtendedDataMessage : ChannelMessage - { - /// - /// Gets message data type code. - /// - public uint DataTypeCode { get; private set; } - - /// - /// Gets message data. - /// - public byte[] Data { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ChannelExtendedDataMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - /// The message data type code. - /// The message data. - public ChannelExtendedDataMessage(uint localChannelNumber, uint dataTypeCode, byte[] data) - { - this.LocalChannelNumber = localChannelNumber; - this.DataTypeCode = dataTypeCode; - this.Data = data; - } - - /// - /// Loads the data. - /// - protected override void LoadData() - { - base.LoadData(); - this.DataTypeCode = this.ReadUInt32(); - this.Data = this.ReadBinaryString(); - } - - /// - /// Saves the data. - /// - protected override void SaveData() - { - base.SaveData(); - this.Write(this.DataTypeCode); - this.WriteBinaryString(this.Data); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelFailureMessage.cs b/Renci.SshNet/Messages/Connection/ChannelFailureMessage.cs deleted file mode 100644 index 222b59a..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelFailureMessage.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_FAILURE message. - /// - [Message("SSH_MSG_CHANNEL_FAILURE", 100)] - public class ChannelFailureMessage : ChannelMessage - { - /// - /// Initializes a new instance of the class. - /// - public ChannelFailureMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - public ChannelFailureMessage(uint localChannelNumber) - { - this.LocalChannelNumber = localChannelNumber; - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelMessage.cs b/Renci.SshNet/Messages/Connection/ChannelMessage.cs deleted file mode 100644 index ee221cb..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelMessage.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Globalization; -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Base class for all channel specific SSH messages. - /// - public abstract class ChannelMessage : Message - { - /// - /// Gets or sets the local channel number. - /// - /// - /// The local channel number. - /// - public uint LocalChannelNumber { get; protected set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.LocalChannelNumber = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.LocalChannelNumber); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "{0} : #{1}", base.ToString(), this.LocalChannelNumber); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenInfo.cs b/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenInfo.cs deleted file mode 100644 index 839afa9..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Base class for open channel messages - /// - public abstract class ChannelOpenInfo : SshData - { - /// - /// Gets the type of the channel to open. - /// - /// - /// The type of the channel to open. - /// - public abstract string ChannelType { get; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs b/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs deleted file mode 100644 index 2ef899a..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpen/ChannelOpenMessage.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_OPEN message. - /// - [Message("SSH_MSG_CHANNEL_OPEN", 90)] - public class ChannelOpenMessage : ChannelMessage - { - /// - /// Gets the type of the channel. - /// - /// - /// The type of the channel. - /// - public string ChannelType - { - get - { - return this.Info.ChannelType; - } - } - - /// - /// Gets the initial size of the window. - /// - /// - /// The initial size of the window. - /// - public uint InitialWindowSize { get; private set; } - - /// - /// Gets the maximum size of the packet. - /// - /// - /// The maximum size of the packet. - /// - public uint MaximumPacketSize { get; private set; } - - /// - /// Gets channel specific open information. - /// - public ChannelOpenInfo Info { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ChannelOpenMessage() - { - // Required for dynamicly loading request type when it comes from the server - } - - /// - /// Initializes a new instance of the class. - /// - /// The channel number. - /// Initial size of the window. - /// Maximum size of the packet. - /// The info. - public ChannelOpenMessage(uint channelNumber, uint initialWindowSize, uint maximumPacketSize, ChannelOpenInfo info) - { - this.LocalChannelNumber = channelNumber; - this.InitialWindowSize = initialWindowSize; - this.MaximumPacketSize = maximumPacketSize; - this.Info = info; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - var channelName = this.ReadAsciiString(); - this.LocalChannelNumber = this.ReadUInt32(); - this.InitialWindowSize = this.ReadUInt32(); - this.MaximumPacketSize = this.ReadUInt32(); - var bytes = this.ReadBytes(); - - if (channelName == SessionChannelOpenInfo.NAME) - { - this.Info = new SessionChannelOpenInfo(); - } - else if (channelName == X11ChannelOpenInfo.NAME) - { - this.Info = new X11ChannelOpenInfo(); - } - else if (channelName == DirectTcpipChannelInfo.NAME) - { - this.Info = new DirectTcpipChannelInfo(); - } - else if (channelName == ForwardedTcpipChannelInfo.NAME) - { - this.Info = new ForwardedTcpipChannelInfo(); - } - else - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Channel type '{0}' is not supported.", channelName)); - } - - this.Info.Load(bytes); - - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.WriteAscii(this.ChannelType); - this.Write(this.LocalChannelNumber); - this.Write(this.InitialWindowSize); - this.Write(this.MaximumPacketSize); - this.Write(this.Info.GetBytes()); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs b/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs deleted file mode 100644 index 491b29f..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpen/DirectTcpipChannelInfo.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Used to open "direct-tcpip" channel type - /// - internal class DirectTcpipChannelInfo : ChannelOpenInfo - { - /// - /// Specifies channel open type - /// - public const string NAME = "direct-tcpip"; - - /// - /// Gets the type of the channel to open. - /// - /// - /// The type of the channel to open. - /// - public override string ChannelType - { - get { return NAME; } - } - - /// - /// Gets the host to connect. - /// - public string HostToConnect { get; private set; } - - /// - /// Gets the port to connect. - /// - public uint PortToConnect { get; private set; } - - /// - /// Gets the originator address. - /// - public string OriginatorAddress { get; private set; } - - /// - /// Gets the originator port. - /// - public uint OriginatorPort { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public DirectTcpipChannelInfo() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The host to connect. - /// The port to connect. - /// The originator address. - /// The originator port. - public DirectTcpipChannelInfo(string hostToConnect, uint portToConnect, string originatorAddress, uint originatorPort) - { - this.HostToConnect = hostToConnect; - this.PortToConnect = portToConnect; - this.OriginatorAddress = originatorAddress; - this.OriginatorPort = originatorPort; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.HostToConnect = this.ReadString(); - this.PortToConnect = this.ReadUInt32(); - this.OriginatorAddress = this.ReadString(); - this.OriginatorPort = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.HostToConnect); - this.Write(this.PortToConnect); - this.Write(this.OriginatorAddress); - this.Write(this.OriginatorPort); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs b/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs deleted file mode 100644 index 798cc5a..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpen/ForwardedTcpipChannelInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Used to open "forwarded-tcpip" channel type - /// - internal class ForwardedTcpipChannelInfo : ChannelOpenInfo - { - /// - /// Specifies channel open type - /// - public const string NAME = "forwarded-tcpip"; - - /// - /// Gets the type of the channel to open. - /// - /// - /// The type of the channel to open. - /// - public override string ChannelType - { - get { return NAME; } - } - - /// - /// Gets the connected address. - /// - public string ConnectedAddress { get; private set; } - - /// - /// Gets the connected port. - /// - public uint ConnectedPort { get; private set; } - - /// - /// Gets the originator address. - /// - public string OriginatorAddress { get; private set; } - - /// - /// Gets the originator port. - /// - public uint OriginatorPort { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.ConnectedAddress = this.ReadString(); - this.ConnectedPort = this.ReadUInt32(); - this.OriginatorAddress = this.ReadString(); - this.OriginatorPort = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.ConnectedAddress); - this.Write(this.ConnectedPort); - this.Write(this.OriginatorAddress); - this.Write(this.OriginatorPort); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs b/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs deleted file mode 100644 index 4ceef92..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpen/SessionChannelOpenInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Used to open "session" channel type - /// - internal class SessionChannelOpenInfo : ChannelOpenInfo - { - /// - /// Specifies channel open type - /// - public const string NAME = "session"; - - /// - /// Gets the type of the channel to open. - /// - /// - /// The type of the channel to open. - /// - public override string ChannelType - { - get { return NAME; } - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs b/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs deleted file mode 100644 index 95178d9..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpen/X11ChannelOpenInfo.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Used to open "x11" channel type - /// - internal class X11ChannelOpenInfo : ChannelOpenInfo - { - /// - /// Specifies channel open type - /// - public const string NAME = "x11"; - - /// - /// Gets the type of the channel to open. - /// - /// - /// The type of the channel to open. - /// - public override string ChannelType - { - get { return NAME; } - } - - /// - /// Gets the originator address. - /// - public string OriginatorAddress { get; private set; } - - /// - /// Gets the originator port. - /// - public uint OriginatorPort { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.OriginatorAddress = this.ReadString(); - this.OriginatorPort = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.OriginatorAddress); - this.Write(this.OriginatorPort); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpenConfirmationMessage.cs b/Renci.SshNet/Messages/Connection/ChannelOpenConfirmationMessage.cs deleted file mode 100644 index a1eca39..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpenConfirmationMessage.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_OPEN_CONFIRMATION message. - /// - [Message("SSH_MSG_CHANNEL_OPEN_CONFIRMATION", 91)] - public class ChannelOpenConfirmationMessage : ChannelMessage - { - /// - /// Gets the remote channel number. - /// - public uint RemoteChannelNumber { get; private set; } - - /// - /// Gets the initial size of the window. - /// - /// - /// The initial size of the window. - /// - public uint InitialWindowSize { get; private set; } - - /// - /// Gets the maximum size of the packet. - /// - /// - /// The maximum size of the packet. - /// - public uint MaximumPacketSize { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ChannelOpenConfirmationMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - /// Initial size of the window. - /// Maximum size of the packet. - /// The remote channel number. - public ChannelOpenConfirmationMessage(uint localChannelNumber, uint initialWindowSize, uint maximumPacketSize, uint remoteChannelNumber) - { - this.LocalChannelNumber = localChannelNumber; - this.InitialWindowSize = initialWindowSize; - this.MaximumPacketSize = maximumPacketSize; - this.RemoteChannelNumber = remoteChannelNumber; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - this.RemoteChannelNumber = this.ReadUInt32(); - this.InitialWindowSize = this.ReadUInt32(); - this.MaximumPacketSize = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - this.Write(this.RemoteChannelNumber); - this.Write(this.InitialWindowSize); - this.Write(this.MaximumPacketSize); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpenFailureMessage.cs b/Renci.SshNet/Messages/Connection/ChannelOpenFailureMessage.cs deleted file mode 100644 index 04277dd..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpenFailureMessage.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_OPEN_FAILURE message. - /// - [Message("SSH_MSG_CHANNEL_OPEN_FAILURE", 92)] - public class ChannelOpenFailureMessage : ChannelMessage - { - /// - /// Gets failure reason code. - /// - public uint ReasonCode { get; private set; } - - /// - /// Gets description for failure. - /// - public string Description { get; private set; } - - /// - /// Gets message language. - /// - public string Language { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ChannelOpenFailureMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - /// The description. - /// The reason code. - public ChannelOpenFailureMessage(uint localChannelNumber, string description, uint reasonCode) - { - this.LocalChannelNumber = localChannelNumber; - this.Description = description; - this.ReasonCode = reasonCode; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - this.ReasonCode = this.ReadUInt32(); - this.Description = this.ReadString(); - this.Language = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - this.Write(this.ReasonCode); - this.Write(this.Description ?? string.Empty); - this.Write(this.Language ?? "en"); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelOpenFailureReasons.cs b/Renci.SshNet/Messages/Connection/ChannelOpenFailureReasons.cs deleted file mode 100644 index 1a397ab..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelOpenFailureReasons.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// List channel open failure reasons defined by the protocol. - /// - internal enum ChannelOpenFailureReasons : uint - { - /// - /// SSH_OPEN_ADMINISTRATIVELY_PROHIBITED - /// - AdministativelyProhibited = 1, - /// - /// SSH_OPEN_CONNECT_FAILED - /// - ConnectFailed = 2, - /// - /// SSH_OPEN_UNKNOWN_CHANNEL_TYPE - /// - UnknownChannelType = 3, - /// - /// SSH_OPEN_RESOURCE_SHORTAGE - /// - ResourceShortage = 4 - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs deleted file mode 100644 index bde3d66..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "break" type channel request information - /// - internal class BreakRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "break"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets break length in milliseconds. - /// - public UInt32 BreakLength { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public BreakRequestInfo() - { - this.WantReply = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Length of the break. - public BreakRequestInfo(UInt32 breakLength) - : this() - { - this.BreakLength = breakLength; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.BreakLength = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.BreakLength); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs deleted file mode 100644 index e2a6170..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ChannelRequestMessage.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_REQUEST message. - /// - [Message("SSH_MSG_CHANNEL_REQUEST", 98)] - public class ChannelRequestMessage : ChannelMessage - { - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public string RequestName { get; private set; } - - /// - /// Gets channel request data. - /// - public byte[] RequestData { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ChannelRequestMessage() - { - // Required for dynamically loading request type when it comes from the server - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the local channel. - /// The info. - public ChannelRequestMessage(uint localChannelName, RequestInfo info) - { - this.LocalChannelNumber = localChannelName; - this.RequestName = info.RequestName; - this.RequestData = info.GetBytes(); - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.RequestName = this.ReadAsciiString(); - this.RequestData = this.ReadBytes(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.WriteAscii(this.RequestName); - this.Write(this.RequestData); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs deleted file mode 100644 index c72cc01..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/EndOfWriteRequestInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "eow@openssh.com" type channel request information - /// - public class EndOfWriteRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "eow@openssh.com"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Initializes a new instance of the class. - /// - public EndOfWriteRequestInfo() - { - this.WantReply = false; - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs deleted file mode 100644 index 2738050..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/EnvironmentVariableRequestInfo.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "env" type channel request information - /// - internal class EnvironmentVariableRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "env"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets or sets the name of the variable. - /// - /// - /// The name of the variable. - /// - public string VariableName { get; set; } - - /// - /// Gets or sets the variable value. - /// - /// - /// The variable value. - /// - public string VariableValue { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public EnvironmentVariableRequestInfo() - { - this.WantReply = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the variable. - /// The variable value. - public EnvironmentVariableRequestInfo(string variableName, string variableValue) - : this() - { - this.VariableName = variableName; - this.VariableValue = variableValue; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.VariableName = this.ReadString(); - this.VariableValue = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.VariableName); - this.Write(this.VariableValue); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs deleted file mode 100644 index 69d213e..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ExecRequestInfo.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Text; - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "exec" type channel request information - /// - internal class ExecRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "exec"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets command to execute. - /// - /// - /// The command. - /// - public string Command { get; private set; } - - /// - /// Gets the encoding. - /// - /// - /// The encoding. - /// - public Encoding Encoding { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ExecRequestInfo() - { - this.WantReply = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// The command. - /// The character encoding to use. - /// or is null. - public ExecRequestInfo(string command, Encoding encoding) - : this() - { - if (command == null) - throw new System.ArgumentNullException("command"); - if (encoding == null) - throw new System.ArgumentNullException("encoding"); - - this.Command = command; - this.Encoding = encoding; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.Command = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.Command, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs deleted file mode 100644 index 6be4a77..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ExitSignalRequestInfo.cs +++ /dev/null @@ -1,101 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "exit-signal" type channel request information - /// - internal class ExitSignalRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "exit-signal"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets the name of the signal. - /// - /// - /// The name of the signal. - /// - public string SignalName { get; private set; } - - /// - /// Gets a value indicating whether core is dumped. - /// - /// - /// true if core is dumped; otherwise, false. - /// - public bool CoreDumped { get; private set; } - - /// - /// Gets the error message. - /// - public string ErrorMessage { get; private set; } - - /// - /// Gets message language. - /// - public string Language { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ExitSignalRequestInfo() - { - this.WantReply = false; - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the signal. - /// if set to true then core is dumped. - /// The error message. - /// The language. - public ExitSignalRequestInfo(string signalName, bool coreDumped, string errorMessage, string language) - : this() - { - this.SignalName = signalName; - this.CoreDumped = coreDumped; - this.ErrorMessage = errorMessage; - this.Language = language; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.SignalName = this.ReadAsciiString(); - this.CoreDumped = this.ReadBoolean(); - this.ErrorMessage = this.ReadString(); - this.Language = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.WriteAscii(this.SignalName); - this.Write(this.CoreDumped); - this.Write(this.ErrorMessage); - this.Write(this.Language); - } - - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs deleted file mode 100644 index e9e612d..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ExitStatusRequestInfo.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "exit-status" type channel request information - /// - internal class ExitStatusRequestInfo : RequestInfo - { - /// - /// Channel request name. - /// - public const string NAME = "exit-status"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets the exit status number. - /// - public uint ExitStatus { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ExitStatusRequestInfo() - { - this.WantReply = false; - } - - /// - /// Initializes a new instance of the class. - /// - /// The exit status number. - public ExitStatusRequestInfo(uint exitStatus) - : this() - { - this.ExitStatus = exitStatus; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.ExitStatus = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.ExitStatus); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs deleted file mode 100644 index c1837cc..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/KeepAliveRequestInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "keepalive@openssh.com" type channel request information - /// - public class KeepAliveRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "keepalive@openssh.com"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Initializes a new instance of the class. - /// - public KeepAliveRequestInfo() - { - this.WantReply = false; - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs deleted file mode 100644 index 29f1fdc..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/PseudoTerminalInfo.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Renci.SshNet.Common; -using System.Collections.Generic; - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "pty-req" type channel request information - /// - internal class PseudoTerminalRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "pty-req"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets or sets the environment variable. - /// - /// - /// The environment variable. - /// - public string EnvironmentVariable { get; set; } - - /// - /// Gets or sets the columns. - /// - /// - /// The columns. - /// - public uint Columns { get; set; } - - /// - /// Gets or sets the rows. - /// - /// - /// The rows. - /// - public uint Rows { get; set; } - - /// - /// Gets or sets the width of the pixel. - /// - /// - /// The width of the pixel. - /// - public uint PixelWidth { get; set; } - - /// - /// Gets or sets the height of the pixel. - /// - /// - /// The height of the pixel. - /// - public uint PixelHeight { get; set; } - - /// - /// Gets or sets the terminal mode. - /// - /// - /// The terminal mode. - /// - public IDictionary TerminalModeValues { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public PseudoTerminalRequestInfo() - { - this.WantReply = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// The environment variable. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode values. - public PseudoTerminalRequestInfo(string environmentVariable, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues) - : this() - { - this.EnvironmentVariable = environmentVariable; - this.Columns = columns; - this.Rows = rows; - this.PixelWidth = width; - this.PixelHeight = height; - this.TerminalModeValues = terminalModeValues; - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.EnvironmentVariable); - this.Write(this.Columns); - this.Write(this.Rows); - this.Write(this.Rows); - this.Write(this.PixelHeight); - - if (this.TerminalModeValues != null) - { - this.Write((uint)this.TerminalModeValues.Count * (1 + 4) + 1); - - foreach (var item in this.TerminalModeValues) - { - this.Write((byte)item.Key); - this.Write(item.Value); - } - this.Write((byte)0); - } - else - { - this.Write((uint)0); - } - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/RequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/RequestInfo.cs deleted file mode 100644 index 194d14f..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/RequestInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents type specific information for channel request. - /// - public abstract class RequestInfo : SshData - { - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public abstract string RequestName { get; } - - /// - /// Gets or sets a value indicating whether reply message is needed. - /// - /// - /// true if reply message is needed; otherwise, false. - /// - public bool WantReply { get; protected set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.WantReply = this.ReadBoolean(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.WantReply); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs deleted file mode 100644 index 480a0a1..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/ShellRequestInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "shell" type channel request information - /// - internal class ShellRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "shell"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Initializes a new instance of the class. - /// - public ShellRequestInfo() - { - this.WantReply = true; - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs deleted file mode 100644 index 8e31fb5..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/SignalRequestInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "signal" type channel request information - /// - internal class SignalRequestInfo : RequestInfo - { - /// - /// Channel request name. - /// - public const string NAME = "signal"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets the name of the signal. - /// - /// - /// The name of the signal. - /// - public string SignalName { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public SignalRequestInfo() - { - this.WantReply = false; - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the signal. - public SignalRequestInfo(string signalName) - : this() - { - this.SignalName = signalName; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.SignalName = this.ReadAsciiString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.WriteAscii(this.SignalName); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs deleted file mode 100644 index 0dd48c9..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/SubsystemRequestInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "subsystem" type channel request information - /// - internal class SubsystemRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "subsystem"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets the name of the subsystem. - /// - /// - /// The name of the subsystem. - /// - public string SubsystemName { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public SubsystemRequestInfo() - { - this.WantReply = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// The subsystem. - public SubsystemRequestInfo(string subsystem) - : this() - { - this.SubsystemName = subsystem; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.SubsystemName = this.ReadAsciiString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.WriteAscii(this.SubsystemName); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs deleted file mode 100644 index 51afe67..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/WindowChangeRequestInfo.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "window-change" type channel request information - /// - internal class WindowChangeRequestInfo : RequestInfo - { - /// - /// Channe request name - /// - public const string NAME = "window-change"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets the columns. - /// - public uint Columns { get; private set; } - - /// - /// Gets the rows. - /// - public uint Rows { get; private set; } - - /// - /// Gets the width. - /// - public uint Width { get; private set; } - - /// - /// Gets the height. - /// - public uint Height { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public WindowChangeRequestInfo() - { - this.WantReply = false; - } - - /// - /// Initializes a new instance of the class. - /// - /// The columns. - /// The rows. - /// The width. - /// The height. - public WindowChangeRequestInfo(uint columns, uint rows, uint width, uint height) - : this() - { - this.Columns = columns; - this.Rows = rows; - this.Width = width; - this.Height = height; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.Columns = this.ReadUInt32(); - this.Rows = this.ReadUInt32(); - this.Width = this.ReadUInt32(); - this.Height = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.Columns); - this.Write(this.Rows); - this.Write(this.Width); - this.Write(this.Height); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs deleted file mode 100644 index 8bff356..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/X11ForwardingRequestInfo.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "x11-req" type channel request information - /// - internal class X11ForwardingRequestInfo : RequestInfo - { - /// - /// Channel request name - /// - public const string NAME = "x11-req"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets or sets a value indicating whether it is a single connection. - /// - /// - /// true if it is a single connection; otherwise, false. - /// - public bool IsSingleConnection { get; set; } - - /// - /// Gets or sets the authentication protocol. - /// - /// - /// The authentication protocol. - /// - public string AuthenticationProtocol { get; set; } - - /// - /// Gets or sets the authentication cookie. - /// - /// - /// The authentication cookie. - /// - public byte[] AuthenticationCookie { get; set; } - - /// - /// Gets or sets the screen number. - /// - /// - /// The screen number. - /// - public uint ScreenNumber { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public X11ForwardingRequestInfo() - { - this.WantReply = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// if set to true it is a single connection. - /// The protocol. - /// The cookie. - /// The screen number. - public X11ForwardingRequestInfo(bool isSingleConnection, string protocol, byte[] cookie, uint screenNumber) - : this() - { - this.IsSingleConnection = isSingleConnection; - this.AuthenticationProtocol = protocol; - this.AuthenticationCookie = cookie; - this.ScreenNumber = screenNumber; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.IsSingleConnection = this.ReadBoolean(); - this.AuthenticationProtocol = this.ReadAsciiString(); - this.AuthenticationCookie = this.ReadBinaryString(); - this.ScreenNumber = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.IsSingleConnection); - this.WriteAscii(this.AuthenticationProtocol); - this.WriteBinaryString(this.AuthenticationCookie); - this.Write(this.ScreenNumber); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs b/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs deleted file mode 100644 index ebe2adf..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelRequest/XonXoffRequestInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents "xon-xoff" type channel request information - /// - internal class XonXoffRequestInfo : RequestInfo - { - /// - /// Channel request type - /// - public const string NAME = "xon-xoff"; - - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public override string RequestName - { - get { return NAME; } - } - - /// - /// Gets or sets a value indicating whether client can do. - /// - /// - /// true if client can do; otherwise, false. - /// - public bool ClientCanDo { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public XonXoffRequestInfo() - { - this.WantReply = false; - } - - /// - /// Initializes a new instance of the class. - /// - /// if set to true [client can do]. - public XonXoffRequestInfo(bool clientCanDo) - : this() - { - this.ClientCanDo = clientCanDo; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - - this.ClientCanDo = this.ReadBoolean(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.ClientCanDo); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelSuccessMessage.cs b/Renci.SshNet/Messages/Connection/ChannelSuccessMessage.cs deleted file mode 100644 index c5c4a6f..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelSuccessMessage.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_SUCCESS message. - /// - [Message("SSH_MSG_CHANNEL_SUCCESS", 99)] - public class ChannelSuccessMessage : ChannelMessage - { - /// - /// Initializes a new instance of the class. - /// - public ChannelSuccessMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - public ChannelSuccessMessage(uint localChannelNumber) - { - this.LocalChannelNumber = localChannelNumber; - } - } -} diff --git a/Renci.SshNet/Messages/Connection/ChannelWindowAdjustMessage.cs b/Renci.SshNet/Messages/Connection/ChannelWindowAdjustMessage.cs deleted file mode 100644 index bb8bdec..0000000 --- a/Renci.SshNet/Messages/Connection/ChannelWindowAdjustMessage.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_CHANNEL_SUCCESS message. - /// - [Message("SSH_MSG_CHANNEL_WINDOW_ADJUST", 93)] - public class ChannelWindowAdjustMessage : ChannelMessage - { - /// - /// Gets number of bytes to add to the window. - /// - public uint BytesToAdd { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public ChannelWindowAdjustMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The local channel number. - /// The bytes to add. - public ChannelWindowAdjustMessage(uint localChannelNumber, uint bytesToAdd) - { - this.LocalChannelNumber = localChannelNumber; - this.BytesToAdd = bytesToAdd; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - base.LoadData(); - this.BytesToAdd = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - base.SaveData(); - this.Write(this.BytesToAdd); - } - } -} diff --git a/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs b/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs deleted file mode 100644 index 99568b2..0000000 --- a/Renci.SshNet/Messages/Connection/GlobalRequestMessage.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_GLOBAL_REQUEST message. - /// - [Message("SSH_MSG_GLOBAL_REQUEST", 80)] - public class GlobalRequestMessage : Message - { - /// - /// Gets the name of the request. - /// - /// - /// The name of the request. - /// - public GlobalRequestName RequestName { get; private set; } - - /// - /// Gets a value indicating whether message reply should be sent.. - /// - /// - /// true if message reply should be sent; otherwise, false. - /// - public bool WantReply { get; private set; } - - /// - /// Gets the address to bind to. - /// - public string AddressToBind { get; private set; } - // TODO: Extract AddressToBind property to be in different class and GlobalREquestMessage to be a base class fo it. - - /// - /// Gets port number to bind to. - /// - public UInt32 PortToBind { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public GlobalRequestMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the request. - /// if set to true [want reply]. - public GlobalRequestMessage(GlobalRequestName requestName, bool wantReply) - { - this.RequestName = requestName; - this.WantReply = wantReply; - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the request. - /// if set to true [want reply]. - /// The address to bind. - /// The port to bind. - public GlobalRequestMessage(GlobalRequestName requestName, bool wantReply, string addressToBind, uint portToBind) - : this(requestName, wantReply) - { - this.AddressToBind = addressToBind; - this.PortToBind = portToBind; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - var requestName = this.ReadAsciiString(); - - this.WantReply = this.ReadBoolean(); - - switch (requestName) - { - case "tcpip-forward": - this.RequestName = GlobalRequestName.TcpIpForward; - this.AddressToBind = this.ReadString(); - this.PortToBind = this.ReadUInt32(); - break; - case "cancel-tcpip-forward": - this.RequestName = GlobalRequestName.CancelTcpIpForward; - this.AddressToBind = this.ReadString(); - this.PortToBind = this.ReadUInt32(); - break; - } - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - switch (this.RequestName) - { - case GlobalRequestName.TcpIpForward: - this.WriteAscii("tcpip-forward"); - break; - case GlobalRequestName.CancelTcpIpForward: - this.WriteAscii("cancel-tcpip-forward"); - break; - } - - this.Write(this.WantReply); - - switch (this.RequestName) - { - case GlobalRequestName.TcpIpForward: - case GlobalRequestName.CancelTcpIpForward: - this.Write(this.AddressToBind); - this.Write(this.PortToBind); - break; - } - } - } -} diff --git a/Renci.SshNet/Messages/Connection/GlobalRequestName.cs b/Renci.SshNet/Messages/Connection/GlobalRequestName.cs deleted file mode 100644 index 13b86e9..0000000 --- a/Renci.SshNet/Messages/Connection/GlobalRequestName.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Specifies supported request names. - /// - public enum GlobalRequestName - { - /// - /// tcpip-forward - /// - TcpIpForward, - /// - /// cancel-tcpip-forward - /// - CancelTcpIpForward, - } -} diff --git a/Renci.SshNet/Messages/Connection/RequestFailureMessage.cs b/Renci.SshNet/Messages/Connection/RequestFailureMessage.cs deleted file mode 100644 index fb18f43..0000000 --- a/Renci.SshNet/Messages/Connection/RequestFailureMessage.cs +++ /dev/null @@ -1,24 +0,0 @@ - -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_REQUEST_FAILURE message. - /// - [Message("SSH_MSG_REQUEST_FAILURE", 82)] - public class RequestFailureMessage : Message - { - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - } - } -} diff --git a/Renci.SshNet/Messages/Connection/RequestSuccessMessage.cs b/Renci.SshNet/Messages/Connection/RequestSuccessMessage.cs deleted file mode 100644 index 694c76f..0000000 --- a/Renci.SshNet/Messages/Connection/RequestSuccessMessage.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Renci.SshNet.Messages.Connection -{ - /// - /// Represents SSH_MSG_REQUEST_SUCCESS message. - /// - [Message("SSH_MSG_REQUEST_SUCCESS", 81)] - public class RequestSuccessMessage : Message - { - /// - /// Gets the bound port. - /// - public uint? BoundPort { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public RequestSuccessMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The bound port. - public RequestSuccessMessage(uint boundPort) - { - this.BoundPort = boundPort; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - if (!this.IsEndOfData) - this.BoundPort = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - if (this.BoundPort != null) - this.Write(this.BoundPort.Value); - } - } -} diff --git a/Renci.SshNet/Messages/Message.cs b/Renci.SshNet/Messages/Message.cs deleted file mode 100644 index f0ceb54..0000000 --- a/Renci.SshNet/Messages/Message.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Renci.SshNet.Common; -using System.Globalization; - -namespace Renci.SshNet.Messages -{ - /// - /// Base class for all SSH protocol messages - /// - public abstract class Message : SshData - { - /// - /// Gets the index that represents zero in current data type. - /// - /// - /// The index of the zero reader. - /// - protected override int ZeroReaderIndex - { - get - { - return 1; - } - } - - /// - /// Gets data bytes array - /// - /// Byte array representation of the message - public override byte[] GetBytes() - { - var messageAttribute = this.GetType().GetCustomAttributes(typeof(MessageAttribute), true).SingleOrDefault() as MessageAttribute; - - if (messageAttribute == null) - throw new SshException(string.Format(CultureInfo.CurrentCulture, "Type '{0}' is not a valid message type.", this.GetType().AssemblyQualifiedName)); - - var data = new List(base.GetBytes()); - - data.Insert(0, messageAttribute.Number); - - return data.ToArray(); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - var messageAttribute = this.GetType().GetCustomAttributes(typeof(MessageAttribute), true).SingleOrDefault() as MessageAttribute; - - if (messageAttribute == null) - return string.Format(CultureInfo.CurrentCulture, "'{0}' without Message attribute.", this.GetType().FullName); - - return messageAttribute.Name; - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Messages/MessageAttribute.cs b/Renci.SshNet/Messages/MessageAttribute.cs deleted file mode 100644 index 91a928e..0000000 --- a/Renci.SshNet/Messages/MessageAttribute.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages -{ - - /// - /// Indicates that a class represents SSH message. This class cannot be inherited. - /// - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] - public sealed class MessageAttribute : Attribute - { - /// - /// Gets or sets message name as defined in RFC 4250. - /// - /// - /// The name. - /// - public string Name { get; set; } - - /// - /// Gets or sets message number as defined in RFC 4250. - /// - /// - /// The number. - /// - public byte Number { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The name. - /// The number. - public MessageAttribute(string name, byte number) - { - this.Name = name; - this.Number = number; - } - } -} diff --git a/Renci.SshNet/Messages/ServiceName.cs b/Renci.SshNet/Messages/ServiceName.cs deleted file mode 100644 index a664b49..0000000 --- a/Renci.SshNet/Messages/ServiceName.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Renci.SshNet.Messages -{ - /// - /// Specifies list of supported services - /// - public enum ServiceName - { - /// - /// ssh-userauth - /// - UserAuthentication, - - /// - /// ssh-connection - /// - Connection - } -} diff --git a/Renci.SshNet/Messages/Transport/DebugMessage.cs b/Renci.SshNet/Messages/Transport/DebugMessage.cs deleted file mode 100644 index 25ec7fb..0000000 --- a/Renci.SshNet/Messages/Transport/DebugMessage.cs +++ /dev/null @@ -1,48 +0,0 @@ - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_DEBUG message. - /// - [Message("SSH_MSG_DEBUG", 4)] - public class DebugMessage : Message - { - /// - /// Gets a value indicating whether the message to be always displayed. - /// - /// - /// true if the message always to be displayed; otherwise, false. - /// - public bool IsAlwaysDisplay { get; private set; } - - /// - /// Gets debug message. - /// - public string Message { get; private set; } - - /// - /// Gets message language. - /// - public string Language { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.IsAlwaysDisplay = this.ReadBoolean(); - this.Message = this.ReadString(); - this.Language = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.IsAlwaysDisplay); - this.Write(this.Message); - this.Write(this.Language); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/DisconnectMessage.cs b/Renci.SshNet/Messages/Transport/DisconnectMessage.cs deleted file mode 100644 index 3b1d249..0000000 --- a/Renci.SshNet/Messages/Transport/DisconnectMessage.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_DISCONNECT message. - /// - [Message("SSH_MSG_DISCONNECT", 1)] - public class DisconnectMessage : Message, IKeyExchangedAllowed - { - /// - /// Gets disconnect reason code. - /// - public DisconnectReason ReasonCode { get; private set; } - - /// - /// Gets disconnect description. - /// - public string Description { get; private set; } - - /// - /// Gets message language. - /// - public string Language { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public DisconnectMessage() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The reason code. - /// The message. - public DisconnectMessage(DisconnectReason reasonCode, string message) - { - this.ReasonCode = reasonCode; - this.Description = message; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.ReasonCode = (DisconnectReason)this.ReadUInt32(); - this.Description = this.ReadString(); - this.Language = this.ReadString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write((uint)this.ReasonCode); - this.Write(this.Description); - this.Write(this.Language ?? "en"); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/DisconnectReason.cs b/Renci.SshNet/Messages/Transport/DisconnectReason.cs deleted file mode 100644 index 5056fe0..0000000 --- a/Renci.SshNet/Messages/Transport/DisconnectReason.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Provides list of disconnect reason as specified by the protocol. - /// - public enum DisconnectReason - { - /// - /// Disconnect reason is not provided. - /// - None = 0, - /// - /// SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT - /// - HostNotAllowedToConnect = 1, - /// - /// SSH_DISCONNECT_PROTOCOL_ERROR - /// - ProtocolError = 2, - /// - /// SSH_DISCONNECT_KEY_EXCHANGE_FAILED - /// - KeyExchangeFailed = 3, - /// - /// SSH_DISCONNECT_RESERVED - /// - Reserved = 4, - /// - /// SSH_DISCONNECT_MAC_ERROR - /// - MacError = 5, - /// - /// SSH_DISCONNECT_COMPRESSION_ERROR - /// - CompressionError = 6, - /// - /// SSH_DISCONNECT_SERVICE_NOT_AVAILABLE - /// - ServiceNotAvailable = 7, - /// - /// SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED - /// - ProtocolVersionNotSupported = 8, - /// - /// SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE - /// - HostKeyNotVerifiable = 9, - /// - /// SSH_DISCONNECT_CONNECTION_LOST - /// - ConnectionLost = 10, - /// - /// SSH_DISCONNECT_BY_APPLICATION - /// - ByApplication = 11, - /// - /// SSH_DISCONNECT_TOO_MANY_CONNECTIONS - /// - TooManyConnections = 12, - /// - /// SSH_DISCONNECT_AUTH_CANCELLED_BY_USER - /// - AuthenticationCanceledByUser = 13, - /// - /// SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE - /// - NoMoreAuthenticationMethodsAvailable = 14, - /// - /// SSH_DISCONNECT_ILLEGAL_USER_NAME - /// - IllegalUserName = 15, - } -} diff --git a/Renci.SshNet/Messages/Transport/IKeyExchangedAllowed.cs b/Renci.SshNet/Messages/Transport/IKeyExchangedAllowed.cs deleted file mode 100644 index 04cc320..0000000 --- a/Renci.SshNet/Messages/Transport/IKeyExchangedAllowed.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Indicates that message that implement this interface is allowed during key exchange phase - /// - public interface IKeyExchangedAllowed - { - } -} diff --git a/Renci.SshNet/Messages/Transport/IgnoreMessage.cs b/Renci.SshNet/Messages/Transport/IgnoreMessage.cs deleted file mode 100644 index 09c63dc..0000000 --- a/Renci.SshNet/Messages/Transport/IgnoreMessage.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_IGNORE message. - /// - [Message("SSH_MSG_IGNORE", 2)] - public class IgnoreMessage : Message - { - /// - /// Gets ignore message data if any. - /// - public byte[] Data { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public IgnoreMessage() - { - this.Data = new byte[] { }; - } - - /// - /// Initializes a new instance of the class. - /// - /// The data. - public IgnoreMessage(byte[] data) - { - this.Data = data; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.Data = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.WriteBinaryString(this.Data); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs deleted file mode 100644 index 2c0211d..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEX_DH_GEX_GROUP message. - /// - [Message("SSH_MSG_KEX_DH_GEX_GROUP", 31)] - public class KeyExchangeDhGroupExchangeGroup : Message - { - /// - /// Gets or sets the safe prime. - /// - /// - /// The safe prime. - /// - public BigInteger SafePrime { get; private set; } - - /// - /// Gets or sets the generator for subgroup in GF(p). - /// - /// - /// The sub group. - /// - public BigInteger SubGroup { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.SafePrime = this.ReadBigInt(); - this.SubGroup = this.ReadBigInt(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.SafePrime); - this.Write(this.SubGroup); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs deleted file mode 100644 index aa14aaa..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeInit.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEX_DH_GEX_INIT message. - /// - [Message("SSH_MSG_KEX_DH_GEX_INIT", 32)] - internal class KeyExchangeDhGroupExchangeInit : Message, IKeyExchangedAllowed - { - /// - /// Gets the E value. - /// - public BigInteger E { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The client exchange value. - public KeyExchangeDhGroupExchangeInit(BigInteger clientExchangeValue) - { - this.E = clientExchangeValue; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.E = this.ReadBigInt(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.E); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeReply.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeReply.cs deleted file mode 100644 index c314785..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeReply.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEX_DH_GEX_REPLY message. - /// - [Message("SSH_MSG_KEX_DH_GEX_REPLY", 33)] - internal class KeyExchangeDhGroupExchangeReply : Message - { - /// - /// Gets server public host key and certificates - /// - /// The host key. - public byte[] HostKey { get; private set; } - - /// - /// Gets the F value. - /// - public BigInteger F { get; private set; } - - /// - /// Gets the signature of H. - /// - /// The signature. - public byte[] Signature { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.HostKey = this.ReadBinaryString(); - this.F = this.ReadBigInt(); - this.Signature = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.WriteBinaryString(this.HostKey); - this.Write(this.F); - this.WriteBinaryString(this.Signature); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs deleted file mode 100644 index fe36991..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeRequest.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEX_DH_GEX_REQUEST message. - /// - [Message("SSH_MSG_KEX_DH_GEX_REQUEST", 34)] - internal class KeyExchangeDhGroupExchangeRequest : Message, IKeyExchangedAllowed - { - /// - /// Gets or sets the minimal size in bits of an acceptable group. - /// - /// - /// The minimum. - /// - public UInt32 Minimum { get; private set; } - - /// - /// Gets or sets the preferred size in bits of the group the server will send. - /// - /// - /// The preferred. - /// - public UInt32 Preferred { get; private set; } - - /// - /// Gets or sets the maximal size in bits of an acceptable group. - /// - /// - /// The maximum. - /// - public UInt32 Maximum { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The minimum. - /// The preferred. - /// The maximum. - public KeyExchangeDhGroupExchangeRequest(uint minimum, uint preferred, uint maximum) - { - this.Minimum = minimum; - this.Preferred = preferred; - this.Maximum = maximum; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.Minimum = this.ReadUInt32(); - this.Preferred = this.ReadUInt32(); - this.Maximum = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.Minimum); - this.Write(this.Preferred); - this.Write(this.Maximum); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs deleted file mode 100644 index 011c5b4..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhInitMessage.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEXDH_INIT message. - /// - [Message("SSH_MSG_KEXDH_INIT", 30)] - internal class KeyExchangeDhInitMessage : Message, IKeyExchangedAllowed - { - /// - /// Gets the E value. - /// - public BigInteger E { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The client exchange value. - public KeyExchangeDhInitMessage(BigInteger clientExchangeValue) - { - this.E = clientExchangeValue; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.ResetReader(); - this.E = this.ReadBigInt(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.E); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs deleted file mode 100644 index 1ac2186..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeDhReplyMessage.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEXDH_REPLY message. - /// - [Message("SSH_MSG_KEXDH_REPLY", 31)] - public class KeyExchangeDhReplyMessage : Message - { - /// - /// Gets server public host key and certificates - /// - /// The host key. - public byte[] HostKey { get; private set; } - - /// - /// Gets the F value. - /// - public BigInteger F { get; private set; } - - /// - /// Gets the signature of H. - /// - /// The signature. - public byte[] Signature { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.ResetReader(); - this.HostKey = this.ReadBinaryString(); - this.F = this.ReadBigInt(); - this.Signature = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.WriteBinaryString(this.HostKey); - this.Write(this.F); - this.WriteBinaryString(this.Signature); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs deleted file mode 100644 index 8701cdf..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Renci.SshNet.Common; -using System.Linq; -using System.Collections.Generic; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEXECDH_INIT message. - /// - [Message("SSH_MSG_KEXECDH_INIT", 30)] - internal class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed - { - /// - /// Gets the client's ephemeral contribution to the ECDH exchange, encoded as an octet string - /// - public byte[] QC { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - public KeyExchangeEcdhInitMessage(BigInteger d, BigInteger q) - { - var data = new List(); - data.Add(0x04); - data.AddRange(d.ToByteArray().Reverse()); - data.AddRange(q.ToByteArray().Reverse()); - this.QC = data.ToArray(); - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.ResetReader(); - this.QC = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.WriteBinaryString(this.QC); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs deleted file mode 100644 index 8b67f73..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEXECDH_REPLY message. - /// - [Message("SSH_MSG_KEXECDH_REPLY", 31)] - public class KeyExchangeEcdhReplyMessage : Message - { - /// - /// Gets a string encoding an X.509v3 certificate containing the server's ECDSA public host key - /// - /// The host key. - public byte[] KS { get; private set; } - - /// - /// Gets the the server's ephemeral contribution to the ECDH exchange, encoded as an octet string. - /// - public byte[] QS { get; private set; } - - /// - /// Gets the an octet string containing the server's signature of the newly established exchange hash value. - /// - /// The signature. - public byte[] Signature { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.ResetReader(); - this.KS = this.ReadBinaryString(); - this.QS = this.ReadBinaryString(); - this.Signature = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.WriteBinaryString(this.KS); - this.WriteBinaryString(this.QS); - this.WriteBinaryString(this.Signature); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs b/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs deleted file mode 100644 index 9527d28..0000000 --- a/Renci.SshNet/Messages/Transport/KeyExchangeInitMessage.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Security.Cryptography; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEXINIT message. - /// - [Message("SSH_MSG_KEXINIT", 20)] - public class KeyExchangeInitMessage : Message, IKeyExchangedAllowed - { - private static readonly RNGCryptoServiceProvider _randomizer = new RNGCryptoServiceProvider(); - - /// - /// Initializes a new instance of the class. - /// - public KeyExchangeInitMessage() - { - var cookie = new byte[16]; - _randomizer.GetBytes(cookie); - this.Cookie = cookie; - } - - #region Message Properties - - /// - /// Gets session cookie. - /// - public byte[] Cookie { get; private set; } - - /// - /// Gets or sets supported key exchange algorithms. - /// - /// - /// Supported key exchange algorithms. - /// - public string[] KeyExchangeAlgorithms { get; set; } - - /// - /// Gets or sets supported server host key algorithms. - /// - /// - /// Supported server host key algorithms. - /// - public string[] ServerHostKeyAlgorithms { get; set; } - - /// - /// Gets or sets supported encryption algorithms client to server. - /// - /// - /// Supported encryption algorithms client to server. - /// - public string[] EncryptionAlgorithmsClientToServer { get; set; } - - /// - /// Gets or sets supported encryption algorithms server to client. - /// - /// - /// Supported encryption algorithms server to client. - /// - public string[] EncryptionAlgorithmsServerToClient { get; set; } - - /// - /// Gets or sets supported hash algorithms client to server. - /// - /// - /// Supported hash algorithms client to server. - /// - public string[] MacAlgorithmsClientToServer { get; set; } - - /// - /// Gets or sets supported hash algorithms server to client. - /// - /// - /// Supported hash algorithms server to client. - /// - public string[] MacAlgorithmsServerToClient { get; set; } - - /// - /// Gets or sets supported compression algorithms client to server. - /// - /// - /// Supported compression algorithms client to server. - /// - public string[] CompressionAlgorithmsClientToServer { get; set; } - - /// - /// Gets or sets supported compression algorithms server to client. - /// - /// - /// Supported compression algorithms server to client. - /// - public string[] CompressionAlgorithmsServerToClient { get; set; } - - /// - /// Gets or sets supported languages client to server. - /// - /// - /// Supported languages client to server. - /// - public string[] LanguagesClientToServer { get; set; } - - /// - /// Gets or sets supported languages server to client. - /// - /// - /// The languages server to client. - /// - public string[] LanguagesServerToClient { get; set; } - - /// - /// Gets or sets a value indicating whether first key exchange packet follows. - /// - /// - /// true if first key exchange packet follows; otherwise, false. - /// - public bool FirstKexPacketFollows { get; set; } - - /// - /// Gets or sets the reserved value. - /// - /// - /// The reserved value. - /// - public UInt32 Reserved { get; set; } - - #endregion - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.ResetReader(); - - this.Cookie = this.ReadBytes(16); - this.KeyExchangeAlgorithms = this.ReadNamesList(); - this.ServerHostKeyAlgorithms = this.ReadNamesList(); - this.EncryptionAlgorithmsClientToServer = this.ReadNamesList(); - this.EncryptionAlgorithmsServerToClient = this.ReadNamesList(); - this.MacAlgorithmsClientToServer = this.ReadNamesList(); - this.MacAlgorithmsServerToClient = this.ReadNamesList(); - this.CompressionAlgorithmsClientToServer = this.ReadNamesList(); - this.CompressionAlgorithmsServerToClient = this.ReadNamesList(); - this.LanguagesClientToServer = this.ReadNamesList(); - this.LanguagesServerToClient = this.ReadNamesList(); - this.FirstKexPacketFollows = this.ReadBoolean(); - this.Reserved = this.ReadUInt32(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.Cookie); - this.Write(this.KeyExchangeAlgorithms); - this.Write(this.ServerHostKeyAlgorithms); - this.Write(this.EncryptionAlgorithmsClientToServer); - this.Write(this.EncryptionAlgorithmsServerToClient); - this.Write(this.MacAlgorithmsClientToServer); - this.Write(this.MacAlgorithmsServerToClient); - this.Write(this.CompressionAlgorithmsClientToServer); - this.Write(this.CompressionAlgorithmsServerToClient); - this.Write(this.LanguagesClientToServer); - this.Write(this.LanguagesServerToClient); - this.Write(this.FirstKexPacketFollows); - this.Write(this.Reserved); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/NewKeysMessage.cs b/Renci.SshNet/Messages/Transport/NewKeysMessage.cs deleted file mode 100644 index 62dcae7..0000000 --- a/Renci.SshNet/Messages/Transport/NewKeysMessage.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_KEXINIT message. - /// - [Message("SSH_MSG_NEWKEYS", 21)] - public class NewKeysMessage : Message, IKeyExchangedAllowed - { - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - } - } -} diff --git a/Renci.SshNet/Messages/Transport/ServiceAcceptMessage.cs b/Renci.SshNet/Messages/Transport/ServiceAcceptMessage.cs deleted file mode 100644 index c4811c8..0000000 --- a/Renci.SshNet/Messages/Transport/ServiceAcceptMessage.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_SERVICE_ACCEPT message. - /// - [Message("SSH_MSG_SERVICE_ACCEPT", 6)] - public class ServiceAcceptMessage : Message - { - /// - /// Gets the name of the service. - /// - /// - /// The name of the service. - /// - public ServiceName ServiceName { get; private set; } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - var serviceName = this.ReadAsciiString(); - switch (serviceName) - { - case "ssh-userauth": - this.ServiceName = ServiceName.UserAuthentication; - break; - case "ssh-connection": - this.ServiceName = ServiceName.Connection; - break; - } - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - throw new InvalidOperationException("Save data is not supported."); - } - } -} diff --git a/Renci.SshNet/Messages/Transport/ServiceRequestMessage.cs b/Renci.SshNet/Messages/Transport/ServiceRequestMessage.cs deleted file mode 100644 index d274317..0000000 --- a/Renci.SshNet/Messages/Transport/ServiceRequestMessage.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; - -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_SERVICE_REQUEST message. - /// - [Message("SSH_MSG_SERVICE_REQUEST", 5)] - public class ServiceRequestMessage : Message - { - /// - /// Gets the name of the service. - /// - /// - /// The name of the service. - /// - public ServiceName ServiceName { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the service. - public ServiceRequestMessage(ServiceName serviceName) - { - this.ServiceName = serviceName; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - throw new InvalidOperationException("Load data is not supported."); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - switch (this.ServiceName) - { - case ServiceName.UserAuthentication: - this.WriteAscii("ssh-userauth"); - break; - case ServiceName.Connection: - this.WriteAscii("ssh-connection"); - break; - default: - throw new NotSupportedException("Not supported service name"); - } - - } - } -} diff --git a/Renci.SshNet/Messages/Transport/UnimplementedMessage.cs b/Renci.SshNet/Messages/Transport/UnimplementedMessage.cs deleted file mode 100644 index 7dd11b9..0000000 --- a/Renci.SshNet/Messages/Transport/UnimplementedMessage.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Renci.SshNet.Messages.Transport -{ - /// - /// Represents SSH_MSG_UNIMPLEMENTED message. - /// - [Message("SSH_MSG_UNIMPLEMENTED", 3)] - public class UnimplementedMessage : Message - { - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - } - } -} diff --git a/Renci.SshNet/NetConfClient.cs b/Renci.SshNet/NetConfClient.cs deleted file mode 100644 index e6d6f23..0000000 --- a/Renci.SshNet/NetConfClient.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System; -using Renci.SshNet.Common; -using Renci.SshNet.NetConf; -using System.Xml; -using System.Diagnostics.CodeAnalysis; - -namespace Renci.SshNet -{ - // TODO: Please help with documentation here, as I don't know the details, specially for the methods not documented. - /// - /// Contains operation for working with NetConf server. - /// - public partial class NetConfClient : BaseClient - { - /// - /// Holds instance that used to communicate to the server - /// - private NetConfSession _netConfSession; - - /// - /// Gets or sets the operation timeout. - /// - /// - /// The timeout to wait until an operation completes. The default value is negative - /// one (-1) milliseconds, which indicates an infinite time-out period. - /// - public TimeSpan OperationTimeout { get; set; } - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// is null. - public NetConfClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public NetConfClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - public NetConfClient(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public NetConfClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - public NetConfClient(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// Specified whether this instance owns the connection info. - /// is null. - /// - /// If is true, then the - /// connection info will be disposed when this instance is disposed. - /// - private NetConfClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo) - : base(connectionInfo, ownsConnectionInfo) - { - this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); - this.AutomaticMessageIdHandling = true; - } - - #endregion - - /// - /// Gets NetConf server capabilities. - /// - /// Client is not connected. - public XmlDocument ServerCapabilities - { - get { return this._netConfSession.ServerCapabilities; } - } - - /// - /// Gets NetConf client capabilities. - /// - /// Client is not connected. - public XmlDocument ClientCapabilities - { - get { return this._netConfSession.ClientCapabilities; } - } - - /// - /// Gets or sets a value indicating whether automatic message id handling is - /// enabled. - /// - /// - /// true if automatic message id handling is enabled; otherwise, false. - /// The default value is true. - /// - public bool AutomaticMessageIdHandling { get; set; } - - /// - /// Sends the receive RPC. - /// - /// The RPC. - /// Reply message to RPC request - /// Client is not connected. - public XmlDocument SendReceiveRpc(XmlDocument rpc) - { - return this._netConfSession.SendReceiveRpc(rpc, this.AutomaticMessageIdHandling); - } - - /// - /// Sends the receive RPC. - /// - /// The XML. - /// Reply message to RPC request - public XmlDocument SendReceiveRpc(string xml) - { - var rpc = new XmlDocument(); - rpc.LoadXml(xml); - return SendReceiveRpc(rpc); - } - - /// - /// Sends the close RPC. - /// - /// Reply message to closing RPC request - /// Client is not connected. - public XmlDocument SendCloseRpc() - { - var rpc = new XmlDocument(); - rpc.LoadXml(""); - return this._netConfSession.SendReceiveRpc(rpc, this.AutomaticMessageIdHandling); - } - - /// - /// Called when client is connected to the server. - /// - protected override void OnConnected() - { - base.OnConnected(); - - this._netConfSession = new NetConfSession(this.Session, this.OperationTimeout); - this._netConfSession.Connect(); - } - - /// - /// Called when client is disconnecting from the server. - /// - protected override void OnDisconnecting() - { - base.OnDisconnecting(); - - this._netConfSession.Disconnect(); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - if (this._netConfSession != null) - { - this._netConfSession.Dispose(); - this._netConfSession = null; - } - - base.Dispose(disposing); - } - } -} diff --git a/Renci.SshNet/Netconf/NetConfSession.cs b/Renci.SshNet/Netconf/NetConfSession.cs deleted file mode 100644 index 7ea1fb2..0000000 --- a/Renci.SshNet/Netconf/NetConfSession.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using Renci.SshNet.Common; -using Renci.SshNet.Sftp; -using System.Xml; -using System.Text.RegularExpressions; - -namespace Renci.SshNet.NetConf -{ - internal class NetConfSession : SubsystemSession - { - private readonly StringBuilder _data = new StringBuilder(); - - private bool _usingFramingProtocol; - - private const string _prompt = "]]>]]>"; - - private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(false); - - private EventWaitHandle _rpcReplyReceived = new AutoResetEvent(false); - - private StringBuilder _rpcReply = new StringBuilder(); - - private int _messageId; - - /// - /// Gets NetConf server capabilities. - /// - public XmlDocument ServerCapabilities { get; private set; } - - /// - /// Gets NetConf client capabilities. - /// - public XmlDocument ClientCapabilities { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The session. - /// The operation timeout. - public NetConfSession(Session session, TimeSpan operationTimeout) - : base(session, "netconf", operationTimeout, Encoding.UTF8) - { - ClientCapabilities = new XmlDocument(); - ClientCapabilities.LoadXml("" + - "" + - "" + - "" + - "urn:ietf:params:netconf:base:1.0" + - "" + - "" + - ""); - - } - - public XmlDocument SendReceiveRpc(XmlDocument rpc, bool automaticMessageIdHandling) - { - this._data.Clear(); - - XmlNamespaceManager ns = null; - if (automaticMessageIdHandling) - { - _messageId++; - ns = new XmlNamespaceManager(rpc.NameTable); - ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0"); - rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value = _messageId.ToString(); - } - _rpcReply = new StringBuilder(); - _rpcReplyReceived.Reset(); - var reply = new XmlDocument(); - if (_usingFramingProtocol) - { - StringBuilder command = new StringBuilder(rpc.InnerXml.Length + 10); - command.AppendFormat("\n#{0}\n", rpc.InnerXml.Length); - command.Append(rpc.InnerXml); - command.Append("\n##\n"); - this.SendData(Encoding.UTF8.GetBytes(command.ToString())); - - this.WaitOnHandle(this._rpcReplyReceived, this._operationTimeout); - reply.LoadXml(_rpcReply.ToString()); - } - else - { - this.SendData(Encoding.UTF8.GetBytes(rpc.InnerXml + _prompt)); - this.WaitOnHandle(this._rpcReplyReceived, this._operationTimeout); - reply.LoadXml(_rpcReply.ToString()); - } - if (automaticMessageIdHandling) - { - //string reply_id = rpc.SelectSingleNode("/nc:rpc-reply/@message-id", ns).Value; - string reply_id = rpc.SelectSingleNode("/nc:rpc/@message-id", ns).Value; - if (reply_id != _messageId.ToString()) - { - throw new NetConfServerException("The rpc message id does not match the rpc-reply message id."); - } - } - return reply; - } - - protected override void OnChannelOpen() - { - this._data.Clear(); - - string message = string.Format("{0}{1}", this.ClientCapabilities.InnerXml, _prompt); - - this.SendData(Encoding.UTF8.GetBytes(message)); - - this.WaitOnHandle(this._serverCapabilitiesConfirmed, this._operationTimeout); - } - - protected override void OnDataReceived(uint dataTypeCode, byte[] data) - { - string chunk = Encoding.UTF8.GetString(data); - - if (this.ServerCapabilities == null) // This must be server capabilities, old protocol - { - this._data.Append(chunk); - - if (!chunk.Contains(_prompt)) - { - return; - } - try - { - chunk = this._data.ToString(); - this._data.Clear(); - - this.ServerCapabilities = new XmlDocument(); - this.ServerCapabilities.LoadXml(chunk.Replace(_prompt, "")); - } - catch (XmlException e) - { - throw new NetConfServerException("Server capabilities received are not well formed XML", e); - } - - XmlNamespaceManager ns = new XmlNamespaceManager(this.ServerCapabilities.NameTable); - - ns.AddNamespace("nc", "urn:ietf:params:xml:ns:netconf:base:1.0"); - - this._usingFramingProtocol = (this.ServerCapabilities.SelectSingleNode("/nc:hello/nc:capabilities/nc:capability[text()='urn:ietf:params:netconf:base:1.1']", ns) != null); - - this._serverCapabilitiesConfirmed.Set(); - } - else if (this._usingFramingProtocol) - { - int position = 0; - - for (; ; ) - { - Match match = Regex.Match(chunk.Substring(position), @"\n#(?\d+)\n"); - if (!match.Success) - { - break; - } - int fractionLength = Convert.ToInt32(match.Groups["length"].Value); - this._rpcReply.Append(chunk, position + match.Index + match.Length, fractionLength); - position += match.Index + match.Length + fractionLength; - } - if (Regex.IsMatch(chunk.Substring(position), @"\n##\n")) - { - this._rpcReplyReceived.Set(); - } - } - else // Old protocol - { - this._data.Append(chunk); - - if (!chunk.Contains(_prompt)) - { - return; - //throw new NetConfServerException("Server XML message does not end with the prompt " + _prompt); - } - - chunk = this._data.ToString(); - this._data.Clear(); - - this._rpcReply.Append(chunk.Replace(_prompt, "")); - this._rpcReplyReceived.Set(); - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - if (this._serverCapabilitiesConfirmed != null) - { - this._serverCapabilitiesConfirmed.Dispose(); - this._serverCapabilitiesConfirmed = null; - } - - if (this._rpcReplyReceived != null) - { - this._rpcReplyReceived.Dispose(); - this._rpcReplyReceived = null; - } - } - } - } -} diff --git a/Renci.SshNet/NoneAuthenticationMethod.cs b/Renci.SshNet/NoneAuthenticationMethod.cs deleted file mode 100644 index fb294c6..0000000 --- a/Renci.SshNet/NoneAuthenticationMethod.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages; - -namespace Renci.SshNet -{ - /// - /// Provides functionality for "none" authentication method - /// - public class NoneAuthenticationMethod : AuthenticationMethod, IDisposable - { - private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - - /// - /// Gets connection name - /// - public override string Name - { - get { return "none"; } - } - - /// - /// Initializes a new instance of the class. - /// - /// The username. - /// is whitespace or null. - public NoneAuthenticationMethod(string username) - : base(username) - { - - } - - /// - /// Authenticates the specified session. - /// - /// The session. - /// - /// Result of authentication process. - /// - /// is null. - public override AuthenticationResult Authenticate(Session session) - { - if (session == null) - throw new ArgumentNullException("session"); - - session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; - - session.SendMessage(new RequestMessageNone(ServiceName.Connection, this.Username)); - - session.WaitOnHandle(this._authenticationCompleted); - - session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; - - return this._authenticationResult; - } - - private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) - { - this._authenticationResult = AuthenticationResult.Success; - - this._authenticationCompleted.Set(); - } - - private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) - { - if (e.Message.PartialSuccess) - this._authenticationResult = AuthenticationResult.PartialSuccess; - else - this._authenticationResult = AuthenticationResult.Failure; - - // Copy allowed authentication methods - this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); - - this._authenticationCompleted.Set(); - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._authenticationCompleted != null) - { - this._authenticationCompleted.Dispose(); - this._authenticationCompleted = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~NoneAuthenticationMethod() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - } -} diff --git a/Renci.SshNet/NoneConnectionInfo.cs b/Renci.SshNet/NoneConnectionInfo.cs deleted file mode 100644 index d7236c9..0000000 --- a/Renci.SshNet/NoneConnectionInfo.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages; - -namespace Renci.SshNet -{ - /// - /// Provides connection information when password authentication method is used - /// - public class NoneConnectionInfo : ConnectionInfo, IDisposable - { - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - - /// - /// Gets list of allowed authentications. - /// - public IEnumerable AllowedAuthentications { get; private set; } - - /// - /// Gets connection name - /// - public override string Name - { - get - { - return "none"; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The host. - /// The username. - public NoneConnectionInfo(string host, string username) - : this(host, 22, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The host. - /// The port. - /// The username. - public NoneConnectionInfo(string host, int port, string username) - : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public NoneConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public NoneConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public NoneConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, 22, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public NoneConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - public NoneConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) - : this(host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - public NoneConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) - : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) - { - } - - /// - /// Called when connection needs to be authenticated. - /// - protected override void OnAuthenticate() - { - this.SendMessage(new RequestMessageNone(ServiceName.Connection, this.Username)); - - this.WaitHandle(this._authenticationCompleted); - } - - /// - /// Handles the UserAuthenticationSuccessMessageReceived event of the session. - /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationSuccessMessageReceived(object sender, MessageEventArgs e) - { - base.Session_UserAuthenticationSuccessMessageReceived(sender, e); - this._authenticationCompleted.Set(); - } - - /// - /// Handles the UserAuthenticationFailureReceived event of the session. - /// - /// The source of the event. - /// The event data. - protected override void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) - { - base.Session_UserAuthenticationFailureReceived(sender, e); - - // Copy allowed authentication methods - this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); - - this._authenticationCompleted.Set(); - } - - #region IDisposable Members - - private bool isDisposed = false; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this.isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._authenticationCompleted != null) - { - this._authenticationCompleted.Dispose(); - this._authenticationCompleted = null; - } - } - - // Note disposing has been done. - isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~NoneConnectionInfo() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } - -} diff --git a/Renci.SshNet/Pageant/COPYDATASTRUCT.cs b/Renci.SshNet/Pageant/COPYDATASTRUCT.cs deleted file mode 100644 index 350dda4..0000000 --- a/Renci.SshNet/Pageant/COPYDATASTRUCT.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Renci.SshNet.Pageant -{ - [StructLayout(LayoutKind.Sequential, Pack = 4)] - internal struct COPYDATASTRUCT - { - public COPYDATASTRUCT(int dwData, string lpData) - { - this.dwData = dwData; - this.lpData = lpData; - cbData = lpData.Length + 1; - } - - - private readonly int dwData; - - private readonly int cbData; - - [MarshalAs(UnmanagedType.LPStr)] private readonly string lpData; - } -} \ No newline at end of file diff --git a/Renci.SshNet/Pageant/NativeMethods.cs b/Renci.SshNet/Pageant/NativeMethods.cs deleted file mode 100644 index 0fe2bd6..0000000 --- a/Renci.SshNet/Pageant/NativeMethods.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Renci.SshNet.Pageant -{ - internal class NativeMethods - { - [DllImport("user32.dll", EntryPoint = "SendMessageA", CallingConvention = CallingConvention.StdCall, - ExactSpelling = true)] - public static extern IntPtr SendMessage(IntPtr hWnd, int dwMsg, IntPtr wParam, ref COPYDATASTRUCT lParam); - - [DllImportAttribute("user32.dll", EntryPoint = "FindWindowA", CallingConvention = CallingConvention.Winapi, - ExactSpelling = true)] - public static extern IntPtr FindWindow([MarshalAsAttribute(UnmanagedType.LPStr)] string lpClassName, - [MarshalAsAttribute(UnmanagedType.LPStr)] string lpWindowName); - } -} \ No newline at end of file diff --git a/Renci.SshNet/Pageant/PageantProtocol.cs b/Renci.SshNet/Pageant/PageantProtocol.cs deleted file mode 100644 index 5a16f36..0000000 --- a/Renci.SshNet/Pageant/PageantProtocol.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.MemoryMappedFiles; -using System.Linq; -using System.Net; -using System.Text; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Pageant -{ - public class PageantProtocol:IAgentProtocol - { - - #region Constants - - private const int WM_COPYDATA = 0x004A; - - private const int AGENT_COPYDATA_ID = unchecked((int)0x804e50ba); - - private const int AGENT_MAX_MSGLEN = 8192; - - public const byte SSH2_AGENTC_REQUEST_IDENTITIES = 11; - - public const byte SSH2_AGENT_IDENTITIES_ANSWER = 12; - - public const byte SSH2_AGENTC_SIGN_REQUEST = 13; - - public const byte SSH2_AGENT_SIGN_RESPONSE = 14; - - #endregion - - - public static bool IsRunning - { - get - { - var hWnd = NativeMethods.FindWindow("Pageant", "Pageant"); - - return hWnd != IntPtr.Zero; - } - } - - - - public PageantProtocol() - { - var hWnd = NativeMethods.FindWindow("Pageant", "Pageant"); - - if (hWnd == IntPtr.Zero) - { - throw new SshException("Pageant not running"); - } - - } - - - #region Implementation of IAgentProtocol - - IEnumerable IAgentProtocol.GetIdentities() - { - var hWnd = NativeMethods.FindWindow("Pageant", "Pageant"); - - if (hWnd == IntPtr.Zero) - { - yield break; - } - - string mmFileName = Path.GetRandomFileName(); - - using (var mmFile = MemoryMappedFile.CreateNew(mmFileName, AGENT_MAX_MSGLEN)) - { - using (var accessor = mmFile.CreateViewAccessor()) - { - var security = mmFile.GetAccessControl(); - security.SetOwner(System.Security.Principal.WindowsIdentity.GetCurrent().User); - mmFile.SetAccessControl(security); - - accessor.Write(0, IPAddress.NetworkToHostOrder(AGENT_MAX_MSGLEN - 4)); - accessor.Write(4, SSH2_AGENTC_REQUEST_IDENTITIES); - - - var copy = new COPYDATASTRUCT(AGENT_COPYDATA_ID, mmFileName); - - if (NativeMethods.SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ref copy) == IntPtr.Zero) - { - yield break; - } - - if (accessor.ReadByte(4) != SSH2_AGENT_IDENTITIES_ANSWER) - { - yield break; - } - - int numberOfIdentities = IPAddress.HostToNetworkOrder(accessor.ReadInt32(5)); - - - if (numberOfIdentities == 0) - { - yield break; - } - - int position = 9; - for (int i = 0; i < numberOfIdentities; i++) - { - int blobSize = IPAddress.HostToNetworkOrder(accessor.ReadInt32(position)); - position += 4; - - var blob = new byte[blobSize]; - - accessor.ReadArray(position, blob, 0, blobSize); - position += blobSize; - int commnetLenght = IPAddress.HostToNetworkOrder(accessor.ReadInt32(position)); - position += 4; - var commentChars = new byte[commnetLenght]; - accessor.ReadArray(position, commentChars, 0, commnetLenght); - position += commnetLenght; - - string comment = Encoding.ASCII.GetString(commentChars); - string type = Encoding.ASCII.GetString(blob, 4, 7);// needs more testing kind of hack - - yield return new IdentityReference(type,blob,comment); - - } - } - - } - } - - byte[] IAgentProtocol.SignData(IdentityReference identity, byte[] data) - { - var hWnd = NativeMethods.FindWindow("Pageant", "Pageant"); - - if (hWnd == IntPtr.Zero) - { - return new byte[0]; - } - - string mmFileName = Path.GetRandomFileName(); - - using (var mmFile = MemoryMappedFile.CreateNew(mmFileName, AGENT_MAX_MSGLEN)) - { - using (var accessor = mmFile.CreateViewAccessor()) - { - var security = mmFile.GetAccessControl(); - security.SetOwner(System.Security.Principal.WindowsIdentity.GetCurrent().User); - mmFile.SetAccessControl(security); - - accessor.Write(0, IPAddress.NetworkToHostOrder(AGENT_MAX_MSGLEN - 4)); - accessor.Write(4, SSH2_AGENTC_SIGN_REQUEST); - accessor.Write(5, IPAddress.NetworkToHostOrder(identity.Blob.Length)); - accessor.WriteArray(9, identity.Blob, 0, identity.Blob.Length); - accessor.Write(9 + identity.Blob.Length, IPAddress.NetworkToHostOrder(data.Length)); - accessor.WriteArray(13 + identity.Blob.Length, data, 0, data.Length); - - - - var copy = new COPYDATASTRUCT(AGENT_COPYDATA_ID, mmFileName); - - if (NativeMethods.SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ref copy) == IntPtr.Zero) - { - return new byte[0]; - } - - if (accessor.ReadByte(4) != SSH2_AGENT_SIGN_RESPONSE) - { - return new byte[0]; - } - - int size = IPAddress.HostToNetworkOrder(accessor.ReadInt32(5)); - var ret = new byte[size]; - accessor.ReadArray(9, ret, 0, size); - return ret; - } - } - } - - #endregion - } -} diff --git a/Renci.SshNet/PasswordAuthenticationMethod.NET40.cs b/Renci.SshNet/PasswordAuthenticationMethod.NET40.cs deleted file mode 100644 index 9848872..0000000 --- a/Renci.SshNet/PasswordAuthenticationMethod.NET40.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - public partial class PasswordAuthenticationMethod : AuthenticationMethod - { - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/PasswordAuthenticationMethod.cs b/Renci.SshNet/PasswordAuthenticationMethod.cs deleted file mode 100644 index e7849a5..0000000 --- a/Renci.SshNet/PasswordAuthenticationMethod.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages; - -namespace Renci.SshNet -{ - /// - /// Provides functionality to perform password authentication. - /// - public partial class PasswordAuthenticationMethod : AuthenticationMethod, IDisposable - { - private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - - private Session _session; - - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - - private Exception _exception; - - private readonly RequestMessage _requestMessage; - - private readonly byte[] _password; - - /// - /// Gets authentication method name - /// - public override string Name - { - get { return this._requestMessage.MethodName; } - } - - /// - /// Occurs when user's password has expired and needs to be changed. - /// - public event EventHandler PasswordExpired; - - /// - /// Initializes a new instance of the class. - /// - /// The username. - /// The password. - /// is whitespace or null. - /// is null. - public PasswordAuthenticationMethod(string username, string password) - : this(username, Encoding.UTF8.GetBytes(password)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The username. - /// The password. - /// is whitespace or null. - /// is null. - public PasswordAuthenticationMethod(string username, byte[] password) - : base(username) - { - if (password == null) - throw new ArgumentNullException("password"); - - this._password = password; - - this._requestMessage = new RequestMessagePassword(ServiceName.Connection, this.Username, this._password); - } - - /// - /// Authenticates the specified session. - /// - /// The session to authenticate. - /// - /// Result of authentication process. - /// - /// is null. - public override AuthenticationResult Authenticate(Session session) - { - if (session == null) - throw new ArgumentNullException("session"); - - this._session = session; - - session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; - session.MessageReceived += Session_MessageReceived; - - session.RegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ"); - - session.SendMessage(this._requestMessage); - - session.WaitOnHandle(this._authenticationCompleted); - - session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; - session.MessageReceived -= Session_MessageReceived; - - - if (this._exception != null) - { - throw this._exception; - } - - return this._authenticationResult; - } - - private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) - { - this._authenticationResult = AuthenticationResult.Success; - - this._authenticationCompleted.Set(); - } - - private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) - { - if (e.Message.PartialSuccess) - this._authenticationResult = AuthenticationResult.PartialSuccess; - else - this._authenticationResult = AuthenticationResult.Failure; - - // Copy allowed authentication methods - this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); - - this._authenticationCompleted.Set(); - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - if (e.Message is PasswordChangeRequiredMessage) - { - this._session.UnRegisterMessage("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ"); - - this.ExecuteThread(() => - { - try - { - var eventArgs = new AuthenticationPasswordChangeEventArgs(this.Username); - - // Raise an event to allow user to supply a new password - if (this.PasswordExpired != null) - { - this.PasswordExpired(this, eventArgs); - } - - // Send new authentication request with new password - this._session.SendMessage(new RequestMessagePassword(ServiceName.Connection, this.Username, this._password, eventArgs.NewPassword)); - } - catch (Exception exp) - { - this._exception = exp; - this._authenticationCompleted.Set(); - } - }); - } - } - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._authenticationCompleted != null) - { - this._authenticationCompleted.Dispose(); - this._authenticationCompleted = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~PasswordAuthenticationMethod() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - } -} diff --git a/Renci.SshNet/PasswordConnectionInfo.NET40.cs b/Renci.SshNet/PasswordConnectionInfo.NET40.cs deleted file mode 100644 index e9b0946..0000000 --- a/Renci.SshNet/PasswordConnectionInfo.NET40.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using System; - -namespace Renci.SshNet -{ - /// - /// Provides connection information when password authentication method is used - /// - public partial class PasswordConnectionInfo - { - partial void ExecuteThread(Action action) - { - Task.Factory.StartNew(action); - } - } -} diff --git a/Renci.SshNet/PasswordConnectionInfo.cs b/Renci.SshNet/PasswordConnectionInfo.cs deleted file mode 100644 index 602b110..0000000 --- a/Renci.SshNet/PasswordConnectionInfo.cs +++ /dev/null @@ -1,322 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using Renci.SshNet.Common; - -namespace Renci.SshNet -{ - /// - /// Provides connection information when password authentication method is used - /// - /// - /// - /// - /// - /// - public class PasswordConnectionInfo : ConnectionInfo, IDisposable - { - /// - /// Occurs when user's password has expired and needs to be changed. - /// - /// - /// - /// - public event EventHandler PasswordExpired; - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - /// - /// - /// - /// is null. - /// is invalid, or is null or contains whitespace characters. - public PasswordConnectionInfo(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password)) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Connection password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - public PasswordConnectionInfo(string host, int port, string username, string password) - : this(host, port, username, Encoding.UTF8.GetBytes(password), ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public PasswordConnectionInfo(string host, int port, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, port, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public PasswordConnectionInfo(string host, int port, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, port, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public PasswordConnectionInfo(string host, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public PasswordConnectionInfo(string host, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - public PasswordConnectionInfo(string host, string username, string password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) - : this(host, ConnectionInfo.DEFAULT_PORT, username, Encoding.UTF8.GetBytes(password), proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - public PasswordConnectionInfo(string host, string username, byte[] password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Connection password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - public PasswordConnectionInfo(string host, int port, string username, byte[] password) - : this(host, port, username, password, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public PasswordConnectionInfo(string host, int port, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, port, username, password, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public PasswordConnectionInfo(string host, int port, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, port, username, password, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - public PasswordConnectionInfo(string host, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password, proxyType, proxyHost, proxyPort, string.Empty, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - public PasswordConnectionInfo(string host, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - public PasswordConnectionInfo(string host, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Connection password. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - public PasswordConnectionInfo(string host, int port, string username, byte[] password, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword) - : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PasswordAuthenticationMethod(username, password)) - { - foreach (var authenticationMethod in this.AuthenticationMethods.OfType()) - { - authenticationMethod.PasswordExpired += AuthenticationMethod_PasswordExpired; - } - } - - private void AuthenticationMethod_PasswordExpired(object sender, AuthenticationPasswordChangeEventArgs e) - { - if (this.PasswordExpired != null) - { - this.PasswordExpired(sender, e); - } - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this.AuthenticationMethods != null) - { - foreach (var authenticationMethods in this.AuthenticationMethods.OfType()) - { - authenticationMethods.Dispose(); - } - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~PasswordConnectionInfo() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/PrivateKeyAuthenticationMethod.cs b/Renci.SshNet/PrivateKeyAuthenticationMethod.cs deleted file mode 100644 index 3fd7c4d..0000000 --- a/Renci.SshNet/PrivateKeyAuthenticationMethod.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Collections.ObjectModel; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Provides functionality to perform private key authentication. - /// - public class PrivateKeyAuthenticationMethod : AuthenticationMethod, IDisposable - { - private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; - - private EventWaitHandle _authenticationCompleted = new ManualResetEvent(false); - - private bool _isSignatureRequired; - - /// - /// Gets authentication method name - /// - public override string Name - { - get { return "publickey"; } - } - - /// - /// Gets the key files used for authentication. - /// - public ICollection KeyFiles { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The username. - /// The key files. - /// is whitespace or null. - public PrivateKeyAuthenticationMethod(string username, params PrivateKeyFile[] keyFiles) - : base(username) - { - if (keyFiles == null) - throw new ArgumentNullException("keyFiles"); - - this.KeyFiles = new Collection(keyFiles); - } - - /// - /// Authenticates the specified session. - /// - /// The session to authenticate. - /// - /// Result of authentication process. - /// - public override AuthenticationResult Authenticate(Session session) - { - session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; - session.MessageReceived += Session_MessageReceived; - - session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK"); - - foreach (var keyFile in this.KeyFiles) - { - this._authenticationCompleted.Reset(); - this._isSignatureRequired = false; - - var message = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data); - - if (this.KeyFiles.Count < 2) - { - // If only one key file provided then send signature for very first request - var signatureData = new SignatureData(message, session.SessionId).GetBytes(); - - message.Signature = keyFile.HostKey.Sign(signatureData); - } - - // Send public key authentication request - session.SendMessage(message); - - session.WaitOnHandle(this._authenticationCompleted); - - if (this._isSignatureRequired) - { - this._authenticationCompleted.Reset(); - - var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection, this.Username, keyFile.HostKey.Name, keyFile.HostKey.Data); - - var signatureData = new SignatureData(message, session.SessionId).GetBytes(); - - signatureMessage.Signature = keyFile.HostKey.Sign(signatureData); - - // Send public key authentication request with signature - session.SendMessage(signatureMessage); - } - - session.WaitOnHandle(this._authenticationCompleted); - - if (this._authenticationResult == AuthenticationResult.Success) - { - break; - } - } - - session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; - session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; - session.MessageReceived -= Session_MessageReceived; - - session.UnRegisterMessage("SSH_MSG_USERAUTH_PK_OK"); - - return this._authenticationResult; - } - - private void Session_UserAuthenticationSuccessReceived(object sender, MessageEventArgs e) - { - this._authenticationResult = AuthenticationResult.Success; - - this._authenticationCompleted.Set(); - } - - private void Session_UserAuthenticationFailureReceived(object sender, MessageEventArgs e) - { - if (e.Message.PartialSuccess) - this._authenticationResult = AuthenticationResult.PartialSuccess; - else - this._authenticationResult = AuthenticationResult.Failure; - - // Copy allowed authentication methods - this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList(); - - this._authenticationCompleted.Set(); - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var publicKeyMessage = e.Message as PublicKeyMessage; - if (publicKeyMessage != null) - { - this._isSignatureRequired = true; - this._authenticationCompleted.Set(); - } - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._authenticationCompleted != null) - { - this._authenticationCompleted.Dispose(); - this._authenticationCompleted = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~PrivateKeyAuthenticationMethod() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - private class SignatureData : SshData - { - private readonly RequestMessagePublicKey _message; - - private readonly byte[] _sessionId; - - public SignatureData(RequestMessagePublicKey message, byte[] sessionId) - { - this._message = message; - this._sessionId = sessionId; - } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.WriteBinaryString(this._sessionId); - this.Write((byte)50); - this.Write(this._message.Username); - this.WriteAscii("ssh-connection"); - this.WriteAscii("publickey"); - this.Write((byte)1); - this.WriteAscii(this._message.PublicKeyAlgorithmName); - this.WriteBinaryString(this._message.PublicKeyData); - } - } - - } -} diff --git a/Renci.SshNet/PrivateKeyConnectionInfo.cs b/Renci.SshNet/PrivateKeyConnectionInfo.cs deleted file mode 100644 index 1c441cb..0000000 --- a/Renci.SshNet/PrivateKeyConnectionInfo.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Collections.ObjectModel; - -namespace Renci.SshNet -{ - /// - /// Provides connection information when private key authentication method is used - /// - /// - /// - /// - public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable - { - /// - /// Gets the key files used for authentication. - /// - public ICollection KeyFiles { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Connection key files. - /// - /// - /// - /// - public PrivateKeyConnectionInfo(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Connection username. - /// Connection key files. - public PrivateKeyConnectionInfo(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The key files. - public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles) - : this(host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The key files. - public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles) - : this(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The key files. - public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The key files. - public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - /// The key files. - public PrivateKeyConnectionInfo(string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// The port. - /// Connection username. - /// Type of the proxy. - /// The proxy host. - /// The proxy port. - /// The proxy username. - /// The proxy password. - /// The key files. - public PrivateKeyConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params PrivateKeyFile[] keyFiles) - : base(host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new PrivateKeyAuthenticationMethod(username, keyFiles)) - { - this.KeyFiles = new Collection(keyFiles); - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this.AuthenticationMethods != null) - { - foreach (var authenticationMethods in this.AuthenticationMethods.OfType()) - { - authenticationMethods.Dispose(); - } - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~PrivateKeyConnectionInfo() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/PrivateKeyFile.cs b/Renci.SshNet/PrivateKeyFile.cs deleted file mode 100644 index cc88020..0000000 --- a/Renci.SshNet/PrivateKeyFile.cs +++ /dev/null @@ -1,422 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using Renci.SshNet.Security; -using Renci.SshNet.Common; -using System.Globalization; -using Renci.SshNet.Security.Cryptography; -using Renci.SshNet.Security.Cryptography.Ciphers; -using Renci.SshNet.Security.Cryptography.Ciphers.Modes; -using Renci.SshNet.Security.Cryptography.Ciphers.Paddings; -using System.Diagnostics.CodeAnalysis; - -namespace Renci.SshNet -{ - /// - /// Represents private key information - /// - /// - /// - /// - public class PrivateKeyFile : IDisposable - { -#if SILVERLIGHT - private static readonly Regex _privateKeyRegex = new Regex(@"^-+ *BEGIN (?\w+( \w+)*) PRIVATE KEY *-+\r?\n(Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k PRIVATE KEY *-+", RegexOptions.Multiline); -#else - private static readonly Regex _privateKeyRegex = new Regex(@"^-+ *BEGIN (?\w+( \w+)*) PRIVATE KEY *-+\r?\n(Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)-+ *END \k PRIVATE KEY *-+", RegexOptions.Compiled | RegexOptions.Multiline); -#endif - - private Key _key; - - /// - /// Gets the host key. - /// - public HostAlgorithm HostKey { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The private key. - public PrivateKeyFile(Stream privateKey) - { - this.Open(privateKey, null); - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the file. - /// is null or empty. - /// This method calls internally, this method does not catch exceptions from . - public PrivateKeyFile(string fileName) - : this(fileName, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Name of the file. - /// The pass phrase. - /// is null or empty, or is null. - /// This method calls internally, this method does not catch exceptions from . - public PrivateKeyFile(string fileName, string passPhrase) - { - if (string.IsNullOrEmpty(fileName)) - throw new ArgumentNullException("fileName"); - - using (var keyFile = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - this.Open(keyFile, passPhrase); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The private key. - /// The pass phrase. - /// or is null. - public PrivateKeyFile(Stream privateKey, string passPhrase) - { - this.Open(privateKey, passPhrase); - } - - /// - /// Opens the specified private key. - /// - /// The private key. - /// The pass phrase. - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "this._key disposed in Dispose(bool) method.")] - private void Open(Stream privateKey, string passPhrase) - { - if (privateKey == null) - throw new ArgumentNullException("privateKey"); - - Match privateKeyMatch; - - using (var sr = new StreamReader(privateKey)) - { - var text = sr.ReadToEnd(); - privateKeyMatch = _privateKeyRegex.Match(text); - } - - if (!privateKeyMatch.Success) - { - throw new SshException("Invalid private key file."); - } - - var keyName = privateKeyMatch.Result("${keyName}"); - var cipherName = privateKeyMatch.Result("${cipherName}"); - var salt = privateKeyMatch.Result("${salt}"); - var data = privateKeyMatch.Result("${data}"); - - var binaryData = Convert.FromBase64String(data); - - byte[] decryptedData; - - if (!string.IsNullOrEmpty(cipherName) && !string.IsNullOrEmpty(salt)) - { - if (string.IsNullOrEmpty(passPhrase)) - throw new SshPassPhraseNullOrEmptyException("Private key is encrypted but passphrase is empty."); - - var binarySalt = new byte[salt.Length / 2]; - for (var i = 0; i < binarySalt.Length; i++) - binarySalt[i] = Convert.ToByte(salt.Substring(i * 2, 2), 16); - - CipherInfo cipher; - switch (cipherName) - { - case "DES-EDE3-CBC": - cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); - break; - case "DES-EDE3-CFB": - cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), new PKCS7Padding())); - break; - case "DES-CBC": - cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); - break; - case "AES-128-CBC": - cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); - break; - case "AES-192-CBC": - cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); - break; - case "AES-256-CBC": - cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); - break; - default: - throw new SshException(string.Format(CultureInfo.CurrentCulture, "Private key cipher \"{0}\" is not supported.", cipherName)); - } - - decryptedData = DecryptKey(cipher, binaryData, passPhrase, binarySalt); - } - else - { - decryptedData = binaryData; - } - - switch (keyName) - { - case "RSA": - this._key = new RsaKey(decryptedData.ToArray()); - this.HostKey = new KeyHostAlgorithm("ssh-rsa", this._key); - break; - case "DSA": - this._key = new DsaKey(decryptedData.ToArray()); - this.HostKey = new KeyHostAlgorithm("ssh-dss", this._key); - break; - case "SSH2 ENCRYPTED": - var reader = new SshDataReader(decryptedData); - var magicNumber = reader.ReadUInt32(); - if (magicNumber != 0x3f6ff9eb) - { - throw new SshException("Invalid SSH2 private key."); - } - - var totalLength = reader.ReadUInt32(); // Read total bytes length including magic number - var keyType = reader.ReadString(); - var ssh2CipherName = reader.ReadString(); - var blobSize = (int)reader.ReadUInt32(); - - byte[] keyData; - if (ssh2CipherName == "none") - { - keyData = reader.ReadBytes(blobSize); - } - //else if (ssh2CipherName == "3des-cbc") - //{ - // var key = GetCipherKey(passPhrase, 192 / 8); - // var ssh2Сipher = new TripleDesCipher(key, null, null); - // keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize)); - //} - else - { - throw new SshException(string.Format("Cipher method '{0}' is not supported.", cipherName)); - } - - // TODO: Create two specific data types to avoid using SshDataReader class - - reader = new SshDataReader(keyData); - - var decryptedLength = reader.ReadUInt32(); - - if (decryptedLength + 4 != blobSize) - throw new SshException("Invalid passphrase."); - - if (keyType == "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}") - { - var exponent = reader.ReadBigIntWithBits();//e - var d = reader.ReadBigIntWithBits();//d - var modulus = reader.ReadBigIntWithBits();//n - var inverseQ = reader.ReadBigIntWithBits();//u - var q = reader.ReadBigIntWithBits();//p - var p = reader.ReadBigIntWithBits();//q - this._key = new RsaKey(modulus, exponent, d, p, q, inverseQ); - this.HostKey = new KeyHostAlgorithm("ssh-rsa", this._key); - } - else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}") - { - var zero = reader.ReadUInt32(); - if (zero != 0) - { - throw new SshException("Invalid private key"); - } - var p = reader.ReadBigIntWithBits(); - var g = reader.ReadBigIntWithBits(); - var q = reader.ReadBigIntWithBits(); - var y = reader.ReadBigIntWithBits(); - var x = reader.ReadBigIntWithBits(); - this._key = new DsaKey(p, q, g, y, x); - this.HostKey = new KeyHostAlgorithm("ssh-dss", this._key); - } - else - { - throw new NotSupportedException(string.Format("Key type '{0}' is not supported.", keyType)); - } - break; - default: - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Key '{0}' is not supported.", keyName)); - } - } - - private static byte[] GetCipherKey(string passphrase, int length) - { - List cipherKey = new List(); - - using (var md5 = new MD5Hash()) - { - byte[] passwordBytes = Encoding.UTF8.GetBytes(passphrase); - - var hash = md5.ComputeHash(passwordBytes.ToArray()).AsEnumerable(); - - cipherKey.AddRange(hash); - - while (cipherKey.Count < length) - { - hash = passwordBytes.Concat(hash); - - hash = md5.ComputeHash(hash.ToArray()); - - cipherKey.AddRange(hash); - } - } - - return cipherKey.Take(length).ToArray(); - } - - /// - /// Decrypts encrypted private key file data. - /// - /// The cipher info. - /// Encrypted data. - /// Decryption pass phrase. - /// Decryption binary salt. - /// Decrypted byte array. - /// cipherInfo - /// , , or is null. - private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string passPhrase, byte[] binarySalt) - { - if (cipherInfo == null) - throw new ArgumentNullException("cipherInfo"); - - if (cipherData == null) - throw new ArgumentNullException("cipherData"); - - if (binarySalt == null) - throw new ArgumentNullException("binarySalt"); - - List cipherKey = new List(); - - using (var md5 = new MD5Hash()) - { - var passwordBytes = Encoding.UTF8.GetBytes(passPhrase); - - // Use 8 bytes binary salkt - var initVector = passwordBytes.Concat(binarySalt.Take(8)); - - var hash = md5.ComputeHash(initVector.ToArray()).AsEnumerable(); - - cipherKey.AddRange(hash); - - while (cipherKey.Count < cipherInfo.KeySize / 8) - { - hash = hash.Concat(initVector); - - hash = md5.ComputeHash(hash.ToArray()); - - cipherKey.AddRange(hash); - } - } - - var cipher = cipherInfo.Cipher(cipherKey.ToArray(), binarySalt); - - return cipher.Decrypt(cipherData); - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._key != null) - { - ((IDisposable)this._key).Dispose(); - this._key = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~PrivateKeyFile() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - private class SshDataReader : SshData - { - public SshDataReader(byte[] data) - { - this.LoadBytes(data); - } - - public new UInt32 ReadUInt32() - { - return base.ReadUInt32(); - } - - public new string ReadString() - { - return base.ReadString(); - } - - public new byte[] ReadBytes(int length) - { - return base.ReadBytes(length); - } - - /// - /// Reads next mpint data type from internal buffer where length specified in bits. - /// - /// mpint read. - public BigInteger ReadBigIntWithBits() - { - var length = (int)base.ReadUInt32(); - - length = (int)(length + 7) / 8; - - var data = base.ReadBytes(length); - var bytesArray = new byte[data.Length + 1]; - Buffer.BlockCopy(data, 0, bytesArray, 1, data.Length); - - return new BigInteger(bytesArray.Reverse().ToArray()); - } - - protected override void LoadData() - { - } - - protected override void SaveData() - { - } - } - } -} diff --git a/Renci.SshNet/Properties/AssemblyInfo.cs b/Renci.SshNet/Properties/AssemblyInfo.cs deleted file mode 100644 index 43a97b7..0000000 --- a/Renci.SshNet/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -[assembly: AssemblyTitle("SSH.NET .NET 4.0")] -[assembly: Guid("ad816c5e-6f13-4589-9f3e-59523f8b77a4")] -[assembly: InternalsVisibleTo("Renci.SshNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f9194e1eb66b7e2575aaee115ee1d27bc100920e7150e43992d6f668f9737de8b9c7ae892b62b8a36dd1d57929ff1541665d101dc476d6e02390846efae7e5186eec409710fdb596e3f83740afef0d4443055937649bc5a773175b61c57615dac0f0fd10f52b52fedf76c17474cc567b3f7a79de95dde842509fb39aaf69c6c2")] \ No newline at end of file diff --git a/Renci.SshNet/Properties/CommonAssemblyInfo.cs b/Renci.SshNet/Properties/CommonAssemblyInfo.cs deleted file mode 100644 index b2593d9..0000000 --- a/Renci.SshNet/Properties/CommonAssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyDescription("SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.")] -[assembly: AssemblyCompany("Renci")] -[assembly: AssemblyProduct("SSH.NET")] -[assembly: AssemblyCopyright("Copyright Renci 2010-2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: AssemblyVersion("2014.4.6")] -[assembly: AssemblyFileVersion("2014.4.6")] -[assembly: AssemblyInformationalVersion("2014.4.6-beta1")] -[assembly: CLSCompliant(false)] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - - -#if DEBUG -[assembly: AssemblyConfiguration("Debug")] -#else -[assembly: AssemblyConfiguration("Release")] -#endif \ No newline at end of file diff --git a/Renci.SshNet/ProxyTypes.cs b/Renci.SshNet/ProxyTypes.cs deleted file mode 100644 index 51d21d2..0000000 --- a/Renci.SshNet/ProxyTypes.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Renci.SshNet -{ - /// - /// Specifies the type of proxy client will use to connect to server. - /// - public enum ProxyTypes - { - /// No proxy server. - None, - /// A SOCKS4 proxy server. - Socks4, - /// A SOCKS5 proxy server. - Socks5, - /// A HTTP proxy server. - Http, - } -} diff --git a/Renci.SshNet/Renci.SshNet.csproj b/Renci.SshNet/Renci.SshNet.csproj deleted file mode 100644 index 02ca667..0000000 --- a/Renci.SshNet/Renci.SshNet.csproj +++ /dev/null @@ -1,488 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {2F5F8C90-0BD1-424F-997C-7BC6280919D1} - Library - Properties - Renci.SshNet - Renci.SshNet - v4.0 - 512 - SAK - SAK - SAK - SAK - - - true - full - false - bin\Debug\ - TRACE;DEBUG - prompt - 4 - bin\Debug\Renci.SshNet.xml - - - none - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Renci.SshNet.xml - 1591 - - - true - - - WinSSH4e1-public.snk - - - - - - - - - - - - Code - - - Code - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - - - - Code - - - - - - - - Code - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - - - - - - - - - - - - - Code - - - - - - - Code - - - - - Code - - - - - - - - - - - Code - - - Code - - - Code - - - - - - - - - Code - - - - - Code - - - Code - - - - Code - - - Code - - - - Code - - - - - - Code - - - - Code - - - Code - - - - - Code - - - Code - - - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - Code - - - - - - - - - Code - - - Code - - - Code - - - Code - - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - Code - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - - - - - - - - Code - - - - - - - - - Code - - - Code - - - - - Code - - - - - Code - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Renci.SshNet/Renci.SshNet.csproj.vspscc b/Renci.SshNet/Renci.SshNet.csproj.vspscc deleted file mode 100644 index feffdec..0000000 --- a/Renci.SshNet/Renci.SshNet.csproj.vspscc +++ /dev/null @@ -1,10 +0,0 @@ -"" -{ -"FILE_VERSION" = "9237" -"ENLISTMENT_CHOICE" = "NEVER" -"PROJECT_FILE_RELATIVE_PATH" = "" -"NUMBER_OF_EXCLUDED_FILES" = "0" -"ORIGINAL_PROJECT_FILE_PATH" = "" -"NUMBER_OF_NESTED_PROJECTS" = "0" -"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" -} diff --git a/Renci.SshNet/ScpClient.NET.cs b/Renci.SshNet/ScpClient.NET.cs deleted file mode 100644 index 294f515..0000000 --- a/Renci.SshNet/ScpClient.NET.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System; -using Renci.SshNet.Channels; -using System.IO; -using Renci.SshNet.Common; -using System.Text.RegularExpressions; - -namespace Renci.SshNet -{ - /// - /// Provides SCP client functionality. - /// - public partial class ScpClient - { - private static readonly Regex _directoryInfoRe = new Regex(@"D(?\d{4}) (?\d+) (?.+)"); - private static readonly Regex _timestampRe = new Regex(@"T(?\d+) 0 (?\d+) 0"); - - /// - /// Uploads the specified file to the remote host. - /// - /// The file system info. - /// The path. - /// is null. - /// is null or empty. - public void Upload(FileInfo fileInfo, string path) - { - if (fileInfo == null) - throw new ArgumentNullException("fileInfo"); - if (string.IsNullOrEmpty(path)) - throw new ArgumentException("path"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateClientChannel()) - { - channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -t \"{0}\"", path)); - this.CheckReturnCode(input); - - this.InternalUpload(channel, input, fileInfo, fileInfo.Name); - - channel.Close(); - } - } - - /// - /// Uploads the specified directory to the remote host. - /// - /// The directory info. - /// The path. - /// fileSystemInfo - /// is null or empty. - public void Upload(DirectoryInfo directoryInfo, string path) - { - if (directoryInfo == null) - throw new ArgumentNullException("directoryInfo"); - if (string.IsNullOrEmpty(path)) - throw new ArgumentException("path"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateClientChannel()) - { - channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -rt \"{0}\"", path)); - this.CheckReturnCode(input); - - this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc); - this.SendData(channel, string.Format("D0755 0 {0}\n", Path.GetFileName(path))); - this.CheckReturnCode(input); - - this.InternalUpload(channel, input, directoryInfo); - - this.SendData(channel, "E\n"); - this.CheckReturnCode(input); - - channel.Close(); - } - } - - /// - /// Downloads the specified file from the remote host to local file. - /// - /// Remote host file name. - /// Local file information. - /// is null. - /// is null or empty. - public void Download(string filename, FileInfo fileInfo) - { - if (string.IsNullOrEmpty(filename)) - throw new ArgumentException("filename"); - if (fileInfo == null) - throw new ArgumentNullException("fileInfo"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateClientChannel()) - { - channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -pf \"{0}\"", filename)); - this.SendConfirmation(channel); // Send reply - - this.InternalDownload(channel, input, fileInfo); - - channel.Close(); - } - } - - /// - /// Downloads the specified directory from the remote host to local directory. - /// - /// Remote host directory name. - /// Local directory information. - /// is null or empty. - /// is null. - public void Download(string directoryName, DirectoryInfo directoryInfo) - { - if (string.IsNullOrEmpty(directoryName)) - throw new ArgumentException("directoryName"); - if (directoryInfo == null) - throw new ArgumentNullException("directoryInfo"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateClientChannel()) - { - channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -prf \"{0}\"", directoryName)); - this.SendConfirmation(channel); // Send reply - - this.InternalDownload(channel, input, directoryInfo); - - channel.Close(); - } - } - - private void InternalUpload(ChannelSession channel, Stream input, FileInfo fileInfo, string filename) - { - this.InternalSetTimestamp(channel, input, fileInfo.LastWriteTimeUtc, fileInfo.LastAccessTimeUtc); - using (var source = fileInfo.OpenRead()) - { - this.InternalUpload(channel, input, source, filename); - } - } - - private void InternalUpload(ChannelSession channel, Stream input, DirectoryInfo directoryInfo) - { - // Upload files - var files = directoryInfo.GetFiles(); - foreach (var file in files) - { - this.InternalUpload(channel, input, file, file.Name); - } - - // Upload directories - var directories = directoryInfo.GetDirectories(); - foreach (var directory in directories) - { - this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc); - this.SendData(channel, string.Format("D0755 0 {0}\n", directory.Name)); - this.CheckReturnCode(input); - - this.InternalUpload(channel, input, directory); - - this.SendData(channel, "E\n"); - this.CheckReturnCode(input); - } - } - - private void InternalDownload(ChannelSession channel, Stream input, FileSystemInfo fileSystemInfo) - { - DateTime modifiedTime = DateTime.Now; - DateTime accessedTime = DateTime.Now; - - var startDirectoryFullName = fileSystemInfo.FullName; - var currentDirectoryFullName = startDirectoryFullName; - var directoryCounter = 0; - - while (true) - { - var message = ReadString(input); - - if (message == "E") - { - this.SendConfirmation(channel); // Send reply - - directoryCounter--; - - currentDirectoryFullName = new DirectoryInfo(currentDirectoryFullName).Parent.FullName; - - if (directoryCounter == 0) - break; - continue; - } - - var match = _directoryInfoRe.Match(message); - if (match.Success) - { - this.SendConfirmation(channel); // Send reply - - // Read directory - var mode = long.Parse(match.Result("${mode}")); - var filename = match.Result("${filename}"); - - DirectoryInfo newDirectoryInfo; - if (directoryCounter > 0) - { - newDirectoryInfo = Directory.CreateDirectory(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, filename)); - newDirectoryInfo.LastAccessTime = accessedTime; - newDirectoryInfo.LastWriteTime = modifiedTime; - } - else - { - // Dont create directory for first level - newDirectoryInfo = fileSystemInfo as DirectoryInfo; - } - - directoryCounter++; - - currentDirectoryFullName = newDirectoryInfo.FullName; - continue; - } - - match = _fileInfoRe.Match(message); - if (match.Success) - { - // Read file - this.SendConfirmation(channel); // Send reply - - var mode = match.Result("${mode}"); - var length = long.Parse(match.Result("${length}")); - var fileName = match.Result("${filename}"); - - var fileInfo = fileSystemInfo as FileInfo; - - if (fileInfo == null) - fileInfo = new FileInfo(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, fileName)); - - using (var output = fileInfo.OpenWrite()) - { - this.InternalDownload(channel, input, output, fileName, length); - } - - fileInfo.LastAccessTime = accessedTime; - fileInfo.LastWriteTime = modifiedTime; - - if (directoryCounter == 0) - break; - continue; - } - - match = _timestampRe.Match(message); - if (match.Success) - { - // Read timestamp - this.SendConfirmation(channel); // Send reply - - var mtime = long.Parse(match.Result("${mtime}")); - var atime = long.Parse(match.Result("${atime}")); - - var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - modifiedTime = zeroTime.AddSeconds(mtime); - accessedTime = zeroTime.AddSeconds(atime); - continue; - } - - this.SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message)); - } - } - - partial void SendData(ChannelSession channel, string command) - { - channel.SendData(System.Text.Encoding.Default.GetBytes(command)); - } - } -} diff --git a/Renci.SshNet/ScpClient.cs b/Renci.SshNet/ScpClient.cs deleted file mode 100644 index b13a584..0000000 --- a/Renci.SshNet/ScpClient.cs +++ /dev/null @@ -1,408 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using Renci.SshNet.Channels; -using System.IO; -using Renci.SshNet.Common; -using System.Text.RegularExpressions; -using System.Threading; -using System.Diagnostics.CodeAnalysis; - -namespace Renci.SshNet -{ - /// - /// Provides SCP client functionality. - /// - public partial class ScpClient : BaseClient - { - private static readonly Regex _fileInfoRe = new Regex(@"C(?\d{4}) (?\d+) (?.+)"); - private static char[] _byteToChar; - - /// - /// Gets or sets the operation timeout. - /// - /// - /// The timeout to wait until an operation completes. The default value is negative - /// one (-1) milliseconds, which indicates an infinite time-out period. - /// - public TimeSpan OperationTimeout { get; set; } - - /// - /// Gets or sets the size of the buffer. - /// - /// - /// The size of the buffer. The default buffer size is 16384 bytes. - /// - public uint BufferSize { get; set; } - - /// - /// Occurs when downloading file. - /// - public event EventHandler Downloading; - - /// - /// Occurs when uploading file. - /// - public event EventHandler Uploading; - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// is null. - public ScpClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public ScpClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - public ScpClient(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public ScpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - public ScpClient(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// Specified whether this instance owns the connection info. - /// is null. - /// - /// If is true, then the - /// connection info will be disposed when this instance is disposed. - /// - private ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo) - : base(connectionInfo, ownsConnectionInfo) - { - this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); - this.BufferSize = 1024 * 16; - - if (_byteToChar == null) - { - _byteToChar = new char[128]; - var ch = '\0'; - for (int i = 0; i < 128; i++) - { - _byteToChar[i] = ch++; - } - } - } - - #endregion - - /// - /// Uploads the specified stream to the remote host. - /// - /// Stream to upload. - /// Remote host file name. - public void Upload(Stream source, string path) - { - using (var input = new PipeStream()) - using (var channel = this.Session.CreateClientChannel()) - { - channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - int pathEnd = path.LastIndexOfAny(new[] { '\\', '/' }); - if (pathEnd != -1) - { - // split the path from the file - string pathOnly = path.Substring(0, pathEnd); - string fileOnly = path.Substring(pathEnd + 1); - // Send channel command request - channel.SendExecRequest(string.Format("scp -t \"{0}\"", pathOnly)); - this.CheckReturnCode(input); - - path = fileOnly; - } - - this.InternalUpload(channel, input, source, path); - - channel.Close(); - } - } - - /// - /// Downloads the specified file from the remote host to the stream. - /// - /// Remote host file name. - /// The stream where to download remote file. - /// is null or contains whitespace characters. - /// is null. - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - public void Download(string filename, Stream destination) - { - if (filename.IsNullOrWhiteSpace()) - throw new ArgumentException("filename"); - - if (destination == null) - throw new ArgumentNullException("destination"); - - using (var input = new PipeStream()) - using (var channel = this.Session.CreateClientChannel()) - { - channel.DataReceived += delegate(object sender, ChannelDataEventArgs e) - { - input.Write(e.Data, 0, e.Data.Length); - input.Flush(); - }; - - channel.Open(); - - // Send channel command request - channel.SendExecRequest(string.Format("scp -f \"{0}\"", filename)); - this.SendConfirmation(channel); // Send reply - - var message = ReadString(input); - var match = _fileInfoRe.Match(message); - - if (match.Success) - { - // Read file - this.SendConfirmation(channel); // Send reply - - var mode = match.Result("${mode}"); - var length = long.Parse(match.Result("${length}")); - var fileName = match.Result("${filename}"); - - this.InternalDownload(channel, input, destination, fileName, length); - } - else - { - this.SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message)); - } - - channel.Close(); - } - } - - private void InternalSetTimestamp(ChannelSession channel, Stream input, DateTime lastWriteTime, DateTime lastAccessime) - { - var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - var modificationSeconds = (long)(lastWriteTime - zeroTime).TotalSeconds; - var accessSeconds = (long)(lastAccessime - zeroTime).TotalSeconds; - this.SendData(channel, string.Format("T{0} 0 {1} 0\n", modificationSeconds, accessSeconds)); - this.CheckReturnCode(input); - } - - private void InternalUpload(ChannelSession channel, Stream input, Stream source, string filename) - { - var length = source.Length; - - this.SendData(channel, string.Format("C0644 {0} {1}\n", length, Path.GetFileName(filename))); - - var buffer = new byte[this.BufferSize]; - - var read = source.Read(buffer, 0, buffer.Length); - - long totalRead = 0; - - while (read > 0) - { - this.SendData(channel, buffer, read); - - totalRead += read; - - this.RaiseUploadingEvent(filename, length, totalRead); - - read = source.Read(buffer, 0, buffer.Length); - } - - this.SendConfirmation(channel); - this.CheckReturnCode(input); - } - - private void InternalDownload(ChannelSession channel, Stream input, Stream output, string filename, long length) - { - var buffer = new byte[Math.Min(length, this.BufferSize)]; - var needToRead = length; - - do - { - var read = input.Read(buffer, 0, (int)Math.Min(needToRead, this.BufferSize)); - - output.Write(buffer, 0, read); - - this.RaiseDownloadingEvent(filename, length, length - needToRead); - - needToRead -= read; - } - while (needToRead > 0); - - output.Flush(); - - // Raise one more time when file downloaded - this.RaiseDownloadingEvent(filename, length, length - needToRead); - - // Send confirmation byte after last data byte was read - this.SendConfirmation(channel); - - this.CheckReturnCode(input); - } - - private void RaiseDownloadingEvent(string filename, long size, long downloaded) - { - if (this.Downloading != null) - { - this.Downloading(this, new ScpDownloadEventArgs(filename, size, downloaded)); - } - } - - private void RaiseUploadingEvent(string filename, long size, long uploaded) - { - if (this.Uploading != null) - { - this.Uploading(this, new ScpUploadEventArgs(filename, size, uploaded)); - } - } - - private void SendConfirmation(ChannelSession channel) - { - this.SendData(channel, new byte[] { 0 }); - } - - private void SendConfirmation(ChannelSession channel, byte errorCode, string message) - { - this.SendData(channel, new[] { errorCode }); - this.SendData(channel, string.Format("{0}\n", message)); - } - - /// - /// Checks the return code. - /// - /// The output stream. - private void CheckReturnCode(Stream input) - { - var b = ReadByte(input); - - if (b > 0) - { - var errorText = ReadString(input); - - throw new ScpException(errorText); - } - } - - partial void SendData(ChannelSession channel, string command); - - private void SendData(ChannelSession channel, byte[] buffer, int length) - { - if (length == buffer.Length) - { - channel.SendData(buffer); - } - else - { - channel.SendData(buffer.Take(length).ToArray()); - } - } - - private void SendData(ChannelSession channel, byte[] buffer) - { - channel.SendData(buffer); - } - - private static int ReadByte(Stream stream) - { - var b = stream.ReadByte(); - - while (b < 0) - { - Thread.Sleep(100); - b = stream.ReadByte(); - } - - return b; - } - - private static string ReadString(Stream stream) - { - var hasError = false; - - var sb = new StringBuilder(); - - var b = ReadByte(stream); - - if (b == 1 || b == 2) - { - hasError = true; - b = ReadByte(stream); - } - - var ch = _byteToChar[b]; - - while (ch != '\n') - { - sb.Append(ch); - - b = ReadByte(stream); - - ch = _byteToChar[b]; - } - - if (hasError) - throw new ScpException(sb.ToString()); - - return sb.ToString(); - } - } -} diff --git a/Renci.SshNet/Security/Algorithm.cs b/Renci.SshNet/Security/Algorithm.cs deleted file mode 100644 index 184a824..0000000 --- a/Renci.SshNet/Security/Algorithm.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Renci.SshNet.Security -{ - /// - /// Represents the abstract base class from which all implementations of algorithms must inherit. - /// - public abstract class Algorithm - { - /// - /// Gets algorithm name. - /// - public abstract string Name { get; } - } -} diff --git a/Renci.SshNet/Security/CertificateHostAlgorithm.cs b/Renci.SshNet/Security/CertificateHostAlgorithm.cs deleted file mode 100644 index 4ef8ba5..0000000 --- a/Renci.SshNet/Security/CertificateHostAlgorithm.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; - -namespace Renci.SshNet.Security -{ - /// - /// Implements certificate support for host algorithm. - /// - public class CertificateHostAlgorithm : HostAlgorithm - { - /// - /// Gets the host key data. - /// - public override byte[] Data - { - get { throw new NotImplementedException(); } - } - - /// - /// Initializes a new instance of the class. - /// - /// The host key name. - public CertificateHostAlgorithm(string name) - : base(name) - { - } - - /// - /// Signs the specified data. - /// - /// The data. - /// Signed data. - /// - public override byte[] Sign(byte[] data) - { - throw new NotImplementedException(); - } - - /// - /// Verifies the signature. - /// - /// The data. - /// The signature. - /// True if signature was successfully verified; otherwise false. - /// - public override bool VerifySignature(byte[] data, byte[] signature) - { - throw new NotImplementedException(); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs b/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs deleted file mode 100644 index e5e70be..0000000 --- a/Renci.SshNet/Security/Cryptography/AsymmetricCipher.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for asymmetric cipher implementations. - /// - public abstract class AsymmetricCipher : Cipher - { - public override byte MinimumSize - { - get { return 0; } - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/BlockCipher.cs b/Renci.SshNet/Security/Cryptography/BlockCipher.cs deleted file mode 100644 index 5e0edb6..0000000 --- a/Renci.SshNet/Security/Cryptography/BlockCipher.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using Renci.SshNet.Security.Cryptography.Ciphers; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for block cipher implementations. - /// - public abstract class BlockCipher : SymmetricCipher - { - private readonly CipherMode _mode; - - private readonly CipherPadding _padding; - - /// - /// Gets the size of the block in bytes. - /// - /// - /// The size of the block in bytes. - /// - protected readonly byte _blockSize; - - /// - /// Gets the minimum data size. - /// - /// - /// The minimum data size. - /// - public override byte MinimumSize - { - get { return this.BlockSize; } - } - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - public byte BlockSize - { - get - { - return this._blockSize; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// Size of the block. - /// Cipher mode. - /// Cipher padding. - /// is null. - protected BlockCipher(byte[] key, byte blockSize, CipherMode mode, CipherPadding padding) - : base(key) - { - this._blockSize = blockSize; - this._mode = mode; - this._padding = padding; - - if (this._mode != null) - this._mode.Init(this); - } - - /// - /// Encrypts the specified data. - /// - /// The data. - /// Encrypted data - public override byte[] Encrypt(byte[] data) - { - var output = new byte[data.Length]; - - if (data.Length % this._blockSize > 0) - { - if (this._padding == null) - { - throw new ArgumentException("data"); - } - data = this._padding.Pad(this._blockSize, data); - } - - var writtenBytes = 0; - - for (int i = 0; i < data.Length / this._blockSize; i++) - { - if (this._mode == null) - { - writtenBytes += this.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - } - else - { - writtenBytes += this._mode.EncryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - } - } - - if (writtenBytes < data.Length) - { - throw new InvalidOperationException("Encryption error."); - } - - return output; - } - - /// - /// Decrypts the specified data. - /// - /// The data. - /// Decrypted data - public override byte[] Decrypt(byte[] data) - { - if (data.Length % this._blockSize > 0) - { - { - if (this._padding == null) - { - throw new ArgumentException("data"); - } - data = this._padding.Pad(this._blockSize, data); - } - } - - var output = new byte[data.Length]; - - var writtenBytes = 0; - for (int i = 0; i < data.Length / this._blockSize; i++) - { - if (this._mode == null) - { - writtenBytes += this.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - } - else - { - writtenBytes += this._mode.DecryptBlock(data, i * this._blockSize, this._blockSize, output, i * this._blockSize); - } - } - - if (writtenBytes < data.Length) - { - throw new InvalidOperationException("Encryption error."); - } - - return output; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Cipher.cs b/Renci.SshNet/Security/Cryptography/Cipher.cs deleted file mode 100644 index 433e234..0000000 --- a/Renci.SshNet/Security/Cryptography/Cipher.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for cipher implementation. - /// - public abstract class Cipher - { - /// - /// Gets the minimum data size. - /// - /// - /// The minimum data size. - /// - public abstract byte MinimumSize { get; } - - /// - /// Encrypts the specified input. - /// - /// The input. - /// Encrypted data. - public abstract byte[] Encrypt(byte[] input); - - /// - /// Decrypts the specified input. - /// - /// The input. - /// Decrypted data. - public abstract byte[] Decrypt(byte[] input); - - #region Packing functions - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt32ToBigEndian(uint number, byte[] buffer) - { - buffer[0] = (byte)(number >> 24); - buffer[1] = (byte)(number >> 16); - buffer[2] = (byte)(number >> 8); - buffer[3] = (byte)(number); - } - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt32ToBigEndian(uint number, byte[] buffer, int offset) - { - buffer[offset] = (byte)(number >> 24); - buffer[offset + 1] = (byte)(number >> 16); - buffer[offset + 2] = (byte)(number >> 8); - buffer[offset + 3] = (byte)(number); - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static uint BigEndianToUInt32(byte[] buffer) - { - uint n = (uint)buffer[0] << 24; - n |= (uint)buffer[1] << 16; - n |= (uint)buffer[2] << 8; - n |= (uint)buffer[3]; - return n; - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static uint BigEndianToUInt32(byte[] buffer, int offset) - { - uint n = (uint)buffer[offset] << 24; - n |= (uint)buffer[offset + 1] << 16; - n |= (uint)buffer[offset + 2] << 8; - n |= (uint)buffer[offset + 3]; - return n; - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static ulong BigEndianToUInt64(byte[] buffer) - { - uint hi = BigEndianToUInt32(buffer); - uint lo = BigEndianToUInt32(buffer, 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Converts big endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static ulong BigEndianToUInt64(byte[] buffer, int offset) - { - uint hi = BigEndianToUInt32(buffer, offset); - uint lo = BigEndianToUInt32(buffer, offset + 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt64ToBigEndian(ulong number, byte[] buffer) - { - UInt32ToBigEndian((uint)(number >> 32), buffer); - UInt32ToBigEndian((uint)(number), buffer, 4); - } - - /// - /// Populates buffer with big endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt64ToBigEndian(ulong number, byte[] buffer, int offset) - { - UInt32ToBigEndian((uint)(number >> 32), buffer, offset); - UInt32ToBigEndian((uint)(number), buffer, offset + 4); - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt32ToLittleEndian(uint number, byte[] buffer) - { - buffer[0] = (byte)(number); - buffer[1] = (byte)(number >> 8); - buffer[2] = (byte)(number >> 16); - buffer[3] = (byte)(number >> 24); - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt32ToLittleEndian(uint number, byte[] buffer, int offset) - { - buffer[offset] = (byte)(number); - buffer[offset + 1] = (byte)(number >> 8); - buffer[offset + 2] = (byte)(number >> 16); - buffer[offset + 3] = (byte)(number >> 24); - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static uint LittleEndianToUInt32(byte[] buffer) - { - uint n = (uint)buffer[0]; - n |= (uint)buffer[1] << 8; - n |= (uint)buffer[2] << 16; - n |= (uint)buffer[3] << 24; - return n; - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static uint LittleEndianToUInt32(byte[] buffer, int offset) - { - uint n = (uint)buffer[offset]; - n |= (uint)buffer[offset + 1] << 8; - n |= (uint)buffer[offset + 2] << 16; - n |= (uint)buffer[offset + 3] << 24; - return n; - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// Converted . - protected static ulong LittleEndianToUInt64(byte[] buffer) - { - uint lo = LittleEndianToUInt32(buffer); - uint hi = LittleEndianToUInt32(buffer, 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Converts little endian bytes into number. - /// - /// The buffer. - /// The buffer offset. - /// Converted . - protected static ulong LittleEndianToUInt64(byte[] buffer, int offset) - { - uint lo = LittleEndianToUInt32(buffer, offset); - uint hi = LittleEndianToUInt32(buffer, offset + 4); - return ((ulong)hi << 32) | (ulong)lo; - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - protected static void UInt64ToLittleEndian(ulong number, byte[] buffer) - { - UInt32ToLittleEndian((uint)(number), buffer); - UInt32ToLittleEndian((uint)(number >> 32), buffer, 4); - } - - /// - /// Populates buffer with little endian number representation. - /// - /// The number to convert. - /// The buffer. - /// The buffer offset. - protected static void UInt64ToLittleEndian(ulong number, byte[] buffer, int offset) - { - UInt32ToLittleEndian((uint)(number), buffer, offset); - UInt32ToLittleEndian((uint)(number >> 32), buffer, offset + 4); - } - - #endregion - } -} diff --git a/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs b/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs deleted file mode 100644 index 921b12e..0000000 --- a/Renci.SshNet/Security/Cryptography/CipherDigitalSignature.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Linq; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Implements digital signature where where asymmetric cipher is used, - /// - public abstract class CipherDigitalSignature : DigitalSignature - { - private readonly AsymmetricCipher _cipher; - - private readonly ObjectIdentifier _oid; - - /// - /// Initializes a new instance of the class. - /// - /// The object identifier. - /// The cipher. - public CipherDigitalSignature(ObjectIdentifier oid, AsymmetricCipher cipher) - { - if (cipher == null) - throw new ArgumentNullException("cipher"); - - this._cipher = cipher; - this._oid = oid; - } - - /// - /// Verifies the signature. - /// - /// The input. - /// The signature. - /// - /// True if signature was successfully verified; otherwise false. - /// - public override bool Verify(byte[] input, byte[] signature) - { - var encryptedSignature = this._cipher.Decrypt(signature); - var hashData = this.Hash(input); - var expected = DerEncode(hashData); - return expected.SequenceEqual(encryptedSignature); - } - - /// - /// Creates the signature. - /// - /// The input. - /// - /// Signed input data. - /// - public override byte[] Sign(byte[] input) - { - // Calculate hash value - var hashData = this.Hash(input); - - // Calculate DER string - var derEncodedHash = DerEncode(hashData); - - return this._cipher.Encrypt(derEncodedHash).TrimLeadingZero().ToArray(); - } - - /// - /// Hashes the specified input. - /// - /// The input. - /// Hashed data. - protected abstract byte[] Hash(byte[] input); - - /// - /// Encodes hash using DER. - /// - /// The hash data. - /// DER Encoded byte array - protected byte[] DerEncode(byte[] hashData) - { - var data = new DerData(); - - var alg = new DerData(); - alg.Write(this._oid); - alg.WriteNull(); - - data.Write(alg); - data.Write(hashData); - - return data.Encode(); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs deleted file mode 100644 index 95b72cd..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs +++ /dev/null @@ -1,835 +0,0 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// AES cipher implementation. - /// - public sealed class AesCipher : BlockCipher - { - private const uint m1 = 0x80808080; - - private const uint m2 = 0x7f7f7f7f; - - private const uint m3 = 0x0000001b; - - private int _rounds; - - private uint[] _encryptionKey; - - private uint[] _decryptionKey; - - private uint C0, C1, C2, C3; - - #region Static Definition Tables - - private static readonly byte[] S = - { - 99, 124, 119, 123, 242, 107, 111, 197, - 48, 1, 103, 43, 254, 215, 171, 118, - 202, 130, 201, 125, 250, 89, 71, 240, - 173, 212, 162, 175, 156, 164, 114, 192, - 183, 253, 147, 38, 54, 63, 247, 204, - 52, 165, 229, 241, 113, 216, 49, 21, - 4, 199, 35, 195, 24, 150, 5, 154, - 7, 18, 128, 226, 235, 39, 178, 117, - 9, 131, 44, 26, 27, 110, 90, 160, - 82, 59, 214, 179, 41, 227, 47, 132, - 83, 209, 0, 237, 32, 252, 177, 91, - 106, 203, 190, 57, 74, 76, 88, 207, - 208, 239, 170, 251, 67, 77, 51, 133, - 69, 249, 2, 127, 80, 60, 159, 168, - 81, 163, 64, 143, 146, 157, 56, 245, - 188, 182, 218, 33, 16, 255, 243, 210, - 205, 12, 19, 236, 95, 151, 68, 23, - 196, 167, 126, 61, 100, 93, 25, 115, - 96, 129, 79, 220, 34, 42, 144, 136, - 70, 238, 184, 20, 222, 94, 11, 219, - 224, 50, 58, 10, 73, 6, 36, 92, - 194, 211, 172, 98, 145, 149, 228, 121, - 231, 200, 55, 109, 141, 213, 78, 169, - 108, 86, 244, 234, 101, 122, 174, 8, - 186, 120, 37, 46, 28, 166, 180, 198, - 232, 221, 116, 31, 75, 189, 139, 138, - 112, 62, 181, 102, 72, 3, 246, 14, - 97, 53, 87, 185, 134, 193, 29, 158, - 225, 248, 152, 17, 105, 217, 142, 148, - 155, 30, 135, 233, 206, 85, 40, 223, - 140, 161, 137, 13, 191, 230, 66, 104, - 65, 153, 45, 15, 176, 84, 187, 22 - }; - - // The inverse S-box - private static readonly byte[] Si = - { - 82, 9, 106, 213, 48, 54, 165, 56, - 191, 64, 163, 158, 129, 243, 215, 251, - 124, 227, 57, 130, 155, 47, 255, 135, - 52, 142, 67, 68, 196, 222, 233, 203, - 84, 123, 148, 50, 166, 194, 35, 61, - 238, 76, 149, 11, 66, 250, 195, 78, - 8, 46, 161, 102, 40, 217, 36, 178, - 118, 91, 162, 73, 109, 139, 209, 37, - 114, 248, 246, 100, 134, 104, 152, 22, - 212, 164, 92, 204, 93, 101, 182, 146, - 108, 112, 72, 80, 253, 237, 185, 218, - 94, 21, 70, 87, 167, 141, 157, 132, - 144, 216, 171, 0, 140, 188, 211, 10, - 247, 228, 88, 5, 184, 179, 69, 6, - 208, 44, 30, 143, 202, 63, 15, 2, - 193, 175, 189, 3, 1, 19, 138, 107, - 58, 145, 17, 65, 79, 103, 220, 234, - 151, 242, 207, 206, 240, 180, 230, 115, - 150, 172, 116, 34, 231, 173, 53, 133, - 226, 249, 55, 232, 28, 117, 223, 110, - 71, 241, 26, 113, 29, 41, 197, 137, - 111, 183, 98, 14, 170, 24, 190, 27, - 252, 86, 62, 75, 198, 210, 121, 32, - 154, 219, 192, 254, 120, 205, 90, 244, - 31, 221, 168, 51, 136, 7, 199, 49, - 177, 18, 16, 89, 39, 128, 236, 95, - 96, 81, 127, 169, 25, 181, 74, 13, - 45, 229, 122, 159, 147, 201, 156, 239, - 160, 224, 59, 77, 174, 42, 245, 176, - 200, 235, 187, 60, 131, 83, 153, 97, - 23, 43, 4, 126, 186, 119, 214, 38, - 225, 105, 20, 99, 85, 33, 12, 125 - }; - - // vector used in calculating key schedule (powers of x in GF(256)) - private static readonly byte[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 - }; - - #endregion - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public AesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize == 256 || keySize == 192 || keySize == 128)) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "KeySize '{0}' is not valid for this algorithm.", keySize)); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - /// or is null. - /// or is too short. - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException("inputBuffer"); - - if (outputBuffer == null) - throw new ArgumentNullException("outputBuffer"); - - if ((inputOffset + (32 / 2)) > inputBuffer.Length) - { - throw new IndexOutOfRangeException("input buffer too short"); - } - - if ((outputOffset + (32 / 2)) > outputBuffer.Length) - { - throw new IndexOutOfRangeException("output buffer too short"); - } - - if (this._encryptionKey == null) - { - this._encryptionKey = this.GenerateWorkingKey(true, this.Key); - } - - this.UnPackBlock(inputBuffer, inputOffset); - - this.EncryptBlock(this._encryptionKey); - - this.PackBlock(outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - /// or is null. - /// or is too short. - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer == null) - throw new ArgumentNullException("inputBuffer"); - - if (outputBuffer == null) - throw new ArgumentNullException("outputBuffer"); - - if ((inputOffset + (32 / 2)) > inputBuffer.Length) - { - throw new IndexOutOfRangeException("input buffer too short"); - } - - if ((outputOffset + (32 / 2)) > outputBuffer.Length) - { - throw new IndexOutOfRangeException("output buffer too short"); - } - - if (this._decryptionKey == null) - { - this._decryptionKey = this.GenerateWorkingKey(false, this.Key); - } - - this.UnPackBlock(inputBuffer, inputOffset); - - this.DecryptBlock(this._decryptionKey); - - this.PackBlock(outputBuffer, outputOffset); - - return this.BlockSize; - } - - private uint[] GenerateWorkingKey(bool isEncryption, byte[] key) - { - int KC = key.Length / 4; // key length in words - - if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length)) - throw new ArgumentException("Key length not 128/192/256 bits."); - - _rounds = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes - uint[] W = new uint[(_rounds + 1) * 4]; // 4 words in a block - - // - // copy the key into the round key array - // - - int t = 0; - - for (int i = 0; i < key.Length; t++) - { - W[(t >> 2) * 4 + (t & 3)] = LittleEndianToUInt32(key, i); - i += 4; - } - - // - // while not enough round key material calculated - // calculate new values - // - int k = (_rounds + 1) << 2; - for (int i = KC; (i < k); i++) - { - uint temp = W[((i - 1) >> 2) * 4 + ((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) * 4 + (i & 3)] = W[((i - KC) >> 2) * 4 + ((i - KC) & 3)] ^ temp; - } - - if (!isEncryption) - { - for (int j = 1; j < _rounds; j++) - { - for (int i = 0; i < 4; i++) - { - W[j * 4 + i] = InvMcol(W[j * 4 + i]); - } - } - } - - return W; - } - - private uint Shift(uint r, int shift) - { - return (r >> shift) | (r << (32 - shift)); - } - - private uint FFmulX(uint x) - { - return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); - } - - private uint InvMcol(uint x) - { - uint f2 = FFmulX(x); - uint f4 = FFmulX(f2); - uint f8 = FFmulX(f4); - uint f9 = x ^ f8; - - return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); - } - - private uint SubWord(uint x) - { - return (uint)S[x & 255] - | (((uint)S[(x >> 8) & 255]) << 8) - | (((uint)S[(x >> 16) & 255]) << 16) - | (((uint)S[(x >> 24) & 255]) << 24); - } - - private void UnPackBlock(byte[] bytes, int off) - { - C0 = LittleEndianToUInt32(bytes, off); - C1 = LittleEndianToUInt32(bytes, off + 4); - C2 = LittleEndianToUInt32(bytes, off + 8); - C3 = LittleEndianToUInt32(bytes, off + 12); - } - - private void PackBlock(byte[] bytes, int off) - { - UInt32ToLittleEndian(C0, bytes, off); - UInt32ToLittleEndian(C1, bytes, off + 4); - UInt32ToLittleEndian(C2, bytes, off + 8); - UInt32ToLittleEndian(C3, bytes, off + 12); - } - - private void EncryptBlock(uint[] KW) - { - int r; - uint r0, r1, r2, r3; - - C0 ^= KW[0 * 4 + 0]; - C1 ^= KW[0 * 4 + 1]; - C2 ^= KW[0 * 4 + 2]; - C3 ^= KW[0 * 4 + 3]; - - for (r = 1; r < _rounds - 1; ) - { - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 3]; - C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ KW[r * 4 + 0]; - C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ KW[r * 4 + 1]; - C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ KW[r * 4 + 2]; - C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ KW[r++ * 4 + 3]; - } - - r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[C3 >> 24] ^ KW[r * 4 + 0]; - r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[C0 >> 24] ^ KW[r * 4 + 1]; - r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[C1 >> 24] ^ KW[r * 4 + 2]; - r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[C2 >> 24] ^ KW[r++ * 4 + 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 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ KW[r * 4 + 0]; - C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ KW[r * 4 + 1]; - C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ KW[r * 4 + 2]; - C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ KW[r * 4 + 3]; - } - - private void DecryptBlock(uint[] KW) - { - int r; - uint r0, r1, r2, r3; - - C0 ^= KW[_rounds * 4 + 0]; - C1 ^= KW[_rounds * 4 + 1]; - C2 ^= KW[_rounds * 4 + 2]; - C3 ^= KW[_rounds * 4 + 3]; - - for (r = _rounds - 1; r > 1; ) - { - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r-- * 4 + 3]; - C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ KW[r * 4 + 0]; - C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ KW[r * 4 + 1]; - C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ KW[r * 4 + 2]; - C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ KW[r-- * 4 + 3]; - } - - r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[C1 >> 24] ^ KW[r * 4 + 0]; - r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[C2 >> 24] ^ KW[r * 4 + 1]; - r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[C3 >> 24] ^ KW[r * 4 + 2]; - r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[C0 >> 24] ^ KW[r * 4 + 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 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ KW[0 * 4 + 0]; - C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ KW[0 * 4 + 1]; - C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ KW[0 * 4 + 2]; - C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ KW[0 * 4 + 3]; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs deleted file mode 100644 index fb3c001..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Arc4Cipher.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements ARCH4 cipher algorithm - /// - public sealed class Arc4Cipher : StreamCipher - { - private static readonly int STATE_LENGTH = 256; - - /// - /// Holds the state of the RC4 engine - /// - private byte[] _engineState; - - private int _x; - - private int _y; - - private byte[] _workingKey; - - /// - /// Gets the minimum data size. - /// - /// - /// The minimum data size. - /// - public override byte MinimumSize - { - get { return 0; } - } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// if set to true will disharged first 1536 bytes. - /// is null. - public Arc4Cipher(byte[] key, bool dischargeFirstBytes) - : base(key) - { - this._workingKey = key; - SetKey(this._workingKey); - // The first 1536 bytes of keystream - // generated by the cipher MUST be discarded, and the first byte of the - // first encrypted packet MUST be encrypted using the 1537th byte of - // keystream. - if (dischargeFirstBytes) - this.Encrypt(new byte[1536]); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - return this.ProcessBytes(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - return this.ProcessBytes(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - } - - /// - /// Encrypts the specified input. - /// - /// The input. - /// - /// Encrypted data. - /// - /// - public override byte[] Encrypt(byte[] input) - { - var output = new byte[input.Length]; - this.ProcessBytes(input, 0, input.Length, output, 0); - return output; - } - - /// - /// Decrypts the specified input. - /// - /// The input. - /// - /// Decrypted data. - /// - /// - public override byte[] Decrypt(byte[] input) - { - var output = new byte[input.Length]; - this.ProcessBytes(input, 0, input.Length, output, 0); - return output; - } - - private int ProcessBytes(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + inputCount) > inputBuffer.Length) - { - throw new IndexOutOfRangeException("input buffer too short"); - } - - if ((outputOffset + inputCount) > outputBuffer.Length) - { - throw new IndexOutOfRangeException("output buffer too short"); - } - - for (int i = 0; i < inputCount; i++) - { - this._x = (this._x + 1) & 0xff; - this._y = (this._engineState[this._x] + this._y) & 0xff; - - // swap - byte tmp = this._engineState[this._x]; - this._engineState[this._x] = this._engineState[this._y]; - this._engineState[this._y] = tmp; - - // xor - outputBuffer[i + outputOffset] = (byte)(inputBuffer[i + inputOffset] ^ this._engineState[(this._engineState[this._x] + this._engineState[this._y]) & 0xff]); - } - return inputCount; - } - - private void SetKey(byte[] keyBytes) - { - this._workingKey = keyBytes; - - this._x = 0; - this._y = 0; - - if (this._engineState == null) - { - this._engineState = new byte[STATE_LENGTH]; - } - - // reset the state of the engine - for (var i = 0; i < STATE_LENGTH; i++) - { - this._engineState[i] = (byte) i; - } - - int i1 = 0; - int i2 = 0; - - for (var i = 0; i < STATE_LENGTH; i++) - { - i2 = ((keyBytes[i1] & 0xff) + this._engineState[i] + i2) & 0xff; - // do the byte-swap inline - byte tmp = this._engineState[i]; - this._engineState[i] = this._engineState[i2]; - this._engineState[i2] = tmp; - i1 = (i1 + 1) % keyBytes.Length; - } - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs deleted file mode 100644 index f78afec..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/BlowfishCipher.cs +++ /dev/null @@ -1,516 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Blowfish cipher implementation. - /// - public sealed class BlowfishCipher : BlockCipher - { - #region Static reference tables - - private readonly static uint[] 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 - }; - - #endregion - - private const int _rounds = 16; - - private const int _sboxSk = 256; - - private const int _pSize = _rounds + 2; - - /// - /// The s-boxes - /// - private readonly uint[] _s0, _s1, _s2, _s3; - - /// - /// The p-array - /// - private readonly uint[] _p; - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public BlowfishCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 8, mode, padding) - { - var keySize = key.Length * 8; - - if (keySize < 1 || keySize > 448) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - this._s0 = new uint[_sboxSk]; - - this._s1 = new uint[_sboxSk]; - - this._s2 = new uint[_sboxSk]; - - this._s3 = new uint[_sboxSk]; - - this._p = new uint[_pSize]; - - this.SetKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - uint xl = BigEndianToUInt32(inputBuffer, inputOffset); - uint xr = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - xl ^= this._p[0]; - - for (int i = 1; i < _rounds; i += 2) - { - xr ^= F(xl) ^ this._p[i]; - xl ^= F(xr) ^ this._p[i + 1]; - } - - xr ^= this._p[_rounds + 1]; - - UInt32ToBigEndian(xr, outputBuffer, outputOffset); - UInt32ToBigEndian(xl, outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - uint xl = BigEndianToUInt32(inputBuffer, inputOffset); - uint xr = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - xl ^= this._p[_rounds + 1]; - - for (int i = _rounds; i > 0; i -= 2) - { - xr ^= F(xl) ^ this._p[i]; - xl ^= F(xr) ^ this._p[i - 1]; - } - - xr ^= this._p[0]; - - UInt32ToBigEndian(xr, outputBuffer, outputOffset); - UInt32ToBigEndian(xl, outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - private uint F(uint x) - { - return (((this._s0[x >> 24] + this._s1[(x >> 16) & 0xff]) ^ this._s2[(x >> 8) & 0xff]) + this._s3[x & 0xff]); - } - - 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...) - */ - Buffer.BlockCopy(KS0, 0, this._s0, 0, _sboxSk * sizeof(uint)); - Buffer.BlockCopy(KS1, 0, this._s1, 0, _sboxSk * sizeof(uint)); - Buffer.BlockCopy(KS2, 0, this._s2, 0, _sboxSk * sizeof(uint)); - Buffer.BlockCopy(KS3, 0, this._s3, 0, _sboxSk * sizeof(uint)); - - Buffer.BlockCopy(KP, 0, this._p, 0, _pSize * sizeof(uint)); - - /* - * (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 < _pSize; i++) - { - // Get the 32 bits of the key, in 4 * 8 bit chunks - uint data = 0x0000000; - for (int j = 0; j < 4; j++) - { - // create a 32 bit block - data = (data << 8) | (uint)key[keyIndex++]; - - // 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 - this._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, this._p); - ProcessTable(this._p[_pSize - 2], this._p[_pSize - 1], this._s0); - ProcessTable(this._s0[_sboxSk - 2], this._s0[_sboxSk - 1], this._s1); - ProcessTable(this._s1[_sboxSk - 2], this._s1[_sboxSk - 1], this._s2); - ProcessTable(this._s2[_sboxSk - 2], this._s2[_sboxSk - 1], this._s3); - } - - /// - /// apply the encryption cycle to each value pair in the table. - /// - /// The xl. - /// The xr. - /// The table. - private void ProcessTable(uint xl, uint xr, uint[] 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]; - } - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs deleted file mode 100644 index f9308e4..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/CastCipher.cs +++ /dev/null @@ -1,726 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements CAST cipher algorithm - /// - public sealed class CastCipher : BlockCipher - { - internal static readonly int MAX_ROUNDS = 16; - - internal static readonly int RED_ROUNDS = 12; - - /// - /// The rotating round key - /// - private readonly int[] _kr = new int[17]; - - /// - /// The masking round key - /// - private readonly uint[] _km = new uint[17]; - - private int _rounds = MAX_ROUNDS; - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public CastCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 8, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize >= 40 && keySize <= 128 && keySize % 8 == 0)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - this.SetKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - // process the input block - // batch the units up into a 32 bit chunk and go for it - // the array is in bytes, the increment is 8x8 bits = 64 - - uint L0 = BigEndianToUInt32(inputBuffer, inputOffset); - uint R0 = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - uint[] result = new uint[2]; - this.CastEncipher(L0, R0, result); - - // now stuff them into the destination block - UInt32ToBigEndian(result[0], outputBuffer, outputOffset); - UInt32ToBigEndian(result[1], outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - // process the input block - // batch the units up into a 32 bit chunk and go for it - // the array is in bytes, the increment is 8x8 bits = 64 - uint L16 = BigEndianToUInt32(inputBuffer, inputOffset); - uint R16 = BigEndianToUInt32(inputBuffer, inputOffset + 4); - - uint[] result = new uint[2]; - this.CastDecipher(L16, R16, result); - - // now stuff them into the destination block - UInt32ToBigEndian(result[0], outputBuffer, outputOffset); - UInt32ToBigEndian(result[1], outputBuffer, outputOffset + 4); - - return this.BlockSize; - } - - #region Static Definition Tables - - internal static readonly uint[] S1 = - { - 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, - 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, - 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, - 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, - 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, - 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, - 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, - 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, - 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, - 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, - 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, - 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, - 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, - 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, - 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, - 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, - 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, - 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, - 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, - 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, - 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, - 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, - 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, - 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, - 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, - 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, - 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, - 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, - 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, - 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, - 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, - 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf - }, - S2 = - { - 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, - 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, - 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, - 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, - 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, - 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, - 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, - 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, - 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, - 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, - 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, - 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, - 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, - 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, - 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, - 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, - 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, - 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, - 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, - 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, - 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, - 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, - 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, - 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, - 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, - 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, - 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, - 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, - 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, - 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, - 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, - 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1 - }, - S3 = - { - 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, - 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, - 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, - 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, - 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, - 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, - 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, - 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, - 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, - 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, - 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, - 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, - 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, - 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, - 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, - 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, - 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, - 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, - 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, - 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, - 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, - 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, - 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, - 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, - 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, - 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, - 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, - 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, - 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, - 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, - 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, - 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783 - }, - S4 = - { - 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, - 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, - 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, - 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, - 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, - 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, - 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, - 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, - 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, - 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, - 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, - 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, - 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, - 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, - 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, - 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, - 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, - 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, - 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, - 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, - 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, - 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, - 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, - 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, - 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, - 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, - 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, - 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, - 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, - 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, - 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, - 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2 - }, - S5 = - { - 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, - 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, - 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, - 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, - 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, - 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, - 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, - 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, - 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, - 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, - 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, - 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, - 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, - 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, - 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, - 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, - 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, - 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, - 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, - 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, - 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, - 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, - 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, - 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, - 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, - 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, - 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, - 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, - 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, - 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, - 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, - 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4 - }, - S6 = - { - 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, - 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, - 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, - 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, - 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, - 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, - 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, - 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, - 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, - 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, - 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, - 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, - 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, - 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, - 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, - 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, - 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, - 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, - 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, - 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, - 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, - 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, - 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, - 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, - 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, - 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, - 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, - 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, - 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, - 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, - 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, - 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f - }, - S7 = - { - 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, - 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, - 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, - 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, - 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, - 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, - 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, - 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, - 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, - 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, - 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, - 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, - 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, - 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, - 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, - 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, - 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, - 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, - 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, - 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, - 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, - 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, - 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, - 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, - 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, - 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, - 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, - 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, - 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, - 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, - 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, - 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3 - }, - S8 = - { - 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, - 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, - 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, - 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, - 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, - 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, - 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, - 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, - 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, - 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, - 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, - 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, - 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, - 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, - 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, - 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, - 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, - 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, - 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, - 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, - 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, - 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, - 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, - 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, - 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, - 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, - 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, - 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, - 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, - 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, - 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, - 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e - }; - - #endregion - - /// - /// Sets the subkeys using the same nomenclatureas described in RFC2144. - /// - /// The key. - private void SetKey(byte[] key) - { - /* - * Determine the key size here, if required - * - * if keysize <= 80bits, use 12 rounds instead of 16 - * if keysize < 128bits, pad with 0 - * - * Typical key sizes => 40, 64, 80, 128 - */ - - if (key.Length < 11) - { - this._rounds = RED_ROUNDS; - } - - int[] z = new int[16]; - int[] x = new int[16]; - - uint z03, z47, z8B, zCF; - uint x03, x47, x8B, xCF; - - /* copy the key into x */ - for (int i = 0; i < key.Length; i++) - { - x[i] = (int)(key[i] & 0xff); - } - - /* - * This will look different because the selection of - * bytes from the input key I've already chosen the - * correct int. - */ - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._km[1] = S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]; - this._km[2] = S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]; - this._km[3] = S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]; - this._km[4] = S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]; - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._km[5] = S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]; - this._km[6] = S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]; - this._km[7] = S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]; - this._km[8] = S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]; - - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._km[9] = S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]; - this._km[10] = S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]; - this._km[11] = S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]; - this._km[12] = S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]; - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._km[13] = S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]; - this._km[14] = S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]; - this._km[15] = S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]; - this._km[16] = S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]; - - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._kr[1] = (int)((S5[z[0x8]] ^ S6[z[0x9]] ^ S7[z[0x7]] ^ S8[z[0x6]] ^ S5[z[0x2]]) & 0x1f); - this._kr[2] = (int)((S5[z[0xA]] ^ S6[z[0xB]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S6[z[0x6]]) & 0x1f); - this._kr[3] = (int)((S5[z[0xC]] ^ S6[z[0xD]] ^ S7[z[0x3]] ^ S8[z[0x2]] ^ S7[z[0x9]]) & 0x1f); - this._kr[4] = (int)((S5[z[0xE]] ^ S6[z[0xF]] ^ S7[z[0x1]] ^ S8[z[0x0]] ^ S8[z[0xC]]) & 0x1f); - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._kr[5] = (int)((S5[x[0x3]] ^ S6[x[0x2]] ^ S7[x[0xC]] ^ S8[x[0xD]] ^ S5[x[0x8]]) & 0x1f); - this._kr[6] = (int)((S5[x[0x1]] ^ S6[x[0x0]] ^ S7[x[0xE]] ^ S8[x[0xF]] ^ S6[x[0xD]]) & 0x1f); - this._kr[7] = (int)((S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x8]] ^ S8[x[0x9]] ^ S7[x[0x3]]) & 0x1f); - this._kr[8] = (int)((S5[x[0x5]] ^ S6[x[0x4]] ^ S7[x[0xA]] ^ S8[x[0xB]] ^ S8[x[0x7]]) & 0x1f); - - x03 = IntsTo32bits(x, 0x0); - x47 = IntsTo32bits(x, 0x4); - x8B = IntsTo32bits(x, 0x8); - xCF = IntsTo32bits(x, 0xC); - z03 = x03 ^ S5[x[0xD]] ^ S6[x[0xF]] ^ S7[x[0xC]] ^ S8[x[0xE]] ^ S7[x[0x8]]; - Bits32ToInts(z03, z, 0x0); - z47 = x8B ^ S5[z[0x0]] ^ S6[z[0x2]] ^ S7[z[0x1]] ^ S8[z[0x3]] ^ S8[x[0xA]]; - Bits32ToInts(z47, z, 0x4); - z8B = xCF ^ S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x5]] ^ S8[z[0x4]] ^ S5[x[0x9]]; - Bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ S5[z[0xA]] ^ S6[z[0x9]] ^ S7[z[0xB]] ^ S8[z[0x8]] ^ S6[x[0xB]]; - Bits32ToInts(zCF, z, 0xC); - this._kr[9] = (int)((S5[z[0x3]] ^ S6[z[0x2]] ^ S7[z[0xC]] ^ S8[z[0xD]] ^ S5[z[0x9]]) & 0x1f); - this._kr[10] = (int)((S5[z[0x1]] ^ S6[z[0x0]] ^ S7[z[0xE]] ^ S8[z[0xF]] ^ S6[z[0xc]]) & 0x1f); - this._kr[11] = (int)((S5[z[0x7]] ^ S6[z[0x6]] ^ S7[z[0x8]] ^ S8[z[0x9]] ^ S7[z[0x2]]) & 0x1f); - this._kr[12] = (int)((S5[z[0x5]] ^ S6[z[0x4]] ^ S7[z[0xA]] ^ S8[z[0xB]] ^ S8[z[0x6]]) & 0x1f); - - z03 = IntsTo32bits(z, 0x0); - z47 = IntsTo32bits(z, 0x4); - z8B = IntsTo32bits(z, 0x8); - zCF = IntsTo32bits(z, 0xC); - x03 = z8B ^ S5[z[0x5]] ^ S6[z[0x7]] ^ S7[z[0x4]] ^ S8[z[0x6]] ^ S7[z[0x0]]; - Bits32ToInts(x03, x, 0x0); - x47 = z03 ^ S5[x[0x0]] ^ S6[x[0x2]] ^ S7[x[0x1]] ^ S8[x[0x3]] ^ S8[z[0x2]]; - Bits32ToInts(x47, x, 0x4); - x8B = z47 ^ S5[x[0x7]] ^ S6[x[0x6]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S5[z[0x1]]; - Bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ S5[x[0xA]] ^ S6[x[0x9]] ^ S7[x[0xB]] ^ S8[x[0x8]] ^ S6[z[0x3]]; - Bits32ToInts(xCF, x, 0xC); - this._kr[13] = (int)((S5[x[0x8]] ^ S6[x[0x9]] ^ S7[x[0x7]] ^ S8[x[0x6]] ^ S5[x[0x3]]) & 0x1f); - this._kr[14] = (int)((S5[x[0xA]] ^ S6[x[0xB]] ^ S7[x[0x5]] ^ S8[x[0x4]] ^ S6[x[0x7]]) & 0x1f); - this._kr[15] = (int)((S5[x[0xC]] ^ S6[x[0xD]] ^ S7[x[0x3]] ^ S8[x[0x2]] ^ S7[x[0x8]]) & 0x1f); - this._kr[16] = (int)((S5[x[0xE]] ^ S6[x[0xF]] ^ S7[x[0x1]] ^ S8[x[0x0]] ^ S8[x[0xD]]) & 0x1f); - } - - /// - /// The first of the three processing functions for the encryption and decryption. - /// - /// The input to be processed. - /// The mask to be used from Km[n]. - /// The rotation value to be used. - /// - private static uint F1(uint D, uint Kmi, int Kri) - { - uint I = Kmi + D; - I = I << Kri | (I >> (32 - Kri)); - return ((S1[(I >> 24) & 0xff] ^ S2[(I >> 16) & 0xff]) - S3[(I >> 8) & 0xff]) + S4[I & 0xff]; - } - - /// - /// The second of the three processing functions for the encryption and decryption. - /// - /// The input to be processed. - /// The mask to be used from Km[n]. - /// The rotation value to be used. - /// - private static uint F2(uint D, uint Kmi, int Kri) - { - uint I = Kmi ^ D; - I = I << Kri | (I >> (32 - Kri)); - return ((S1[(I >> 24) & 0xff] - S2[(I >> 16) & 0xff]) + S3[(I >> 8) & 0xff]) ^ S4[I & 0xff]; - } - - /// - /// The third of the three processing functions for the encryption and decryption. - /// - /// The input to be processed. - /// The mask to be used from Km[n]. - /// The rotation value to be used. - /// - private static uint F3(uint D, uint Kmi, int Kri) - { - uint I = Kmi - D; - I = I << Kri | (I >> (32 - Kri)); - return ((S1[(I >> 24) & 0xff] + S2[(I >> 16) & 0xff]) ^ S3[(I >> 8) & 0xff]) - S4[I & 0xff]; - } - - /// - /// Does the 16 rounds to encrypt the block. - /// - /// The LH-32bits of the plaintext block. - /// The RH-32bits of the plaintext block. - /// The result. - private void CastEncipher(uint L0, uint R0, uint[] result) - { - uint Lp = L0; // the previous value, equiv to L[i-1] - uint Rp = R0; // equivalent to R[i-1] - - /* - * numbering consistent with paper to make - * checking and validating easier - */ - uint Li = L0, Ri = R0; - - for (int i = 1; i <= this._rounds; i++) - { - Lp = Li; - Rp = Ri; - - Li = Rp; - switch (i) - { - case 1: - case 4: - case 7: - case 10: - case 13: - case 16: - Ri = Lp ^ F1(Rp, this._km[i], this._kr[i]); - break; - case 2: - case 5: - case 8: - case 11: - case 14: - Ri = Lp ^ F2(Rp, this._km[i], this._kr[i]); - break; - case 3: - case 6: - case 9: - case 12: - case 15: - Ri = Lp ^ F3(Rp, this._km[i], this._kr[i]); - break; - } - } - - result[0] = Ri; - result[1] = Li; - } - - private void CastDecipher(uint L16, uint R16, uint[] result) - { - uint Lp = L16; // the previous value, equiv to L[i-1] - uint Rp = R16; // equivalent to R[i-1] - - /* - * numbering consistent with paper to make - * checking and validating easier - */ - uint Li = L16, Ri = R16; - - for (int i = this._rounds; i > 0; i--) - { - Lp = Li; - Rp = Ri; - - Li = Rp; - switch (i) - { - case 1: - case 4: - case 7: - case 10: - case 13: - case 16: - Ri = Lp ^ F1(Rp, this._km[i], this._kr[i]); - break; - case 2: - case 5: - case 8: - case 11: - case 14: - Ri = Lp ^ F2(Rp, this._km[i], this._kr[i]); - break; - case 3: - case 6: - case 9: - case 12: - case 15: - Ri = Lp ^ F3(Rp, this._km[i], this._kr[i]); - break; - } - } - - result[0] = Ri; - result[1] = Li; - } - - private static void Bits32ToInts(uint inData, int[] b, int offset) - { - b[offset + 3] = (int)(inData & 0xff); - b[offset + 2] = (int)((inData >> 8) & 0xff); - b[offset + 1] = (int)((inData >> 16) & 0xff); - b[offset] = (int)((inData >> 24) & 0xff); - } - - private static uint IntsTo32bits(int[] b, int i) - { - return (uint)(((b[i] & 0xff) << 24) | - ((b[i + 1] & 0xff) << 16) | - ((b[i + 2] & 0xff) << 8) | - ((b[i + 3] & 0xff))); - } - - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs deleted file mode 100644 index a57cb5a..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/CipherMode.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Linq; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Base class for cipher mode implementations - /// - public abstract class CipherMode - { - /// - /// Gets the cipher. - /// - protected BlockCipher Cipher; - - /// - /// Gets the IV vector. - /// - protected byte[] IV; - - /// - /// Holds block size of the cipher. - /// - protected int _blockSize; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - protected CipherMode(byte[] iv) - { - this.IV = iv; - } - - /// - /// Initializes the specified cipher mode. - /// - /// The cipher. - internal void Init(BlockCipher cipher) - { - this.Cipher = cipher; - this._blockSize = cipher.BlockSize; - this.IV = this.IV.Take(this._blockSize).ToArray(); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs b/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs deleted file mode 100644 index c76080d..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/CipherPadding.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Base class for cipher padding implementations - /// - public abstract class CipherPadding - { - /// - /// Pads specified input to match block size. - /// - /// Size of the block. - /// The input. - /// Padded data array. - public abstract byte[] Pad(int blockSize, byte[] input); - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs deleted file mode 100644 index 41ff0cc..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/DesCipher.cs +++ /dev/null @@ -1,484 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements DES cipher algorithm. - /// - public class DesCipher : BlockCipher - { - private int[] _encryptionKey; - - private int[] _decryptionKey; - - #region Static tables - - private static readonly short[] bytebit = - { - 128, 64, 32, 16, 8, 4, 2, 1 - }; - - private static readonly 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). - */ - private static readonly 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 - }; - - private static readonly byte[] totrot = - { - 1, 2, 4, 6, 8, 10, 12, 14, - 15, 17, 19, 21, 23, 25, 27, 28 - }; - - private static readonly 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - private static readonly uint[] 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 - }; - - #endregion - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - public DesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 8, mode, padding) - { - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._encryptionKey == null) - { - this._encryptionKey = GenerateWorkingKey(true, this.Key); - } - - DesFunc(this._encryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._decryptionKey == null) - { - this._decryptionKey = GenerateWorkingKey(false, this.Key); - } - - DesFunc(this._decryptionKey, inputBuffer, inputOffset, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Generates the working key. - /// - /// if set to true [encrypting]. - /// The key. - /// Generated working key. - protected int[] GenerateWorkingKey(bool encrypting, byte[] key) - { - this.ValidateKey(); - - int[] newKey = new int[32]; - bool[] pc1m = new bool[56]; - bool[] pcr = new bool[56]; - - for (int j = 0; j < 56; j++) - { - int l = pc1[j]; - - pc1m[j] = ((key[(uint)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] = (int) ((uint)((i1 & 0x00fc0000) << 6) | - (uint)((i1 & 0x00000fc0) << 10) | - ((uint)(i2 & 0x00fc0000) >> 10) | - ((uint)(i2 & 0x00000fc0) >> 6)); - - newKey[i + 1] = (int)((uint)((i1 & 0x0003f000) << 12) | - (uint)((i1 & 0x0000003f) << 16) | - ((uint)(i2 & 0x0003f000) >> 4) | - (uint)(i2 & 0x0000003f)); - } - - return newKey; - } - - /// - /// Validates the key. - /// - protected virtual void ValidateKey() - { - var keySize = this.Key.Length * 8; - - if (!(keySize == 64)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - } - - /// - /// Performs DES function. - /// - /// The w key. - /// The input. - /// The in off. - /// The out bytes. - /// The out off. - protected static void DesFunc(int[] wKey, byte[] input, int inOff, byte[] outBytes, int outOff) - { - uint left = BigEndianToUInt32(input, inOff); - uint right = BigEndianToUInt32(input, inOff + 4); - uint work; - - 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); - work = (left ^ right) & 0xaaaaaaaa; - left ^= work; - right ^= work; - left = (left << 1) | (left >> 31); - - for (int round = 0; round < 8; round++) - { - uint fval; - - work = (right << 28) | (right >> 4); - work ^= (uint)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 ^ (uint)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 ^= (uint)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 ^ (uint)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); - - UInt32ToBigEndian(right, outBytes, outOff); - UInt32ToBigEndian(left, outBytes, outOff + 4); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs deleted file mode 100644 index fff0ba9..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CbcCipherMode.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements CBC cipher mode - /// - public class CbcCipherMode : CipherMode - { - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public CbcCipherMode(byte[] iv) - : base(iv) - { - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - for (int i = 0; i < this._blockSize; i++) - { - this.IV[i] ^= inputBuffer[inputOffset + i]; - } - - this.Cipher.EncryptBlock(this.IV, 0, inputCount, outputBuffer, outputOffset); - - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, 0, this.IV.Length); - - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] ^= this.IV[i]; - } - - Buffer.BlockCopy(inputBuffer, inputOffset, this.IV, 0, this.IV.Length); - - return this._blockSize; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs deleted file mode 100644 index 71bfca9..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CfbCipherMode.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements CFB cipher mode - /// - public class CfbCipherMode : CipherMode - { - private readonly byte[] _ivOutput; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public CfbCipherMode(byte[] iv) - : base(iv) - { - this._ivOutput = new byte[iv.Length]; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(inputBuffer, inputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - return this._blockSize; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs deleted file mode 100644 index b51fdef..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/CtrCipherMode.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements CTR cipher mode - /// - public class CtrCipherMode : CipherMode - { - private readonly byte[] _ivOutput; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public CtrCipherMode(byte[] iv) - : base(iv) - { - this._ivOutput = new byte[iv.Length]; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - int j = this.IV.Length; - while (--j >= 0 && ++this.IV[j] == 0) ; - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - int j = this.IV.Length; - while (--j >= 0 && ++this.IV[j] == 0) ; - - return this._blockSize; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs deleted file mode 100644 index be320e5..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Modes/OfbCipherMode.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Globalization; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Modes -{ - /// - /// Implements OFB cipher mode - /// - public class OfbCipherMode : CipherMode - { - private readonly byte[] _ivOutput; - - /// - /// Initializes a new instance of the class. - /// - /// The iv. - public OfbCipherMode(byte[] iv) - : base(iv) - { - this._ivOutput = new byte[iv.Length]; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - return this._blockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputBuffer.Length - inputOffset < this._blockSize) - throw new ArgumentException("Invalid input buffer"); - - if (outputBuffer.Length - outputOffset < this._blockSize) - throw new ArgumentException("Invalid output buffer"); - - if (inputCount != this._blockSize) - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "inputCount must be {0}.", this._blockSize)); - - this.Cipher.EncryptBlock(this.IV, 0, this.IV.Length, this._ivOutput, 0); - - for (int i = 0; i < this._blockSize; i++) - { - outputBuffer[outputOffset + i] = (byte)(this._ivOutput[i] ^ inputBuffer[inputOffset + i]); - } - - Buffer.BlockCopy(this.IV, this._blockSize, this.IV, 0, this.IV.Length - this._blockSize); - Buffer.BlockCopy(outputBuffer, outputOffset, this.IV, this.IV.Length - this._blockSize, this._blockSize); - - return this._blockSize; - } - - - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs deleted file mode 100644 index e83f444..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS5Padding.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings -{ - /// - /// Implements PKCS5 cipher padding - /// - public class PKCS5Padding : CipherPadding - { - /// - /// Transforms the specified input. - /// - /// Size of the block. - /// The input. - /// - /// Padded data array. - /// - public override byte[] Pad(int blockSize, byte[] input) - { - var numOfPaddedBytes = blockSize - (input.Length % blockSize); - - var output = new byte[input.Length + numOfPaddedBytes]; - Buffer.BlockCopy(input, 0, output, 0, input.Length); - for (int i = 0; i < numOfPaddedBytes; i++) - { - output[input.Length + i] = (byte)numOfPaddedBytes; - } - - return output; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs b/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs deleted file mode 100644 index 372ab3f..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/Paddings/PKCS7Padding.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers.Paddings -{ - /// - /// Implements PKCS7 cipher padding - /// - public class PKCS7Padding : CipherPadding - { - /// - /// Transforms the specified input. - /// - /// Size of the block. - /// The input. - /// - /// Padded data array. - /// - public override byte[] Pad(int blockSize, byte[] input) - { - var numOfPaddedBytes = blockSize - (input.Length % blockSize); - - var output = new byte[input.Length + numOfPaddedBytes]; - Buffer.BlockCopy(input, 0, output, 0, input.Length); - for (int i = 0; i < numOfPaddedBytes; i++) - { - output[input.Length + i] = (byte)numOfPaddedBytes; - } - - return output; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs deleted file mode 100644 index c80b135..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/RsaCipher.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements RSA cipher algorithm. - /// - public class RsaCipher : AsymmetricCipher - { - private readonly bool _isPrivate; - - private readonly RsaKey _key; - - /// - /// Initializes a new instance of the class. - /// - /// The RSA key. - public RsaCipher(RsaKey key) - { - if (key == null) - throw new ArgumentNullException("key"); - - this._key = key; - this._isPrivate = !this._key.D.IsZero; - } - - /// - /// Encrypts the specified data. - /// - /// The data. - /// Encrypted data. - public override byte[] Encrypt(byte[] data) - { - // Calculate signature - var bitLength = this._key.Modulus.BitLength; - - var paddedBlock = new byte[bitLength / 8 + (bitLength % 8 > 0 ? 1 : 0) - 1]; - - paddedBlock[0] = 0x01; - for (int i = 1; i < paddedBlock.Length - data.Length - 1; i++) - { - paddedBlock[i] = 0xFF; - } - - Buffer.BlockCopy(data, 0, paddedBlock, paddedBlock.Length - data.Length, data.Length); - - return this.Transform(paddedBlock); - } - - /// - /// Decrypts the specified data. - /// - /// The data. - /// - /// Decrypted data. - /// - /// Only block type 01 or 02 are supported. - /// Thrown when decrypted block type is not supported. - public override byte[] Decrypt(byte[] data) - { - var paddedBlock = this.Transform(data); - - if (paddedBlock[0] != 1 && paddedBlock[0] != 2) - throw new NotSupportedException("Only block type 01 or 02 are supported."); - - var position = 1; - while (position < paddedBlock.Length && paddedBlock[position] != 0) - position++; - position++; - - var result = new byte[paddedBlock.Length - position]; - - Buffer.BlockCopy(paddedBlock, position, result, 0, result.Length); - - return result; - } - - private byte[] Transform(byte[] data) - { - var bytes = new List(data.Reverse()); - bytes.Add(0); - - var input = new BigInteger(bytes.ToArray()); - - BigInteger result; - - if (this._isPrivate) - { - BigInteger random = BigInteger.One; - - var max = this._key.Modulus - 1; - - var bitLength = this._key.Modulus.BitLength; - - if (max < BigInteger.One) - throw new SshException("Invalid RSA key."); - - while (random <= BigInteger.One || random >= max) - { - random = BigInteger.Random(bitLength); - } - - BigInteger blindedInput = BigInteger.PositiveMod((BigInteger.ModPow(random, this._key.Exponent, this._key.Modulus) * input), this._key.Modulus); - - // mP = ((input Mod p) ^ dP)) Mod p - var mP = BigInteger.ModPow((blindedInput % this._key.P), this._key.DP, this._key.P); - - // mQ = ((input Mod q) ^ dQ)) Mod q - var mQ = BigInteger.ModPow((blindedInput % this._key.Q), this._key.DQ, this._key.Q); - - var h = BigInteger.PositiveMod(((mP - mQ) * this._key.InverseQ), this._key.P); - - var m = h * this._key.Q + mQ; - - BigInteger rInv = BigInteger.ModInverse(random, this._key.Modulus); - - result = BigInteger.PositiveMod((m * rInv), this._key.Modulus); - } - else - { - result = BigInteger.ModPow(input, this._key.Exponent, this._key.Modulus); - } - - return result.ToByteArray().Reverse().ToArray(); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs deleted file mode 100644 index 5f58320..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/SerpentCipher.cs +++ /dev/null @@ -1,769 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements Serpent cipher algorithm. - /// - public sealed class SerpentCipher : BlockCipher - { - private const int ROUNDS = 32; - - private const int PHI = unchecked((int) 0x9E3779B9); // (Sqrt(5) - 1) * 2**31 - - private readonly int[] _workingKey; - - private int _x0, _x1, _x2, _x3; // registers - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public SerpentCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize == 128 || keySize == 192 || keySize == 256)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - this._workingKey = this.MakeWorkingKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - this._x3 = BytesToWord(inputBuffer, inputOffset); - this._x2 = BytesToWord(inputBuffer, inputOffset + 4); - this._x1 = BytesToWord(inputBuffer, inputOffset + 8); - this._x0 = BytesToWord(inputBuffer, inputOffset + 12); - - Sb0(this._workingKey[0] ^ this._x0, this._workingKey[1] ^ this._x1, this._workingKey[2] ^ this._x2, this._workingKey[3] ^ this._x3); LT(); - Sb1(this._workingKey[4] ^ this._x0, this._workingKey[5] ^ this._x1, this._workingKey[6] ^ this._x2, this._workingKey[7] ^ this._x3); LT(); - Sb2(this._workingKey[8] ^ this._x0, this._workingKey[9] ^ this._x1, this._workingKey[10] ^ this._x2, this._workingKey[11] ^ this._x3); LT(); - Sb3(this._workingKey[12] ^ this._x0, this._workingKey[13] ^ this._x1, this._workingKey[14] ^ this._x2, this._workingKey[15] ^ this._x3); LT(); - Sb4(this._workingKey[16] ^ this._x0, this._workingKey[17] ^ this._x1, this._workingKey[18] ^ this._x2, this._workingKey[19] ^ this._x3); LT(); - Sb5(this._workingKey[20] ^ this._x0, this._workingKey[21] ^ this._x1, this._workingKey[22] ^ this._x2, this._workingKey[23] ^ this._x3); LT(); - Sb6(this._workingKey[24] ^ this._x0, this._workingKey[25] ^ this._x1, this._workingKey[26] ^ this._x2, this._workingKey[27] ^ this._x3); LT(); - Sb7(this._workingKey[28] ^ this._x0, this._workingKey[29] ^ this._x1, this._workingKey[30] ^ this._x2, this._workingKey[31] ^ this._x3); LT(); - Sb0(this._workingKey[32] ^ this._x0, this._workingKey[33] ^ this._x1, this._workingKey[34] ^ this._x2, this._workingKey[35] ^ this._x3); LT(); - Sb1(this._workingKey[36] ^ this._x0, this._workingKey[37] ^ this._x1, this._workingKey[38] ^ this._x2, this._workingKey[39] ^ this._x3); LT(); - Sb2(this._workingKey[40] ^ this._x0, this._workingKey[41] ^ this._x1, this._workingKey[42] ^ this._x2, this._workingKey[43] ^ this._x3); LT(); - Sb3(this._workingKey[44] ^ this._x0, this._workingKey[45] ^ this._x1, this._workingKey[46] ^ this._x2, this._workingKey[47] ^ this._x3); LT(); - Sb4(this._workingKey[48] ^ this._x0, this._workingKey[49] ^ this._x1, this._workingKey[50] ^ this._x2, this._workingKey[51] ^ this._x3); LT(); - Sb5(this._workingKey[52] ^ this._x0, this._workingKey[53] ^ this._x1, this._workingKey[54] ^ this._x2, this._workingKey[55] ^ this._x3); LT(); - Sb6(this._workingKey[56] ^ this._x0, this._workingKey[57] ^ this._x1, this._workingKey[58] ^ this._x2, this._workingKey[59] ^ this._x3); LT(); - Sb7(this._workingKey[60] ^ this._x0, this._workingKey[61] ^ this._x1, this._workingKey[62] ^ this._x2, this._workingKey[63] ^ this._x3); LT(); - Sb0(this._workingKey[64] ^ this._x0, this._workingKey[65] ^ this._x1, this._workingKey[66] ^ this._x2, this._workingKey[67] ^ this._x3); LT(); - Sb1(this._workingKey[68] ^ this._x0, this._workingKey[69] ^ this._x1, this._workingKey[70] ^ this._x2, this._workingKey[71] ^ this._x3); LT(); - Sb2(this._workingKey[72] ^ this._x0, this._workingKey[73] ^ this._x1, this._workingKey[74] ^ this._x2, this._workingKey[75] ^ this._x3); LT(); - Sb3(this._workingKey[76] ^ this._x0, this._workingKey[77] ^ this._x1, this._workingKey[78] ^ this._x2, this._workingKey[79] ^ this._x3); LT(); - Sb4(this._workingKey[80] ^ this._x0, this._workingKey[81] ^ this._x1, this._workingKey[82] ^ this._x2, this._workingKey[83] ^ this._x3); LT(); - Sb5(this._workingKey[84] ^ this._x0, this._workingKey[85] ^ this._x1, this._workingKey[86] ^ this._x2, this._workingKey[87] ^ this._x3); LT(); - Sb6(this._workingKey[88] ^ this._x0, this._workingKey[89] ^ this._x1, this._workingKey[90] ^ this._x2, this._workingKey[91] ^ this._x3); LT(); - Sb7(this._workingKey[92] ^ this._x0, this._workingKey[93] ^ this._x1, this._workingKey[94] ^ this._x2, this._workingKey[95] ^ this._x3); LT(); - Sb0(this._workingKey[96] ^ this._x0, this._workingKey[97] ^ this._x1, this._workingKey[98] ^ this._x2, this._workingKey[99] ^ this._x3); LT(); - Sb1(this._workingKey[100] ^ this._x0, this._workingKey[101] ^ this._x1, this._workingKey[102] ^ this._x2, this._workingKey[103] ^ this._x3); LT(); - Sb2(this._workingKey[104] ^ this._x0, this._workingKey[105] ^ this._x1, this._workingKey[106] ^ this._x2, this._workingKey[107] ^ this._x3); LT(); - Sb3(this._workingKey[108] ^ this._x0, this._workingKey[109] ^ this._x1, this._workingKey[110] ^ this._x2, this._workingKey[111] ^ this._x3); LT(); - Sb4(this._workingKey[112] ^ this._x0, this._workingKey[113] ^ this._x1, this._workingKey[114] ^ this._x2, this._workingKey[115] ^ this._x3); LT(); - Sb5(this._workingKey[116] ^ this._x0, this._workingKey[117] ^ this._x1, this._workingKey[118] ^ this._x2, this._workingKey[119] ^ this._x3); LT(); - Sb6(this._workingKey[120] ^ this._x0, this._workingKey[121] ^ this._x1, this._workingKey[122] ^ this._x2, this._workingKey[123] ^ this._x3); LT(); - Sb7(this._workingKey[124] ^ this._x0, this._workingKey[125] ^ this._x1, this._workingKey[126] ^ this._x2, this._workingKey[127] ^ this._x3); - - WordToBytes(this._workingKey[131] ^ this._x3, outputBuffer, outputOffset); - WordToBytes(this._workingKey[130] ^ this._x2, outputBuffer, outputOffset + 4); - WordToBytes(this._workingKey[129] ^ this._x1, outputBuffer, outputOffset + 8); - WordToBytes(this._workingKey[128] ^ this._x0, outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if (inputCount != this.BlockSize) - throw new ArgumentException("inputCount"); - - this._x3 = this._workingKey[131] ^ BytesToWord(inputBuffer, inputOffset); - this._x2 = this._workingKey[130] ^ BytesToWord(inputBuffer, inputOffset + 4); - this._x1 = this._workingKey[129] ^ BytesToWord(inputBuffer, inputOffset + 8); - this._x0 = this._workingKey[128] ^ BytesToWord(inputBuffer, inputOffset + 12); - - Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[124]; this._x1 ^= this._workingKey[125]; this._x2 ^= this._workingKey[126]; this._x3 ^= this._workingKey[127]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[120]; this._x1 ^= this._workingKey[121]; this._x2 ^= this._workingKey[122]; this._x3 ^= this._workingKey[123]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[116]; this._x1 ^= this._workingKey[117]; this._x2 ^= this._workingKey[118]; this._x3 ^= this._workingKey[119]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[112]; this._x1 ^= this._workingKey[113]; this._x2 ^= this._workingKey[114]; this._x3 ^= this._workingKey[115]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[108]; this._x1 ^= this._workingKey[109]; this._x2 ^= this._workingKey[110]; this._x3 ^= this._workingKey[111]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[104]; this._x1 ^= this._workingKey[105]; this._x2 ^= this._workingKey[106]; this._x3 ^= this._workingKey[107]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[100]; this._x1 ^= this._workingKey[101]; this._x2 ^= this._workingKey[102]; this._x3 ^= this._workingKey[103]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[96]; this._x1 ^= this._workingKey[97]; this._x2 ^= this._workingKey[98]; this._x3 ^= this._workingKey[99]; - InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[92]; this._x1 ^= this._workingKey[93]; this._x2 ^= this._workingKey[94]; this._x3 ^= this._workingKey[95]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[88]; this._x1 ^= this._workingKey[89]; this._x2 ^= this._workingKey[90]; this._x3 ^= this._workingKey[91]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[84]; this._x1 ^= this._workingKey[85]; this._x2 ^= this._workingKey[86]; this._x3 ^= this._workingKey[87]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[80]; this._x1 ^= this._workingKey[81]; this._x2 ^= this._workingKey[82]; this._x3 ^= this._workingKey[83]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[76]; this._x1 ^= this._workingKey[77]; this._x2 ^= this._workingKey[78]; this._x3 ^= this._workingKey[79]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[72]; this._x1 ^= this._workingKey[73]; this._x2 ^= this._workingKey[74]; this._x3 ^= this._workingKey[75]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[68]; this._x1 ^= this._workingKey[69]; this._x2 ^= this._workingKey[70]; this._x3 ^= this._workingKey[71]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[64]; this._x1 ^= this._workingKey[65]; this._x2 ^= this._workingKey[66]; this._x3 ^= this._workingKey[67]; - InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[60]; this._x1 ^= this._workingKey[61]; this._x2 ^= this._workingKey[62]; this._x3 ^= this._workingKey[63]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[56]; this._x1 ^= this._workingKey[57]; this._x2 ^= this._workingKey[58]; this._x3 ^= this._workingKey[59]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[52]; this._x1 ^= this._workingKey[53]; this._x2 ^= this._workingKey[54]; this._x3 ^= this._workingKey[55]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[48]; this._x1 ^= this._workingKey[49]; this._x2 ^= this._workingKey[50]; this._x3 ^= this._workingKey[51]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[44]; this._x1 ^= this._workingKey[45]; this._x2 ^= this._workingKey[46]; this._x3 ^= this._workingKey[47]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[40]; this._x1 ^= this._workingKey[41]; this._x2 ^= this._workingKey[42]; this._x3 ^= this._workingKey[43]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[36]; this._x1 ^= this._workingKey[37]; this._x2 ^= this._workingKey[38]; this._x3 ^= this._workingKey[39]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[32]; this._x1 ^= this._workingKey[33]; this._x2 ^= this._workingKey[34]; this._x3 ^= this._workingKey[35]; - InverseLT(); Ib7(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[28]; this._x1 ^= this._workingKey[29]; this._x2 ^= this._workingKey[30]; this._x3 ^= this._workingKey[31]; - InverseLT(); Ib6(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[24]; this._x1 ^= this._workingKey[25]; this._x2 ^= this._workingKey[26]; this._x3 ^= this._workingKey[27]; - InverseLT(); Ib5(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[20]; this._x1 ^= this._workingKey[21]; this._x2 ^= this._workingKey[22]; this._x3 ^= this._workingKey[23]; - InverseLT(); Ib4(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[16]; this._x1 ^= this._workingKey[17]; this._x2 ^= this._workingKey[18]; this._x3 ^= this._workingKey[19]; - InverseLT(); Ib3(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[12]; this._x1 ^= this._workingKey[13]; this._x2 ^= this._workingKey[14]; this._x3 ^= this._workingKey[15]; - InverseLT(); Ib2(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[8]; this._x1 ^= this._workingKey[9]; this._x2 ^= this._workingKey[10]; this._x3 ^= this._workingKey[11]; - InverseLT(); Ib1(this._x0, this._x1, this._x2, this._x3); - this._x0 ^= this._workingKey[4]; this._x1 ^= this._workingKey[5]; this._x2 ^= this._workingKey[6]; this._x3 ^= this._workingKey[7]; - InverseLT(); Ib0(this._x0, this._x1, this._x2, this._x3); - - WordToBytes(this._x3 ^ this._workingKey[3], outputBuffer, outputOffset); - WordToBytes(this._x2 ^ this._workingKey[2], outputBuffer, outputOffset + 4); - WordToBytes(this._x1 ^ this._workingKey[1], outputBuffer, outputOffset + 8); - WordToBytes(this._x0 ^ this._workingKey[0], outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - /** - * Expand a user-supplied key material into a session key. - * - * @param key The user-key bytes (multiples of 4) to use. - * @exception ArgumentException - */ - private int[] MakeWorkingKey(byte[] key) - { - // - // pad key to 256 bits - // - int[] kPad = new int[16]; - int off; - int length = 0; - - for (off = key.Length - 4; off > 0; off -= 4) - { - kPad[length++] = BytesToWord(key, off); - } - - if (off == 0) - { - kPad[length++] = BytesToWord(key, 0); - if (length < 8) - { - kPad[length] = 1; - } - } - else - { - throw new ArgumentException("key must be a multiple of 4 bytes"); - } - - // - // expand the padded key up to 33 x 128 bits of key material - // - int amount = (ROUNDS + 1) * 4; - int[] w = new int[amount]; - - // - // compute w0 to w7 from w-8 to w-1 - // - for (int i = 8; i < 16; i++) - { - kPad[i] = RotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11); - } - - Buffer.BlockCopy(kPad, 8, w, 0, 8); - - // - // compute w8 to w136 - // - for (int i = 8; i < amount; i++) - { - w[i] = RotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11); - } - - // - // create the working keys by processing w with the Sbox and IP - // - Sb3(w[0], w[1], w[2], w[3]); - w[0] = this._x0; w[1] = this._x1; w[2] = this._x2; w[3] = this._x3; - Sb2(w[4], w[5], w[6], w[7]); - w[4] = this._x0; w[5] = this._x1; w[6] = this._x2; w[7] = this._x3; - Sb1(w[8], w[9], w[10], w[11]); - w[8] = this._x0; w[9] = this._x1; w[10] = this._x2; w[11] = this._x3; - Sb0(w[12], w[13], w[14], w[15]); - w[12] = this._x0; w[13] = this._x1; w[14] = this._x2; w[15] = this._x3; - Sb7(w[16], w[17], w[18], w[19]); - w[16] = this._x0; w[17] = this._x1; w[18] = this._x2; w[19] = this._x3; - Sb6(w[20], w[21], w[22], w[23]); - w[20] = this._x0; w[21] = this._x1; w[22] = this._x2; w[23] = this._x3; - Sb5(w[24], w[25], w[26], w[27]); - w[24] = this._x0; w[25] = this._x1; w[26] = this._x2; w[27] = this._x3; - Sb4(w[28], w[29], w[30], w[31]); - w[28] = this._x0; w[29] = this._x1; w[30] = this._x2; w[31] = this._x3; - Sb3(w[32], w[33], w[34], w[35]); - w[32] = this._x0; w[33] = this._x1; w[34] = this._x2; w[35] = this._x3; - Sb2(w[36], w[37], w[38], w[39]); - w[36] = this._x0; w[37] = this._x1; w[38] = this._x2; w[39] = this._x3; - Sb1(w[40], w[41], w[42], w[43]); - w[40] = this._x0; w[41] = this._x1; w[42] = this._x2; w[43] = this._x3; - Sb0(w[44], w[45], w[46], w[47]); - w[44] = this._x0; w[45] = this._x1; w[46] = this._x2; w[47] = this._x3; - Sb7(w[48], w[49], w[50], w[51]); - w[48] = this._x0; w[49] = this._x1; w[50] = this._x2; w[51] = this._x3; - Sb6(w[52], w[53], w[54], w[55]); - w[52] = this._x0; w[53] = this._x1; w[54] = this._x2; w[55] = this._x3; - Sb5(w[56], w[57], w[58], w[59]); - w[56] = this._x0; w[57] = this._x1; w[58] = this._x2; w[59] = this._x3; - Sb4(w[60], w[61], w[62], w[63]); - w[60] = this._x0; w[61] = this._x1; w[62] = this._x2; w[63] = this._x3; - Sb3(w[64], w[65], w[66], w[67]); - w[64] = this._x0; w[65] = this._x1; w[66] = this._x2; w[67] = this._x3; - Sb2(w[68], w[69], w[70], w[71]); - w[68] = this._x0; w[69] = this._x1; w[70] = this._x2; w[71] = this._x3; - Sb1(w[72], w[73], w[74], w[75]); - w[72] = this._x0; w[73] = this._x1; w[74] = this._x2; w[75] = this._x3; - Sb0(w[76], w[77], w[78], w[79]); - w[76] = this._x0; w[77] = this._x1; w[78] = this._x2; w[79] = this._x3; - Sb7(w[80], w[81], w[82], w[83]); - w[80] = this._x0; w[81] = this._x1; w[82] = this._x2; w[83] = this._x3; - Sb6(w[84], w[85], w[86], w[87]); - w[84] = this._x0; w[85] = this._x1; w[86] = this._x2; w[87] = this._x3; - Sb5(w[88], w[89], w[90], w[91]); - w[88] = this._x0; w[89] = this._x1; w[90] = this._x2; w[91] = this._x3; - Sb4(w[92], w[93], w[94], w[95]); - w[92] = this._x0; w[93] = this._x1; w[94] = this._x2; w[95] = this._x3; - Sb3(w[96], w[97], w[98], w[99]); - w[96] = this._x0; w[97] = this._x1; w[98] = this._x2; w[99] = this._x3; - Sb2(w[100], w[101], w[102], w[103]); - w[100] = this._x0; w[101] = this._x1; w[102] = this._x2; w[103] = this._x3; - Sb1(w[104], w[105], w[106], w[107]); - w[104] = this._x0; w[105] = this._x1; w[106] = this._x2; w[107] = this._x3; - Sb0(w[108], w[109], w[110], w[111]); - w[108] = this._x0; w[109] = this._x1; w[110] = this._x2; w[111] = this._x3; - Sb7(w[112], w[113], w[114], w[115]); - w[112] = this._x0; w[113] = this._x1; w[114] = this._x2; w[115] = this._x3; - Sb6(w[116], w[117], w[118], w[119]); - w[116] = this._x0; w[117] = this._x1; w[118] = this._x2; w[119] = this._x3; - Sb5(w[120], w[121], w[122], w[123]); - w[120] = this._x0; w[121] = this._x1; w[122] = this._x2; w[123] = this._x3; - Sb4(w[124], w[125], w[126], w[127]); - w[124] = this._x0; w[125] = this._x1; w[126] = this._x2; w[127] = this._x3; - Sb3(w[128], w[129], w[130], w[131]); - w[128] = this._x0; w[129] = this._x1; w[130] = this._x2; w[131] = this._x3; - - return w; - } - - private static int RotateLeft(int x, int bits) - { - return ((x << bits) | (int)((uint)x >> (32 - bits))); - } - - private static int RotateRight(int x, int bits) - { - return ((int)((uint)x >> bits) | (x << (32 - bits))); - } - - private static int BytesToWord(byte[] src, int srcOff) - { - return (((src[srcOff] & 0xff) << 24) | ((src[srcOff + 1] & 0xff) << 16) | - ((src[srcOff + 2] & 0xff) << 8) | ((src[srcOff + 3] & 0xff))); - } - - private static void WordToBytes(int word, byte[] dst, int dstOff) - { - dst[dstOff + 3] = (byte)(word); - dst[dstOff + 2] = (byte)((uint)word >> 8); - dst[dstOff + 1] = (byte)((uint)word >> 16); - dst[dstOff] = (byte)((uint)word >> 24); - } - - /* - * The sboxes below are based on the work of Brian Gladman and - * Sam Simpson, whose original notice appears below. - *

- * For further details see: - * http://fp.gladman.plus.com/cryptography_technology/serpent/ - *

- */ - - /* Partially optimised Serpent S Box bool functions derived */ - /* using a recursive descent analyser but without a full search */ - /* of all subtrees. This set of S boxes is the result of work */ - /* by Sam Simpson and Brian Gladman using the spare time on a */ - /* cluster of high capacity servers to search for S boxes with */ - /* this customised search engine. There are now an average of */ - /* 15.375 terms per S box. */ - /* */ - /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */ - /* and Sam Simpson (s.simpson@mia.co.uk) */ - /* 17th December 1998 */ - /* */ - /* We hereby give permission for information in this file to be */ - /* used freely subject only to acknowledgement of its origin. */ - - /// - /// S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb0(int a, int b, int c, int d) - { - int t1 = a ^ d; - int t3 = c ^ t1; - int t4 = b ^ t3; - this._x3 = (a & d) ^ t4; - int t7 = a ^ (b & t1); - this._x2 = t4 ^ (c | t7); - int t12 = this._x3 & (t3 ^ t7); - this._x1 = (~t3) ^ t12; - this._x0 = t12 ^ (~t7); - } - - /// - /// InvSO - {13, 3,11, 0,10, 6, 5,12, 1,14, 4, 7,15, 9, 8, 2 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib0(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ b; - int t4 = d ^ (t1 | t2); - int t5 = c ^ t4; - this._x2 = t2 ^ t5; - int t8 = t1 ^ (d & t2); - this._x1 = t4 ^ (this._x2 & t8); - this._x3 = (a & t4) ^ (t5 | this._x1); - this._x0 = this._x3 ^ (t5 ^ t8); - } - - /// - /// S1 - {15,12, 2, 7, 9, 0, 5,10, 1,11,14, 8, 6,13, 3, 4 } - 14 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb1(int a, int b, int c, int d) - { - int t2 = b ^ (~a); - int t5 = c ^ (a | t2); - this._x2 = d ^ t5; - int t7 = b ^ (d | t2); - int t8 = t2 ^ this._x2; - this._x3 = t8 ^ (t5 & t7); - int t11 = t5 ^ t7; - this._x1 = this._x3 ^ t11; - this._x0 = t5 ^ (t8 & t11); - } - - /// - /// InvS1 - { 5, 8, 2,14,15, 6,12, 3,11, 4, 7, 9, 1,13,10, 0 } - 14 steps. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib1(int a, int b, int c, int d) - { - int t1 = b ^ d; - int t3 = a ^ (b & t1); - int t4 = t1 ^ t3; - this._x3 = c ^ t4; - int t7 = b ^ (t1 & t3); - int t8 = this._x3 | t7; - this._x1 = t3 ^ t8; - int t10 = ~this._x1; - int t11 = this._x3 ^ t7; - this._x0 = t10 ^ t11; - this._x2 = t4 ^ (t10 | t11); - } - - /// - /// S2 - { 8, 6, 7, 9, 3,12,10,15,13, 1,14, 4, 0,11, 5, 2 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb2(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = b ^ d; - int t3 = c & t1; - this._x0 = t2 ^ t3; - int t5 = c ^ t1; - int t6 = c ^ this._x0; - int t7 = b & t6; - this._x3 = t5 ^ t7; - this._x2 = a ^ ((d | t7) & (this._x0 | t5)); - this._x1 = (t2 ^ this._x3) ^ (this._x2 ^ (d | t1)); - } - - /// - /// InvS2 - {12, 9,15, 4,11,14, 1, 2, 0, 3, 6,13, 5, 8,10, 7 } - 16 steps. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib2(int a, int b, int c, int d) - { - int t1 = b ^ d; - int t2 = ~t1; - int t3 = a ^ c; - int t4 = c ^ t1; - int t5 = b & t4; - this._x0 = t3 ^ t5; - int t7 = a | t2; - int t8 = d ^ t7; - int t9 = t3 | t8; - this._x3 = t1 ^ t9; - int t11 = ~t4; - int t12 = this._x0 | this._x3; - this._x1 = t11 ^ t12; - this._x2 = (d & t11) ^ (t3 ^ t12); - } - - /// - /// S3 - { 0,15,11, 8,12, 9, 6, 3,13, 1, 2, 4,10, 7, 5,14 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb3(int a, int b, int c, int d) - { - int t1 = a ^ b; - int t2 = a & c; - int t3 = a | d; - int t4 = c ^ d; - int t5 = t1 & t3; - int t6 = t2 | t5; - this._x2 = t4 ^ t6; - int t8 = b ^ t3; - int t9 = t6 ^ t8; - int t10 = t4 & t9; - this._x0 = t1 ^ t10; - int t12 = this._x2 & this._x0; - this._x1 = t9 ^ t12; - this._x3 = (b | d) ^ (t4 ^ t12); - } - - /// - /// InvS3 - { 0, 9,10, 7,11,14, 6,13, 3, 5,12, 2, 4, 8,15, 1 } - 15 terms - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib3(int a, int b, int c, int d) - { - int t1 = a | b; - int t2 = b ^ c; - int t3 = b & t2; - int t4 = a ^ t3; - int t5 = c ^ t4; - int t6 = d | t4; - this._x0 = t2 ^ t6; - int t8 = t2 | t6; - int t9 = d ^ t8; - this._x2 = t5 ^ t9; - int t11 = t1 ^ t9; - int t12 = this._x0 & t11; - this._x3 = t4 ^ t12; - this._x1 = this._x3 ^ (this._x0 ^ t11); - } - - /// - /// S4 - { 1,15, 8, 3,12, 0,11, 6, 2, 5, 4,10, 9,14, 7,13 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb4(int a, int b, int c, int d) - { - int t1 = a ^ d; - int t2 = d & t1; - int t3 = c ^ t2; - int t4 = b | t3; - this._x3 = t1 ^ t4; - int t6 = ~b; - int t7 = t1 | t6; - this._x0 = t3 ^ t7; - int t9 = a & this._x0; - int t10 = t1 ^ t6; - int t11 = t4 & t10; - this._x2 = t9 ^ t11; - this._x1 = (a ^ t3) ^ (t10 & this._x2); - } - - /// - /// InvS4 - { 5, 0, 8, 3,10, 9, 7,14, 2,12,11, 6, 4,15,13, 1 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib4(int a, int b, int c, int d) - { - int t1 = c | d; - int t2 = a & t1; - int t3 = b ^ t2; - int t4 = a & t3; - int t5 = c ^ t4; - this._x1 = d ^ t5; - int t7 = ~a; - int t8 = t5 & this._x1; - this._x3 = t3 ^ t8; - int t10 = this._x1 | t7; - int t11 = d ^ t10; - this._x0 = this._x3 ^ t11; - this._x2 = (t3 & t11) ^ (this._x1 ^ t7); - } - - /// - /// S5 - {15, 5, 2,11, 4,10, 9,12, 0, 3,14, 8,13, 6, 7, 1 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb5(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ b; - int t3 = a ^ d; - int t4 = c ^ t1; - int t5 = t2 | t3; - this._x0 = t4 ^ t5; - int t7 = d & this._x0; - int t8 = t2 ^ this._x0; - this._x1 = t7 ^ t8; - int t10 = t1 | this._x0; - int t11 = t2 | t7; - int t12 = t3 ^ t10; - this._x2 = t11 ^ t12; - this._x3 = (b ^ t7) ^ (this._x1 & t12); - } - - /// - /// InvS5 - { 8,15, 2, 9, 4, 1,13,14,11, 6, 5, 3, 7,12,10, 0 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib5(int a, int b, int c, int d) - { - int t1 = ~c; - int t2 = b & t1; - int t3 = d ^ t2; - int t4 = a & t3; - int t5 = b ^ t1; - this._x3 = t4 ^ t5; - int t7 = b | this._x3; - int t8 = a & t7; - this._x1 = t3 ^ t8; - int t10 = a | d; - int t11 = t1 ^ t7; - this._x0 = t10 ^ t11; - this._x2 = (b & t10) ^ (t4 | (a ^ c)); - } - - /// - /// S6 - { 7, 2,12, 5, 8, 4, 6,11,14, 9, 1,15,13, 3,10, 0 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb6(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ d; - int t3 = b ^ t2; - int t4 = t1 | t2; - int t5 = c ^ t4; - this._x1 = b ^ t5; - int t7 = t2 | this._x1; - int t8 = d ^ t7; - int t9 = t5 & t8; - this._x2 = t3 ^ t9; - int t11 = t5 ^ t8; - this._x0 = this._x2 ^ t11; - this._x3 = (~t5) ^ (t3 & t11); - } - - /// - /// InvS6 - {15,10, 1,13, 5, 3, 6, 0, 4, 9,14, 7, 2,12, 8,11 } - 15 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib6(int a, int b, int c, int d) - { - int t1 = ~a; - int t2 = a ^ b; - int t3 = c ^ t2; - int t4 = c | t1; - int t5 = d ^ t4; - this._x1 = t3 ^ t5; - int t7 = t3 & t5; - int t8 = t2 ^ t7; - int t9 = b | t8; - this._x3 = t5 ^ t9; - int t11 = b | this._x3; - this._x0 = t8 ^ t11; - this._x2 = (d & t1) ^ (t3 ^ t11); - } - - /// - /// S7 - { 1,13,15, 0,14, 8, 2,11, 7, 4,12,10, 9, 3, 5, 6 } - 16 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Sb7(int a, int b, int c, int d) - { - int t1 = b ^ c; - int t2 = c & t1; - int t3 = d ^ t2; - int t4 = a ^ t3; - int t5 = d | t1; - int t6 = t4 & t5; - this._x1 = b ^ t6; - int t8 = t3 | this._x1; - int t9 = a & t4; - this._x3 = t1 ^ t9; - int t11 = t4 ^ t8; - int t12 = this._x3 & t11; - this._x2 = t3 ^ t12; - this._x0 = (~t11) ^ (this._x3 & this._x2); - } - - /// - /// InvS7 - { 3, 0, 6,13, 9,14,15, 8, 5,12,11, 7,10, 1, 4, 2 } - 17 terms. - /// - /// A. - /// The b. - /// The c. - /// The d. - private void Ib7(int a, int b, int c, int d) - { - int t3 = c | (a & b); - int t4 = d & (a | b); - this._x3 = t3 ^ t4; - int t6 = ~d; - int t7 = b ^ t4; - int t9 = t7 | (this._x3 ^ t6); - this._x1 = a ^ t9; - this._x0 = (c ^ t7) ^ (d | this._x1); - this._x2 = (t3 ^ this._x1) ^ (this._x0 ^ (a & this._x3)); - } - - /// - /// Apply the linear transformation to the register set. - /// - private void LT() - { - int x0 = RotateLeft(this._x0, 13); - int x2 = RotateLeft(this._x2, 3); - int x1 = this._x1 ^ x0 ^ x2; - int x3 = this._x3 ^ x2 ^ x0 << 3; - - this._x1 = RotateLeft(x1, 1); - this._x3 = RotateLeft(x3, 7); - this._x0 = RotateLeft(x0 ^ this._x1 ^ this._x3, 5); - this._x2 = RotateLeft(x2 ^ this._x3 ^ (this._x1 << 7), 22); - } - - /// - /// Apply the inverse of the linear transformation to the register set. - /// - private void InverseLT() - { - int x2 = RotateRight(this._x2, 22) ^ this._x3 ^ (this._x1 << 7); - int x0 = RotateRight(this._x0, 5) ^ this._x1 ^ this._x3; - int x3 = RotateRight(this._x3, 7); - int x1 = RotateRight(this._x1, 1); - this._x3 = x3 ^ x2 ^ x0 << 3; - this._x1 = x1 ^ x0 ^ x2; - this._x2 = RotateRight(x2, 3); - this._x0 = RotateRight(x0, 13); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs deleted file mode 100644 index 6bd2d51..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/TripleDesCipher.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements 3DES cipher algorithm. - /// - public sealed class TripleDesCipher : DesCipher - { - private int[] _encryptionKey1; - - private int[] _encryptionKey2; - - private int[] _encryptionKey3; - - private int[] _decryptionKey1; - - private int[] _decryptionKey2; - - private int[] _decryptionKey3; - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - public TripleDesCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, mode, padding) - { - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._encryptionKey1 == null || this._encryptionKey2 == null || this._encryptionKey3 == null) - { - var part1 = new byte[8]; - var part2 = new byte[8]; - - Buffer.BlockCopy(this.Key, 0, part1, 0, 8); - Buffer.BlockCopy(this.Key, 8, part2, 0, 8); - - this._encryptionKey1 = this.GenerateWorkingKey(true, part1); - - this._encryptionKey2 = this.GenerateWorkingKey(false, part2); - - if (this.Key.Length == 24) - { - var part3 = new byte[8]; - Buffer.BlockCopy(this.Key, 16, part3, 0, 8); - - this._encryptionKey3 = this.GenerateWorkingKey(true, part3); - } - else - { - this._encryptionKey3 = this._encryptionKey1; - } - } - - byte[] temp = new byte[this.BlockSize]; - - DesCipher.DesFunc(this._encryptionKey1, inputBuffer, inputOffset, temp, 0); - DesCipher.DesFunc(this._encryptionKey2, temp, 0, temp, 0); - DesCipher.DesFunc(this._encryptionKey3, temp, 0, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - if ((inputOffset + this.BlockSize) > inputBuffer.Length) - throw new IndexOutOfRangeException("input buffer too short"); - - if ((outputOffset + this.BlockSize) > outputBuffer.Length) - throw new IndexOutOfRangeException("output buffer too short"); - - if (this._decryptionKey1 == null || this._decryptionKey2 == null || this._decryptionKey3 == null) - { - var part1 = new byte[8]; - var part2 = new byte[8]; - - Buffer.BlockCopy(this.Key, 0, part1, 0, 8); - Buffer.BlockCopy(this.Key, 8, part2, 0, 8); - - this._decryptionKey1 = this.GenerateWorkingKey(false, part1); - this._decryptionKey2 = this.GenerateWorkingKey(true, part2); - - if (this.Key.Length == 24) - { - var part3 = new byte[8]; - Buffer.BlockCopy(this.Key, 16, part3, 0, 8); - - this._decryptionKey3 = this.GenerateWorkingKey(false, part3); - } - else - { - this._decryptionKey3 = this._decryptionKey1; - } - } - - byte[] temp = new byte[this.BlockSize]; - - DesCipher.DesFunc(this._decryptionKey3, inputBuffer, inputOffset, temp, 0); - DesCipher.DesFunc(this._decryptionKey2, temp, 0, temp, 0); - DesCipher.DesFunc(this._decryptionKey1, temp, 0, outputBuffer, outputOffset); - - return this.BlockSize; - } - - /// - /// Validates the key. - /// - protected override void ValidateKey() - { - var keySize = this.Key.Length * 8; - - if (!(keySize == 128 || keySize == 128 + 64)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs b/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs deleted file mode 100644 index 04bfb9f..0000000 --- a/Renci.SshNet/Security/Cryptography/Ciphers/TwofishCipher.cs +++ /dev/null @@ -1,614 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography.Ciphers -{ - /// - /// Implements Twofish cipher algorithm - /// - public sealed class TwofishCipher : BlockCipher - { - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// The mode. - /// The padding. - /// is null. - /// Keysize is not valid for this algorithm. - public TwofishCipher(byte[] key, CipherMode mode, CipherPadding padding) - : base(key, 16, mode, padding) - { - var keySize = key.Length * 8; - - if (!(keySize == 128 || keySize == 192 || keySize == 256)) - throw new ArgumentException(string.Format("KeySize '{0}' is not valid for this algorithm.", keySize)); - - // TODO: Refactor this algorithm - - // calculate the MDS matrix - int[] m1 = new int[2]; - int[] mX = new int[2]; - int[] mY = new int[2]; - int j; - - for (int i = 0; i < MAX_KEY_BITS; i++) - { - j = P[0 + i] & 0xff; - m1[0] = j; - mX[0] = Mx_X(j) & 0xff; - mY[0] = Mx_Y(j) & 0xff; - - j = P[(1 * 256) + i] & 0xff; - m1[1] = j; - mX[1] = Mx_X(j) & 0xff; - mY[1] = Mx_Y(j) & 0xff; - - gMDS0[i] = m1[P_00] | mX[P_00] << 8 | mY[P_00] << 16 | mY[P_00] << 24; - - gMDS1[i] = mY[P_10] | mY[P_10] << 8 | mX[P_10] << 16 | m1[P_10] << 24; - - gMDS2[i] = mX[P_20] | mY[P_20] << 8 | m1[P_20] << 16 | mY[P_20] << 24; - - gMDS3[i] = mX[P_30] | m1[P_30] << 8 | mY[P_30] << 16 | mX[P_30] << 24; - } - - this.k64Cnt = key.Length / 8; // pre-padded ? - this.SetKey(key); - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - int x0 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[INPUT_WHITEN]; - int x1 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[INPUT_WHITEN + 1]; - int x2 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[INPUT_WHITEN + 2]; - int x3 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[INPUT_WHITEN + 3]; - - int k = ROUND_SUBKEYS; - int t0, t1; - for (int r = 0; r < ROUNDS; r += 2) - { - t0 = Fe32_0(gSBox, x0); - t1 = Fe32_3(gSBox, x1); - x2 ^= t0 + t1 + gSubKeys[k++]; - x2 = (int)((uint)x2 >> 1) | x2 << 31; - x3 = (x3 << 1 | (int)((uint)x3 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); - - t0 = Fe32_0(gSBox, x2); - t1 = Fe32_3(gSBox, x3); - x0 ^= t0 + t1 + gSubKeys[k++]; - x0 = (int)((uint)x0 >> 1) | x0 << 31; - x1 = (x1 << 1 | (int)((uint)x1 >> 31)) ^ (t0 + 2 * t1 + gSubKeys[k++]); - } - - Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], outputBuffer, outputOffset); - Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], outputBuffer, outputOffset + 4); - Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], outputBuffer, outputOffset + 8); - Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) - { - int x2 = BytesTo32Bits(inputBuffer, inputOffset) ^ gSubKeys[OUTPUT_WHITEN]; - int x3 = BytesTo32Bits(inputBuffer, inputOffset + 4) ^ gSubKeys[OUTPUT_WHITEN + 1]; - int x0 = BytesTo32Bits(inputBuffer, inputOffset + 8) ^ gSubKeys[OUTPUT_WHITEN + 2]; - int x1 = BytesTo32Bits(inputBuffer, inputOffset + 12) ^ gSubKeys[OUTPUT_WHITEN + 3]; - - int k = ROUND_SUBKEYS + 2 * ROUNDS - 1; - int t0, t1; - for (int r = 0; r < ROUNDS; r += 2) - { - t0 = Fe32_0(gSBox, x2); - t1 = Fe32_3(gSBox, x3); - x1 ^= t0 + 2 * t1 + gSubKeys[k--]; - x0 = (x0 << 1 | (int)((uint)x0 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); - x1 = (int)((uint)x1 >> 1) | x1 << 31; - - t0 = Fe32_0(gSBox, x0); - t1 = Fe32_3(gSBox, x1); - x3 ^= t0 + 2 * t1 + gSubKeys[k--]; - x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); - x3 = (int)((uint)x3 >> 1) | x3 << 31; - } - - Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], outputBuffer, outputOffset); - Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], outputBuffer, outputOffset + 4); - Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], outputBuffer, outputOffset + 8); - Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], outputBuffer, outputOffset + 12); - - return this.BlockSize; - } - - #region Static Definition Tables - - private static readonly byte[] P = { - //{ // p0 - (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8, - (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76, - (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78, - (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38, - (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98, - (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C, - (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26, - (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48, - (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30, - (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23, - (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59, - (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82, - (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E, - (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C, - (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE, - (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61, - (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5, - (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B, - (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B, - (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1, - (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45, - (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66, - (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56, - (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7, - (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5, - (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA, - (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF, - (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71, - (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD, - (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8, - (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D, - (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7, - (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED, - (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2, - (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11, - (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90, - (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF, - (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB, - (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B, - (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF, - (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE, - (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B, - (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46, - (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64, - (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F, - (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A, - (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A, - (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A, - (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29, - (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02, - (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17, - (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D, - (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74, - (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72, - (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12, - (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34, - (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68, - (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8, - (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40, - (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4, - (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0, - (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00, - (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42, - (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0, - // }, - //{ // p1 - (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4, - (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8, - (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B, - (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B, - (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD, - (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1, - (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B, - (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F, - (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B, - (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D, - (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E, - (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5, - (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14, - (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3, - (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54, - (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51, - (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A, - (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96, - (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10, - (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C, - (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7, - (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70, - (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB, - (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8, - (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF, - (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC, - (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF, - (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2, - (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82, - (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9, - (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97, - (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17, - (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D, - (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3, - (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C, - (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E, - (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F, - (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49, - (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21, - (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9, - (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD, - (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01, - (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F, - (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48, - (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E, - (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19, - (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57, - (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64, - (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE, - (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5, - (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44, - (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69, - (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15, - (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E, - (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34, - (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC, - (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B, - (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB, - (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52, - (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9, - (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4, - (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2, - (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56, - (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 - //} - }; - - #endregion - - /** - * Define the fixed p0/p1 permutations used in keyed S-box lookup. - * By changing the following constant definitions, the S-boxes will - * automatically Get changed in the Twofish engine. - */ - private const int P_00 = 1; - private const int P_01 = 0; - private const int P_02 = 0; - private const int P_03 = P_01 ^ 1; - private const int P_04 = 1; - - private const int P_10 = 0; - private const int P_11 = 0; - private const int P_12 = 1; - private const int P_13 = P_11 ^ 1; - private const int P_14 = 0; - - private const int P_20 = 1; - private const int P_21 = 1; - private const int P_22 = 0; - private const int P_23 = P_21 ^ 1; - private const int P_24 = 0; - - private const int P_30 = 0; - private const int P_31 = 1; - private const int P_32 = 1; - private const int P_33 = P_31 ^ 1; - private const int P_34 = 1; - - /* Primitive polynomial for GF(256) */ - private const int GF256_FDBK = 0x169; - private const int GF256_FDBK_2 = GF256_FDBK / 2; - private const int GF256_FDBK_4 = GF256_FDBK / 4; - - private const int RS_GF_FDBK = 0x14D; // field generator - - //==================================== - // Useful constants - //==================================== - - private const int ROUNDS = 16; - private const int MAX_ROUNDS = 16; // bytes = 128 bits - private const int MAX_KEY_BITS = 256; - - private const int INPUT_WHITEN = 0; - private const int OUTPUT_WHITEN = INPUT_WHITEN + 16 / 4; // 4 - private const int ROUND_SUBKEYS = OUTPUT_WHITEN + 16 / 4;// 8 - - private const int TOTAL_SUBKEYS = ROUND_SUBKEYS + 2 * MAX_ROUNDS;// 40 - - private const int SK_STEP = 0x02020202; - private const int SK_BUMP = 0x01010101; - private const int SK_ROTL = 9; - - private readonly int[] gMDS0 = new int[MAX_KEY_BITS]; - private readonly int[] gMDS1 = new int[MAX_KEY_BITS]; - private readonly int[] gMDS2 = new int[MAX_KEY_BITS]; - private readonly int[] gMDS3 = new int[MAX_KEY_BITS]; - - /** - * gSubKeys[] and gSBox[] are eventually used in the - * encryption and decryption methods. - */ - private int[] gSubKeys; - private int[] gSBox; - - private int k64Cnt; - - private void SetKey(byte[] key) - { - int[] k32e = new int[MAX_KEY_BITS / 64]; // 4 - int[] k32o = new int[MAX_KEY_BITS / 64]; // 4 - - int[] sBoxKeys = new int[MAX_KEY_BITS / 64]; // 4 - gSubKeys = new int[TOTAL_SUBKEYS]; - - if (k64Cnt < 1) - { - throw new ArgumentException("Key size less than 64 bits"); - } - - if (k64Cnt > 4) - { - throw new ArgumentException("Key size larger than 256 bits"); - } - - /* - * k64Cnt is the number of 8 byte blocks (64 chunks) - * that are in the input key. The input key is a - * maximum of 32 bytes ( 256 bits ), so the range - * for k64Cnt is 1..4 - */ - for (int i = 0; i < k64Cnt; i++) - { - var p = i * 8; - - k32e[i] = BytesTo32Bits(key, p); - k32o[i] = BytesTo32Bits(key, p + 4); - - sBoxKeys[k64Cnt - 1 - i] = RS_MDS_Encode(k32e[i], k32o[i]); - } - - int q, A, B; - for (int i = 0; i < TOTAL_SUBKEYS / 2; i++) - { - q = i * SK_STEP; - A = F32(q, k32e); - B = F32(q + SK_BUMP, k32o); - B = B << 8 | (int)((uint)B >> 24); - A += B; - gSubKeys[i * 2] = A; - A += B; - gSubKeys[i * 2 + 1] = A << SK_ROTL | (int)((uint)A >> (32 - SK_ROTL)); - } - - /* - * fully expand the table for speed - */ - int k0 = sBoxKeys[0]; - int k1 = sBoxKeys[1]; - int k2 = sBoxKeys[2]; - int k3 = sBoxKeys[3]; - int b0, b1, b2, b3; - gSBox = new int[4 * MAX_KEY_BITS]; - for (int i = 0; i < MAX_KEY_BITS; i++) - { - b0 = b1 = b2 = b3 = i; - switch (k64Cnt & 3) - { - case 1: - gSBox[i * 2] = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)]; - gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)]; - gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)]; - gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; - break; - case 0: /* 256 bits of key */ - b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); - b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3); - b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3); - b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3); - goto case 3; - case 3: - b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2); - b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2); - b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2); - b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2); - goto case 2; - case 2: - gSBox[i * 2] = gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)]; - gSBox[i * 2 + 1] = gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)]; - gSBox[i * 2 + 0x200] = gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)]; - gSBox[i * 2 + 0x201] = gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; - break; - } - } - - /* - * the function exits having setup the gSBox with the - * input key material. - */ - } - - /* - * TODO: This can be optimised and made cleaner by combining - * the functionality in this function and applying it appropriately - * to the creation of the subkeys during key setup. - */ - private int F32(int x, int[] k32) - { - int b0 = M_b0(x); - int b1 = M_b1(x); - int b2 = M_b2(x); - int b3 = M_b3(x); - int k0 = k32[0]; - int k1 = k32[1]; - int k2 = k32[2]; - int k3 = k32[3]; - - int result = 0; - switch (k64Cnt & 3) - { - case 1: - result = gMDS0[(P[P_01 * 256 + b0] & 0xff) ^ M_b0(k0)] ^ - gMDS1[(P[P_11 * 256 + b1] & 0xff) ^ M_b1(k0)] ^ - gMDS2[(P[P_21 * 256 + b2] & 0xff) ^ M_b2(k0)] ^ - gMDS3[(P[P_31 * 256 + b3] & 0xff) ^ M_b3(k0)]; - break; - case 0: /* 256 bits of key */ - b0 = (P[P_04 * 256 + b0] & 0xff) ^ M_b0(k3); - b1 = (P[P_14 * 256 + b1] & 0xff) ^ M_b1(k3); - b2 = (P[P_24 * 256 + b2] & 0xff) ^ M_b2(k3); - b3 = (P[P_34 * 256 + b3] & 0xff) ^ M_b3(k3); - goto case 3; - case 3: - b0 = (P[P_03 * 256 + b0] & 0xff) ^ M_b0(k2); - b1 = (P[P_13 * 256 + b1] & 0xff) ^ M_b1(k2); - b2 = (P[P_23 * 256 + b2] & 0xff) ^ M_b2(k2); - b3 = (P[P_33 * 256 + b3] & 0xff) ^ M_b3(k2); - goto case 2; - case 2: - result = - gMDS0[(P[P_01 * 256 + (P[P_02 * 256 + b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)] ^ - gMDS1[(P[P_11 * 256 + (P[P_12 * 256 + b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)] ^ - gMDS2[(P[P_21 * 256 + (P[P_22 * 256 + b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)] ^ - gMDS3[(P[P_31 * 256 + (P[P_32 * 256 + b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_b3(k0)]; - break; - } - return result; - } - - /** - * Use (12, 8) Reed-Solomon code over GF(256) to produce - * a key S-box 32-bit entity from 2 key material 32-bit - * entities. - * - * @param k0 first 32-bit entity - * @param k1 second 32-bit entity - * @return Remainder polynomial Generated using RS code - */ - private static int RS_MDS_Encode(int k0, int k1) - { - int r = k1; - // shift 1 byte at a time - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - r ^= k0; - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - r = RS_rem(r); - - return r; - } - - /** - * Reed-Solomon code parameters: (12,8) reversible code: - *

- *

-        * G(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
-        * 
- * where a = primitive root of field generator 0x14D - *

- */ - private static int RS_rem(int x) - { - int b = (int)(((uint)x >> 24) & 0xff); - int g2 = ((b << 1) ^ - ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; - int g3 = ((int)((uint)b >> 1) ^ - ((b & 0x01) != 0 ? (int)((uint)RS_GF_FDBK >> 1) : 0)) ^ g2; - return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b); - } - - private static int LFSR1(int x) - { - return (x >> 1) ^ - (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); - } - - private static int LFSR2(int x) - { - return (x >> 2) ^ - (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ - (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); - } - - private static int Mx_X(int x) - { - return x ^ LFSR2(x); - } // 5B - - private static int Mx_Y(int x) - { - return x ^ LFSR1(x) ^ LFSR2(x); - } // EF - - private static int M_b0(int x) - { - return x & 0xff; - } - - private static int M_b1(int x) - { - return (int)((uint)x >> 8) & 0xff; - } - - private static int M_b2(int x) - { - return (int)((uint)x >> 16) & 0xff; - } - - private static int M_b3(int x) - { - return (int)((uint)x >> 24) & 0xff; - } - - private static int Fe32_0(int[] gSBox1, int x) - { - return gSBox1[0x000 + 2 * (x & 0xff)] ^ - gSBox1[0x001 + 2 * ((int)((uint)x >> 8) & 0xff)] ^ - gSBox1[0x200 + 2 * ((int)((uint)x >> 16) & 0xff)] ^ - gSBox1[0x201 + 2 * ((int)((uint)x >> 24) & 0xff)]; - } - - private static int Fe32_3(int[] gSBox1, int x) - { - return gSBox1[0x000 + 2 * ((int)((uint)x >> 24) & 0xff)] ^ - gSBox1[0x001 + 2 * (x & 0xff)] ^ - gSBox1[0x200 + 2 * ((int)((uint)x >> 8) & 0xff)] ^ - gSBox1[0x201 + 2 * ((int)((uint)x >> 16) & 0xff)]; - } - - private static int BytesTo32Bits(byte[] b, int p) - { - return ((b[p] & 0xff)) | - ((b[p + 1] & 0xff) << 8) | - ((b[p + 2] & 0xff) << 16) | - ((b[p + 3] & 0xff) << 24); - } - - private static void Bits32ToBytes(int inData, byte[] b, int offset) - { - b[offset] = (byte)inData; - b[offset + 1] = (byte)(inData >> 8); - b[offset + 2] = (byte)(inData >> 16); - b[offset + 3] = (byte)(inData >> 24); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/DigitalSignature.cs b/Renci.SshNet/Security/Cryptography/DigitalSignature.cs deleted file mode 100644 index e0a4e40..0000000 --- a/Renci.SshNet/Security/Cryptography/DigitalSignature.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for signature implementations - /// - public abstract class DigitalSignature - { - /// - /// Verifies the signature. - /// - /// The input. - /// The signature. - /// True if signature was successfully verified; otherwise false. - public abstract bool Verify(byte[] input, byte[] signature); - - /// - /// Creates the signature. - /// - /// The input. - /// Signed input data. - public abstract byte[] Sign(byte[] input); - } -} diff --git a/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs b/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs deleted file mode 100644 index a9b47fc..0000000 --- a/Renci.SshNet/Security/Cryptography/DsaDigitalSignature.cs +++ /dev/null @@ -1,206 +0,0 @@ -using System; -using System.Linq; -using System.Security.Cryptography; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Implements DSA digital signature algorithm. - /// - public class DsaDigitalSignature : DigitalSignature, IDisposable - { - private HashAlgorithm _hash; - - private readonly DsaKey _key; - - /// - /// Initializes a new instance of the class. - /// - /// The DSA key. - /// key - public DsaDigitalSignature(DsaKey key) - { - if (key == null) - throw new ArgumentNullException("key"); - - this._key = key; - - this._hash = new SHA1Hash(); - } - - /// - /// Verifies the signature. - /// - /// The input. - /// The signature. - /// - /// True if signature was successfully verified; otherwise false. - /// - /// Invalid signature. - public override bool Verify(byte[] input, byte[] signature) - { - var hashInput = this._hash.ComputeHash(input); - - BigInteger hm = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray()); - - if (signature.Length != 40) - throw new InvalidOperationException("Invalid signature."); - - // Extract r and s numbers from the signature - var rBytes = new byte[21]; - var sBytes = new byte[21]; - - for (int i = 0, j = 20; i < 20; i++, j--) - { - rBytes[i] = signature[j - 1]; - sBytes[i] = signature[j + 20 - 1]; - } - - BigInteger r = new BigInteger(rBytes); - BigInteger s = new BigInteger(sBytes); - - // Reject the signature if 0 < r < q or 0 < s < q is not satisfied. - if (r <= 0 || r >= this._key.Q) - return false; - - if (s <= 0 || s >= this._key.Q) - return false; - - // Calculate w = s−1 mod q - BigInteger w = BigInteger.ModInverse(s, this._key.Q); - - // Calculate u1 = H(m)·w mod q - BigInteger u1 = hm * w % this._key.Q; - - // Calculate u2 = r * w mod q - BigInteger u2 = r * w % this._key.Q; - - u1 = BigInteger.ModPow(this._key.G, u1, this._key.P); - u2 = BigInteger.ModPow(this._key.Y, u2, this._key.P); - - // Calculate v = ((g pow u1 * y pow u2) mod p) mod q - BigInteger v = ((u1 * u2) % this._key.P) % this._key.Q; - - // The signature is valid if v = r - return v == r; - } - - /// - /// Creates the signature. - /// - /// The input. - /// - /// Signed input data. - /// - /// Invalid DSA key. - public override byte[] Sign(byte[] input) - { - var hashInput = this._hash.ComputeHash(input); - - BigInteger m = new BigInteger(hashInput.Reverse().Concat(new byte[] { 0 }).ToArray()); - - BigInteger s; - BigInteger r; - - do - { - BigInteger k = BigInteger.Zero; - - do - { - // Generate a random per-message value k where 0 < k < q - var bitLength = this._key.Q.BitLength; - - if (this._key.Q < BigInteger.Zero) - throw new SshException("Invalid DSA key."); - - while (k <= 0 || k >= this._key.Q) - { - k = BigInteger.Random(bitLength); - } - - // Calculate r = ((g pow k) mod p) mod q - r = BigInteger.ModPow(this._key.G, k, this._key.P) % this._key.Q; - - // In the unlikely case that r = 0, start again with a different random k - } while (r.IsZero); - - - // Calculate s = ((k pow −1)(H(m) + x*r)) mod q - k = (BigInteger.ModInverse(k, this._key.Q) * (m + this._key.X * r)); - - s = k % this._key.Q; - - // In the unlikely case that s = 0, start again with a different random k - } while (s.IsZero); - - // The signature is (r, s) - var signature = new byte[40]; - - // issue #1918: pad part with zero's on the left if length is less than 20 - var rBytes = r.ToByteArray().Reverse().TrimLeadingZero().ToArray(); - Array.Copy(rBytes, 0, signature, 20 - rBytes.Length, rBytes.Length); - - // issue #1918: pad part with zero's on the left if length is less than 20 - var sBytes = s.ToByteArray().Reverse().TrimLeadingZero().ToArray(); - Array.Copy(sBytes, 0, signature, 40 - sBytes.Length, sBytes.Length); - - return signature; - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._hash != null) - { - this._hash.Clear(); - this._hash = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~DsaDigitalSignature() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/Security/Cryptography/DsaKey.cs b/Renci.SshNet/Security/Cryptography/DsaKey.cs deleted file mode 100644 index b8565ae..0000000 --- a/Renci.SshNet/Security/Cryptography/DsaKey.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Contains DSA private and public key - /// - public class DsaKey : Key, IDisposable - { - /// - /// Gets the P. - /// - public BigInteger P - { - get - { - return this._privateKey[0]; - } - } - - /// - /// Gets the Q. - /// - public BigInteger Q - { - get - { - return this._privateKey[1]; - } - } - - /// - /// Gets the G. - /// - public BigInteger G - { - get - { - return this._privateKey[2]; - } - } - - /// - /// Gets public key Y. - /// - public BigInteger Y - { - get - { - return this._privateKey[3]; - } - } - - /// - /// Gets private key X. - /// - public BigInteger X - { - get - { - return this._privateKey[4]; - } - } - - /// - /// Gets the length of the key. - /// - /// - /// The length of the key. - /// - public override int KeyLength - { - get - { - return this.P.BitLength; - } - } - - private DsaDigitalSignature _digitalSignature; - /// - /// Gets the digital signature. - /// - protected override DigitalSignature DigitalSignature - { - get - { - if (this._digitalSignature == null) - { - this._digitalSignature = new DsaDigitalSignature(this); - } - return this._digitalSignature; - } - } - - /// - /// Gets or sets the public. - /// - /// - /// The public. - /// - public override BigInteger[] Public - { - get - { - return new BigInteger[] { this.P, this.Q, this.G, this.Y }; - } - set - { - if (value.Length != 4) - throw new InvalidOperationException("Invalid public key."); - - this._privateKey = value; - } - } - - /// - /// Initializes a new instance of the class. - /// - public DsaKey() - { - this._privateKey = new BigInteger[5]; - } - - /// - /// Initializes a new instance of the class. - /// - /// DER encoded private key data. - public DsaKey(byte[] data) - : base(data) - { - if (this._privateKey.Length != 5) - throw new InvalidOperationException("Invalid private key."); - } - - /// - /// Initializes a new instance of the class. - /// - /// The p. - /// The q. - /// The g. - /// The y. - /// The x. - public DsaKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y, BigInteger x) - { - this._privateKey = new BigInteger[5]; - this._privateKey[0] = p; - this._privateKey[1] = q; - this._privateKey[2] = g; - this._privateKey[3] = y; - this._privateKey[4] = x; - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._digitalSignature != null) - { - this._digitalSignature.Dispose(); - this._digitalSignature = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~DsaKey() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/Security/Cryptography/HMAC.cs b/Renci.SshNet/Security/Cryptography/HMAC.cs deleted file mode 100644 index e5941f6..0000000 --- a/Renci.SshNet/Security/Cryptography/HMAC.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System.Linq; -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Provides HMAC algorithm implementation. - /// - /// Class that implements . - public class HMac : KeyedHashAlgorithm where T : HashAlgorithm, new() - { - private HashAlgorithm _hash; - //private bool _isHashing; - private byte[] _innerPadding; - private byte[] _outerPadding; - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - protected int BlockSize - { - get - { - return this._hash.InputBlockSize; - } - } - - private HMac() - { - // Create the hash algorithms. - this._hash = new T(); - this.HashSizeValue = this._hash.HashSize; - } - - /// - /// Rfc 2104. - /// - /// The key. - /// The size, in bits, of the computed hash code. - public HMac(byte[] key, int hashSizeValue) - : this(key) - { - this.HashSizeValue = hashSizeValue; - } - - /// - /// Rfc 2104. - /// - /// The key. - public HMac(byte[] key) - : this() - { - base.KeyValue = key; - - this.InternalInitialize(); - } - - - /// - /// Gets or sets the key to use in the hash algorithm. - /// - /// The key to use in the hash algorithm. - public override byte[] Key - { - get - { - return (byte[])base.KeyValue.Clone(); - } - set - { - this.SetKey(value); - } - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - /// - /// Hashes the core. - /// - /// The RGB. - /// The ib. - /// The cb. - protected override void HashCore(byte[] rgb, int ib, int cb) - { - this._hash.TransformBlock(rgb, ib, cb, rgb, ib); - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - // Finalize the original hash. - this._hash.TransformFinalBlock(new byte[0], 0, 0); - - var hashValue = this._hash.Hash; - - // Write the outer array. - this._hash.TransformBlock(this._outerPadding, 0, this.BlockSize, this._outerPadding, 0); - - // Write the inner hash and finalize the hash. - this._hash.TransformFinalBlock(hashValue, 0, hashValue.Length); - - return this._hash.Hash.Take(this.HashSize / 8).ToArray(); - } - - private void InternalInitialize() - { - this.SetKey(base.KeyValue); - } - - private void SetKey(byte[] value) - { - this._hash.Initialize(); - - if (value.Length > this.BlockSize) - { - this.KeyValue = this._hash.ComputeHash(value); - // No need to call Initialize, ComputeHash does it automatically. - } - else - { - this.KeyValue = (byte[]) value.Clone(); - } - - this._innerPadding = new byte[this.BlockSize]; - this._outerPadding = new byte[this.BlockSize]; - - // Compute inner and outer padding. - for (var i = 0; i < this.KeyValue.Length; i++) - { - this._innerPadding[i] = (byte)(0x36 ^ this.KeyValue[i]); - this._outerPadding[i] = (byte)(0x5C ^ this.KeyValue[i]); - } - for (var i = this.KeyValue.Length; i < this.BlockSize; i++) - { - this._innerPadding[i] = 0x36; - this._outerPadding[i] = 0x5C; - } - - this._hash.TransformBlock(this._innerPadding, 0, this.BlockSize, this._innerPadding, 0); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._hash != null) - { - this._hash.Clear(); - this._hash = null; - } - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/MD5Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/MD5Hash.cs deleted file mode 100644 index 95dfb12..0000000 --- a/Renci.SshNet/Security/Cryptography/Hashes/MD5Hash.cs +++ /dev/null @@ -1,385 +0,0 @@ -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// MD5 algorithm implementation - /// - public sealed class MD5Hash : HashAlgorithm - { - private readonly byte[] _buffer = new byte[4]; - private int _bufferOffset; - private long _byteCount; - private int H1, H2, H3, H4; // IV's - private readonly int[] _hashValue = new int[16]; - private int _offset; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return 128; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public MD5Hash() - { - this.InternalInitialize(); - } - - /// - /// Routes data written to the object into the hash algorithm for computing the hash. - /// - /// The input to compute the hash code for. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - long bitLength = (this._byteCount << 3); - - // Add the pad bytes. - this.Update((byte)128); - - while (this._bufferOffset != 0) - this.Update((byte)0); - - if (this._offset > 14) - { - this.ProcessBlock(); - } - - this._hashValue[14] = (int)(bitLength & 0xffffffff); - this._hashValue[15] = (int)((ulong)bitLength >> 32); - - this.ProcessBlock(); - - var output = new byte[16]; - - this.UnpackWord(H1, output, 0); - this.UnpackWord(H2, output, 0 + 4); - this.UnpackWord(H3, output, 0 + 8); - this.UnpackWord(H4, output, 0 + 12); - - this.Initialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - private void InternalInitialize() - { - this._byteCount = 0; - this._bufferOffset = 0; - for (var i = 0; i < 4; i++) - { - this._buffer[i] = 0; - } - - H1 = unchecked((int)0x67452301); - H2 = unchecked((int)0xefcdab89); - H3 = unchecked((int)0x98badcfe); - H4 = unchecked((int)0x10325476); - - this._offset = 0; - for (var i = 0; i != this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - private void ProcessWord(byte[] input, int inOff) - { - this._hashValue[this._offset++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) - | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); - - if (this._offset == 16) - { - ProcessBlock(); - } - } - - private void UnpackWord(int word, byte[] outBytes, int outOff) - { - outBytes[outOff] = (byte)word; - outBytes[outOff + 1] = (byte)((uint)word >> 8); - outBytes[outOff + 2] = (byte)((uint)word >> 16); - outBytes[outOff + 3] = (byte)((uint)word >> 24); - } - - // - // round 1 left rotates - // - private const int S11 = 7; - private const int S12 = 12; - private const int S13 = 17; - private const int S14 = 22; - - // - // round 2 left rotates - // - private const int S21 = 5; - private const int S22 = 9; - private const int S23 = 14; - private const int S24 = 20; - - // - // round 3 left rotates - // - private const int S31 = 4; - private const int S32 = 11; - private const int S33 = 16; - private const int S34 = 23; - - // - // round 4 left rotates - // - private const int S41 = 6; - private const int S42 = 10; - private const int S43 = 15; - private const int S44 = 21; - - /* - * rotate int x left n bits. - */ - private static int RotateLeft(int x, int n) - { - return (x << n) | (int)((uint)x >> (32 - n)); - } - - /* - * F, G, H and I are the basic MD5 functions. - */ - private static int F(int u, int v, int w) - { - return (u & v) | (~u & w); - } - - private static int G(int u, int v, int w) - { - return (u & w) | (v & ~w); - } - - private static int H(int u, int v, int w) - { - return u ^ v ^ w; - } - - private static int K(int u, int v, int w) - { - return v ^ (u | ~w); - } - - private void ProcessBlock() - { - int a = H1; - int b = H2; - int c = H3; - int d = H4; - - // - // Round 1 - F cycle, 16 times. - // - a = RotateLeft((a + F(b, c, d) + this._hashValue[0] + unchecked((int)0xd76aa478)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[1] + unchecked((int)0xe8c7b756)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[2] + unchecked((int)0x242070db)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[3] + unchecked((int)0xc1bdceee)), S14) + c; - a = RotateLeft((a + F(b, c, d) + this._hashValue[4] + unchecked((int)0xf57c0faf)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[5] + unchecked((int)0x4787c62a)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[6] + unchecked((int)0xa8304613)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[7] + unchecked((int)0xfd469501)), S14) + c; - a = RotateLeft((a + F(b, c, d) + this._hashValue[8] + unchecked((int)0x698098d8)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[9] + unchecked((int)0x8b44f7af)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[10] + unchecked((int)0xffff5bb1)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[11] + unchecked((int)0x895cd7be)), S14) + c; - a = RotateLeft((a + F(b, c, d) + this._hashValue[12] + unchecked((int)0x6b901122)), S11) + b; - d = RotateLeft((d + F(a, b, c) + this._hashValue[13] + unchecked((int)0xfd987193)), S12) + a; - c = RotateLeft((c + F(d, a, b) + this._hashValue[14] + unchecked((int)0xa679438e)), S13) + d; - b = RotateLeft((b + F(c, d, a) + this._hashValue[15] + unchecked((int)0x49b40821)), S14) + c; - - // - // Round 2 - G cycle, 16 times. - // - a = RotateLeft((a + G(b, c, d) + this._hashValue[1] + unchecked((int)0xf61e2562)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[6] + unchecked((int)0xc040b340)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[11] + unchecked((int)0x265e5a51)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[0] + unchecked((int)0xe9b6c7aa)), S24) + c; - a = RotateLeft((a + G(b, c, d) + this._hashValue[5] + unchecked((int)0xd62f105d)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[10] + unchecked((int)0x02441453)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[15] + unchecked((int)0xd8a1e681)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[4] + unchecked((int)0xe7d3fbc8)), S24) + c; - a = RotateLeft((a + G(b, c, d) + this._hashValue[9] + unchecked((int)0x21e1cde6)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[14] + unchecked((int)0xc33707d6)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[3] + unchecked((int)0xf4d50d87)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[8] + unchecked((int)0x455a14ed)), S24) + c; - a = RotateLeft((a + G(b, c, d) + this._hashValue[13] + unchecked((int)0xa9e3e905)), S21) + b; - d = RotateLeft((d + G(a, b, c) + this._hashValue[2] + unchecked((int)0xfcefa3f8)), S22) + a; - c = RotateLeft((c + G(d, a, b) + this._hashValue[7] + unchecked((int)0x676f02d9)), S23) + d; - b = RotateLeft((b + G(c, d, a) + this._hashValue[12] + unchecked((int)0x8d2a4c8a)), S24) + c; - - // - // Round 3 - H cycle, 16 times. - // - a = RotateLeft((a + H(b, c, d) + this._hashValue[5] + unchecked((int)0xfffa3942)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[8] + unchecked((int)0x8771f681)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[11] + unchecked((int)0x6d9d6122)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[14] + unchecked((int)0xfde5380c)), S34) + c; - a = RotateLeft((a + H(b, c, d) + this._hashValue[1] + unchecked((int)0xa4beea44)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[4] + unchecked((int)0x4bdecfa9)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[7] + unchecked((int)0xf6bb4b60)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[10] + unchecked((int)0xbebfbc70)), S34) + c; - a = RotateLeft((a + H(b, c, d) + this._hashValue[13] + unchecked((int)0x289b7ec6)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[0] + unchecked((int)0xeaa127fa)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[3] + unchecked((int)0xd4ef3085)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[6] + unchecked((int)0x04881d05)), S34) + c; - a = RotateLeft((a + H(b, c, d) + this._hashValue[9] + unchecked((int)0xd9d4d039)), S31) + b; - d = RotateLeft((d + H(a, b, c) + this._hashValue[12] + unchecked((int)0xe6db99e5)), S32) + a; - c = RotateLeft((c + H(d, a, b) + this._hashValue[15] + unchecked((int)0x1fa27cf8)), S33) + d; - b = RotateLeft((b + H(c, d, a) + this._hashValue[2] + unchecked((int)0xc4ac5665)), S34) + c; - - // - // Round 4 - K cycle, 16 times. - // - a = RotateLeft((a + K(b, c, d) + this._hashValue[0] + unchecked((int)0xf4292244)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[7] + unchecked((int)0x432aff97)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[14] + unchecked((int)0xab9423a7)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[5] + unchecked((int)0xfc93a039)), S44) + c; - a = RotateLeft((a + K(b, c, d) + this._hashValue[12] + unchecked((int)0x655b59c3)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[3] + unchecked((int)0x8f0ccc92)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[10] + unchecked((int)0xffeff47d)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[1] + unchecked((int)0x85845dd1)), S44) + c; - a = RotateLeft((a + K(b, c, d) + this._hashValue[8] + unchecked((int)0x6fa87e4f)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[15] + unchecked((int)0xfe2ce6e0)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[6] + unchecked((int)0xa3014314)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[13] + unchecked((int)0x4e0811a1)), S44) + c; - a = RotateLeft((a + K(b, c, d) + this._hashValue[4] + unchecked((int)0xf7537e82)), S41) + b; - d = RotateLeft((d + K(a, b, c) + this._hashValue[11] + unchecked((int)0xbd3af235)), S42) + a; - c = RotateLeft((c + K(d, a, b) + this._hashValue[2] + unchecked((int)0x2ad7d2bb)), S43) + d; - b = RotateLeft((b + K(c, d, a) + this._hashValue[9] + unchecked((int)0xeb86d391)), S44) + c; - - H1 += a; - H2 += b; - H3 += c; - H4 += d; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i != this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs deleted file mode 100644 index def23f7..0000000 --- a/Renci.SshNet/Security/Cryptography/Hashes/RIPEMD160Hash.cs +++ /dev/null @@ -1,518 +0,0 @@ -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// - /// - public sealed class RIPEMD160Hash : HashAlgorithm - { - private const int DIGEST_SIZE = 20; - - private readonly byte[] _buffer; - private int _bufferOffset; - private long _byteCount; - private int _offset; - private int H0, H1, H2, H3, H4; // IV's - private readonly int[] X = new int[16]; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // - // fill the current word - // - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // - // process whole words. - // - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // - // load in the remainder. - // - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - long bitLength = (this._byteCount << 3); - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - Update((byte)0); - ProcessLength(bitLength); - ProcessBlock(); - - UnpackWord(H0, output, 0); - UnpackWord(H1, output, 4); - UnpackWord(H2, output, 8); - UnpackWord(H3, output, 12); - UnpackWord(H4, output, 16); - - this.InternalInitialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - /// - /// Initializes a new instance of the class. - /// - public RIPEMD160Hash() - { - this._buffer = new byte[4]; - this.InternalInitialize(); - } - - private void ProcessWord(byte[] input, int inOff) - { - this.X[this._offset++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) - | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); - - if (this._offset == 16) - { - ProcessBlock(); - } - } - - private void ProcessLength(long bitLength) - { - if (this._offset > 14) - { - ProcessBlock(); - } - - this.X[14] = (int)(bitLength & 0xffffffff); - this.X[15] = (int)((ulong)bitLength >> 32); - } - - private void UnpackWord(int word, byte[] outBytes, int outOff) - { - outBytes[outOff] = (byte)word; - outBytes[outOff + 1] = (byte)((uint)word >> 8); - outBytes[outOff + 2] = (byte)((uint)word >> 16); - outBytes[outOff + 3] = (byte)((uint)word >> 24); - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - /// - /// Reset the chaining variables to the IV values. - /// - private void InternalInitialize() - { - this._byteCount = 0; - this._bufferOffset = 0; - for (int i = 0; i < _buffer.Length; i++) - { - this._buffer[i] = 0; - } - - H0 = unchecked((int)0x67452301); - H1 = unchecked((int)0xefcdab89); - H2 = unchecked((int)0x98badcfe); - H3 = unchecked((int)0x10325476); - H4 = unchecked((int)0xc3d2e1f0); - - this._offset = 0; - - for (int i = 0; i != X.Length; i++) - { - this.X[i] = 0; - } - } - - private int RL(int x, int n) - { - return (x << n) | (int)((uint)x >> (32 - n)); - } - - /// - /// Rounds 0-15 - /// - /// The x. - /// The y. - /// The z. - /// - private int F1(int x, int y, int z) - { - return x ^ y ^ z; - } - - /// - /// Rounds 16-31 - /// - /// The x. - /// The y. - /// The z. - /// - private int F2(int x, int y, int z) - { - return (x & y) | (~x & z); - } - - /// - /// ounds 32-47 - /// - /// The x. - /// The y. - /// The z. - /// - private int F3(int x, int y, int z) - { - return (x | ~y) ^ z; - } - - /// - /// Rounds 48-63 - /// - /// The x. - /// The y. - /// The z. - /// - private int F4(int x, int y, int z) - { - return (x & z) | (y & ~z); - } - - /// - /// ounds 64-79 - /// - /// The x. - /// The y. - /// The z. - /// - private int F5(int x, int y, int z) - { - return x ^ (y | ~z); - } - - private void ProcessBlock() - { - int a, aa; - int b, bb; - int c, cc; - int d, dd; - int e, ee; - - a = aa = H0; - b = bb = H1; - c = cc = H2; - d = dd = H3; - e = ee = H4; - - // - // Rounds 1 - 16 - // - // left - a = RL(a + F1(b, c, d) + this.X[0], 11) + e; c = RL(c, 10); - e = RL(e + F1(a, b, c) + this.X[1], 14) + d; b = RL(b, 10); - d = RL(d + F1(e, a, b) + this.X[2], 15) + c; a = RL(a, 10); - c = RL(c + F1(d, e, a) + this.X[3], 12) + b; e = RL(e, 10); - b = RL(b + F1(c, d, e) + this.X[4], 5) + a; d = RL(d, 10); - a = RL(a + F1(b, c, d) + this.X[5], 8) + e; c = RL(c, 10); - e = RL(e + F1(a, b, c) + this.X[6], 7) + d; b = RL(b, 10); - d = RL(d + F1(e, a, b) + this.X[7], 9) + c; a = RL(a, 10); - c = RL(c + F1(d, e, a) + this.X[8], 11) + b; e = RL(e, 10); - b = RL(b + F1(c, d, e) + this.X[9], 13) + a; d = RL(d, 10); - a = RL(a + F1(b, c, d) + this.X[10], 14) + e; c = RL(c, 10); - e = RL(e + F1(a, b, c) + this.X[11], 15) + d; b = RL(b, 10); - d = RL(d + F1(e, a, b) + this.X[12], 6) + c; a = RL(a, 10); - c = RL(c + F1(d, e, a) + this.X[13], 7) + b; e = RL(e, 10); - b = RL(b + F1(c, d, e) + this.X[14], 9) + a; d = RL(d, 10); - a = RL(a + F1(b, c, d) + this.X[15], 8) + e; c = RL(c, 10); - - // right - aa = RL(aa + F5(bb, cc, dd) + this.X[5] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); - ee = RL(ee + F5(aa, bb, cc) + this.X[14] + unchecked((int)0x50a28be6), 9) + dd; bb = RL(bb, 10); - dd = RL(dd + F5(ee, aa, bb) + this.X[7] + unchecked((int)0x50a28be6), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F5(dd, ee, aa) + this.X[0] + unchecked((int)0x50a28be6), 11) + bb; ee = RL(ee, 10); - bb = RL(bb + F5(cc, dd, ee) + this.X[9] + unchecked((int)0x50a28be6), 13) + aa; dd = RL(dd, 10); - aa = RL(aa + F5(bb, cc, dd) + this.X[2] + unchecked((int)0x50a28be6), 15) + ee; cc = RL(cc, 10); - ee = RL(ee + F5(aa, bb, cc) + this.X[11] + unchecked((int)0x50a28be6), 15) + dd; bb = RL(bb, 10); - dd = RL(dd + F5(ee, aa, bb) + this.X[4] + unchecked((int)0x50a28be6), 5) + cc; aa = RL(aa, 10); - cc = RL(cc + F5(dd, ee, aa) + this.X[13] + unchecked((int)0x50a28be6), 7) + bb; ee = RL(ee, 10); - bb = RL(bb + F5(cc, dd, ee) + this.X[6] + unchecked((int)0x50a28be6), 7) + aa; dd = RL(dd, 10); - aa = RL(aa + F5(bb, cc, dd) + this.X[15] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); - ee = RL(ee + F5(aa, bb, cc) + this.X[8] + unchecked((int)0x50a28be6), 11) + dd; bb = RL(bb, 10); - dd = RL(dd + F5(ee, aa, bb) + this.X[1] + unchecked((int)0x50a28be6), 14) + cc; aa = RL(aa, 10); - cc = RL(cc + F5(dd, ee, aa) + this.X[10] + unchecked((int)0x50a28be6), 14) + bb; ee = RL(ee, 10); - bb = RL(bb + F5(cc, dd, ee) + this.X[3] + unchecked((int)0x50a28be6), 12) + aa; dd = RL(dd, 10); - aa = RL(aa + F5(bb, cc, dd) + this.X[12] + unchecked((int)0x50a28be6), 6) + ee; cc = RL(cc, 10); - - // - // Rounds 16-31 - // - // left - e = RL(e + F2(a, b, c) + this.X[7] + unchecked((int)0x5a827999), 7) + d; b = RL(b, 10); - d = RL(d + F2(e, a, b) + this.X[4] + unchecked((int)0x5a827999), 6) + c; a = RL(a, 10); - c = RL(c + F2(d, e, a) + this.X[13] + unchecked((int)0x5a827999), 8) + b; e = RL(e, 10); - b = RL(b + F2(c, d, e) + this.X[1] + unchecked((int)0x5a827999), 13) + a; d = RL(d, 10); - a = RL(a + F2(b, c, d) + this.X[10] + unchecked((int)0x5a827999), 11) + e; c = RL(c, 10); - e = RL(e + F2(a, b, c) + this.X[6] + unchecked((int)0x5a827999), 9) + d; b = RL(b, 10); - d = RL(d + F2(e, a, b) + this.X[15] + unchecked((int)0x5a827999), 7) + c; a = RL(a, 10); - c = RL(c + F2(d, e, a) + this.X[3] + unchecked((int)0x5a827999), 15) + b; e = RL(e, 10); - b = RL(b + F2(c, d, e) + this.X[12] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); - a = RL(a + F2(b, c, d) + this.X[0] + unchecked((int)0x5a827999), 12) + e; c = RL(c, 10); - e = RL(e + F2(a, b, c) + this.X[9] + unchecked((int)0x5a827999), 15) + d; b = RL(b, 10); - d = RL(d + F2(e, a, b) + this.X[5] + unchecked((int)0x5a827999), 9) + c; a = RL(a, 10); - c = RL(c + F2(d, e, a) + this.X[2] + unchecked((int)0x5a827999), 11) + b; e = RL(e, 10); - b = RL(b + F2(c, d, e) + this.X[14] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); - a = RL(a + F2(b, c, d) + this.X[11] + unchecked((int)0x5a827999), 13) + e; c = RL(c, 10); - e = RL(e + F2(a, b, c) + this.X[8] + unchecked((int)0x5a827999), 12) + d; b = RL(b, 10); - - // right - ee = RL(ee + F4(aa, bb, cc) + this.X[6] + unchecked((int)0x5c4dd124), 9) + dd; bb = RL(bb, 10); - dd = RL(dd + F4(ee, aa, bb) + this.X[11] + unchecked((int)0x5c4dd124), 13) + cc; aa = RL(aa, 10); - cc = RL(cc + F4(dd, ee, aa) + this.X[3] + unchecked((int)0x5c4dd124), 15) + bb; ee = RL(ee, 10); - bb = RL(bb + F4(cc, dd, ee) + this.X[7] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); - aa = RL(aa + F4(bb, cc, dd) + this.X[0] + unchecked((int)0x5c4dd124), 12) + ee; cc = RL(cc, 10); - ee = RL(ee + F4(aa, bb, cc) + this.X[13] + unchecked((int)0x5c4dd124), 8) + dd; bb = RL(bb, 10); - dd = RL(dd + F4(ee, aa, bb) + this.X[5] + unchecked((int)0x5c4dd124), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F4(dd, ee, aa) + this.X[10] + unchecked((int)0x5c4dd124), 11) + bb; ee = RL(ee, 10); - bb = RL(bb + F4(cc, dd, ee) + this.X[14] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); - aa = RL(aa + F4(bb, cc, dd) + this.X[15] + unchecked((int)0x5c4dd124), 7) + ee; cc = RL(cc, 10); - ee = RL(ee + F4(aa, bb, cc) + this.X[8] + unchecked((int)0x5c4dd124), 12) + dd; bb = RL(bb, 10); - dd = RL(dd + F4(ee, aa, bb) + this.X[12] + unchecked((int)0x5c4dd124), 7) + cc; aa = RL(aa, 10); - cc = RL(cc + F4(dd, ee, aa) + this.X[4] + unchecked((int)0x5c4dd124), 6) + bb; ee = RL(ee, 10); - bb = RL(bb + F4(cc, dd, ee) + this.X[9] + unchecked((int)0x5c4dd124), 15) + aa; dd = RL(dd, 10); - aa = RL(aa + F4(bb, cc, dd) + this.X[1] + unchecked((int)0x5c4dd124), 13) + ee; cc = RL(cc, 10); - ee = RL(ee + F4(aa, bb, cc) + this.X[2] + unchecked((int)0x5c4dd124), 11) + dd; bb = RL(bb, 10); - - // - // Rounds 32-47 - // - // left - d = RL(d + F3(e, a, b) + this.X[3] + unchecked((int)0x6ed9eba1), 11) + c; a = RL(a, 10); - c = RL(c + F3(d, e, a) + this.X[10] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); - b = RL(b + F3(c, d, e) + this.X[14] + unchecked((int)0x6ed9eba1), 6) + a; d = RL(d, 10); - a = RL(a + F3(b, c, d) + this.X[4] + unchecked((int)0x6ed9eba1), 7) + e; c = RL(c, 10); - e = RL(e + F3(a, b, c) + this.X[9] + unchecked((int)0x6ed9eba1), 14) + d; b = RL(b, 10); - d = RL(d + F3(e, a, b) + this.X[15] + unchecked((int)0x6ed9eba1), 9) + c; a = RL(a, 10); - c = RL(c + F3(d, e, a) + this.X[8] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); - b = RL(b + F3(c, d, e) + this.X[1] + unchecked((int)0x6ed9eba1), 15) + a; d = RL(d, 10); - a = RL(a + F3(b, c, d) + this.X[2] + unchecked((int)0x6ed9eba1), 14) + e; c = RL(c, 10); - e = RL(e + F3(a, b, c) + this.X[7] + unchecked((int)0x6ed9eba1), 8) + d; b = RL(b, 10); - d = RL(d + F3(e, a, b) + this.X[0] + unchecked((int)0x6ed9eba1), 13) + c; a = RL(a, 10); - c = RL(c + F3(d, e, a) + this.X[6] + unchecked((int)0x6ed9eba1), 6) + b; e = RL(e, 10); - b = RL(b + F3(c, d, e) + this.X[13] + unchecked((int)0x6ed9eba1), 5) + a; d = RL(d, 10); - a = RL(a + F3(b, c, d) + this.X[11] + unchecked((int)0x6ed9eba1), 12) + e; c = RL(c, 10); - e = RL(e + F3(a, b, c) + this.X[5] + unchecked((int)0x6ed9eba1), 7) + d; b = RL(b, 10); - d = RL(d + F3(e, a, b) + this.X[12] + unchecked((int)0x6ed9eba1), 5) + c; a = RL(a, 10); - - // right - dd = RL(dd + F3(ee, aa, bb) + this.X[15] + unchecked((int)0x6d703ef3), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F3(dd, ee, aa) + this.X[5] + unchecked((int)0x6d703ef3), 7) + bb; ee = RL(ee, 10); - bb = RL(bb + F3(cc, dd, ee) + this.X[1] + unchecked((int)0x6d703ef3), 15) + aa; dd = RL(dd, 10); - aa = RL(aa + F3(bb, cc, dd) + this.X[3] + unchecked((int)0x6d703ef3), 11) + ee; cc = RL(cc, 10); - ee = RL(ee + F3(aa, bb, cc) + this.X[7] + unchecked((int)0x6d703ef3), 8) + dd; bb = RL(bb, 10); - dd = RL(dd + F3(ee, aa, bb) + this.X[14] + unchecked((int)0x6d703ef3), 6) + cc; aa = RL(aa, 10); - cc = RL(cc + F3(dd, ee, aa) + this.X[6] + unchecked((int)0x6d703ef3), 6) + bb; ee = RL(ee, 10); - bb = RL(bb + F3(cc, dd, ee) + this.X[9] + unchecked((int)0x6d703ef3), 14) + aa; dd = RL(dd, 10); - aa = RL(aa + F3(bb, cc, dd) + this.X[11] + unchecked((int)0x6d703ef3), 12) + ee; cc = RL(cc, 10); - ee = RL(ee + F3(aa, bb, cc) + this.X[8] + unchecked((int)0x6d703ef3), 13) + dd; bb = RL(bb, 10); - dd = RL(dd + F3(ee, aa, bb) + this.X[12] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); - cc = RL(cc + F3(dd, ee, aa) + this.X[2] + unchecked((int)0x6d703ef3), 14) + bb; ee = RL(ee, 10); - bb = RL(bb + F3(cc, dd, ee) + this.X[10] + unchecked((int)0x6d703ef3), 13) + aa; dd = RL(dd, 10); - aa = RL(aa + F3(bb, cc, dd) + this.X[0] + unchecked((int)0x6d703ef3), 13) + ee; cc = RL(cc, 10); - ee = RL(ee + F3(aa, bb, cc) + this.X[4] + unchecked((int)0x6d703ef3), 7) + dd; bb = RL(bb, 10); - dd = RL(dd + F3(ee, aa, bb) + this.X[13] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); - - // - // Rounds 48-63 - // - // left - c = RL(c + F4(d, e, a) + this.X[1] + unchecked((int)0x8f1bbcdc), 11) + b; e = RL(e, 10); - b = RL(b + F4(c, d, e) + this.X[9] + unchecked((int)0x8f1bbcdc), 12) + a; d = RL(d, 10); - a = RL(a + F4(b, c, d) + this.X[11] + unchecked((int)0x8f1bbcdc), 14) + e; c = RL(c, 10); - e = RL(e + F4(a, b, c) + this.X[10] + unchecked((int)0x8f1bbcdc), 15) + d; b = RL(b, 10); - d = RL(d + F4(e, a, b) + this.X[0] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); - c = RL(c + F4(d, e, a) + this.X[8] + unchecked((int)0x8f1bbcdc), 15) + b; e = RL(e, 10); - b = RL(b + F4(c, d, e) + this.X[12] + unchecked((int)0x8f1bbcdc), 9) + a; d = RL(d, 10); - a = RL(a + F4(b, c, d) + this.X[4] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); - e = RL(e + F4(a, b, c) + this.X[13] + unchecked((int)0x8f1bbcdc), 9) + d; b = RL(b, 10); - d = RL(d + F4(e, a, b) + this.X[3] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); - c = RL(c + F4(d, e, a) + this.X[7] + unchecked((int)0x8f1bbcdc), 5) + b; e = RL(e, 10); - b = RL(b + F4(c, d, e) + this.X[15] + unchecked((int)0x8f1bbcdc), 6) + a; d = RL(d, 10); - a = RL(a + F4(b, c, d) + this.X[14] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); - e = RL(e + F4(a, b, c) + this.X[5] + unchecked((int)0x8f1bbcdc), 6) + d; b = RL(b, 10); - d = RL(d + F4(e, a, b) + this.X[6] + unchecked((int)0x8f1bbcdc), 5) + c; a = RL(a, 10); - c = RL(c + F4(d, e, a) + this.X[2] + unchecked((int)0x8f1bbcdc), 12) + b; e = RL(e, 10); - - // right - cc = RL(cc + F2(dd, ee, aa) + this.X[8] + unchecked((int)0x7a6d76e9), 15) + bb; ee = RL(ee, 10); - bb = RL(bb + F2(cc, dd, ee) + this.X[6] + unchecked((int)0x7a6d76e9), 5) + aa; dd = RL(dd, 10); - aa = RL(aa + F2(bb, cc, dd) + this.X[4] + unchecked((int)0x7a6d76e9), 8) + ee; cc = RL(cc, 10); - ee = RL(ee + F2(aa, bb, cc) + this.X[1] + unchecked((int)0x7a6d76e9), 11) + dd; bb = RL(bb, 10); - dd = RL(dd + F2(ee, aa, bb) + this.X[3] + unchecked((int)0x7a6d76e9), 14) + cc; aa = RL(aa, 10); - cc = RL(cc + F2(dd, ee, aa) + this.X[11] + unchecked((int)0x7a6d76e9), 14) + bb; ee = RL(ee, 10); - bb = RL(bb + F2(cc, dd, ee) + this.X[15] + unchecked((int)0x7a6d76e9), 6) + aa; dd = RL(dd, 10); - aa = RL(aa + F2(bb, cc, dd) + this.X[0] + unchecked((int)0x7a6d76e9), 14) + ee; cc = RL(cc, 10); - ee = RL(ee + F2(aa, bb, cc) + this.X[5] + unchecked((int)0x7a6d76e9), 6) + dd; bb = RL(bb, 10); - dd = RL(dd + F2(ee, aa, bb) + this.X[12] + unchecked((int)0x7a6d76e9), 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F2(dd, ee, aa) + this.X[2] + unchecked((int)0x7a6d76e9), 12) + bb; ee = RL(ee, 10); - bb = RL(bb + F2(cc, dd, ee) + this.X[13] + unchecked((int)0x7a6d76e9), 9) + aa; dd = RL(dd, 10); - aa = RL(aa + F2(bb, cc, dd) + this.X[9] + unchecked((int)0x7a6d76e9), 12) + ee; cc = RL(cc, 10); - ee = RL(ee + F2(aa, bb, cc) + this.X[7] + unchecked((int)0x7a6d76e9), 5) + dd; bb = RL(bb, 10); - dd = RL(dd + F2(ee, aa, bb) + this.X[10] + unchecked((int)0x7a6d76e9), 15) + cc; aa = RL(aa, 10); - cc = RL(cc + F2(dd, ee, aa) + this.X[14] + unchecked((int)0x7a6d76e9), 8) + bb; ee = RL(ee, 10); - - // - // Rounds 64-79 - // - // left - b = RL(b + F5(c, d, e) + this.X[4] + unchecked((int)0xa953fd4e), 9) + a; d = RL(d, 10); - a = RL(a + F5(b, c, d) + this.X[0] + unchecked((int)0xa953fd4e), 15) + e; c = RL(c, 10); - e = RL(e + F5(a, b, c) + this.X[5] + unchecked((int)0xa953fd4e), 5) + d; b = RL(b, 10); - d = RL(d + F5(e, a, b) + this.X[9] + unchecked((int)0xa953fd4e), 11) + c; a = RL(a, 10); - c = RL(c + F5(d, e, a) + this.X[7] + unchecked((int)0xa953fd4e), 6) + b; e = RL(e, 10); - b = RL(b + F5(c, d, e) + this.X[12] + unchecked((int)0xa953fd4e), 8) + a; d = RL(d, 10); - a = RL(a + F5(b, c, d) + this.X[2] + unchecked((int)0xa953fd4e), 13) + e; c = RL(c, 10); - e = RL(e + F5(a, b, c) + this.X[10] + unchecked((int)0xa953fd4e), 12) + d; b = RL(b, 10); - d = RL(d + F5(e, a, b) + this.X[14] + unchecked((int)0xa953fd4e), 5) + c; a = RL(a, 10); - c = RL(c + F5(d, e, a) + this.X[1] + unchecked((int)0xa953fd4e), 12) + b; e = RL(e, 10); - b = RL(b + F5(c, d, e) + this.X[3] + unchecked((int)0xa953fd4e), 13) + a; d = RL(d, 10); - a = RL(a + F5(b, c, d) + this.X[8] + unchecked((int)0xa953fd4e), 14) + e; c = RL(c, 10); - e = RL(e + F5(a, b, c) + this.X[11] + unchecked((int)0xa953fd4e), 11) + d; b = RL(b, 10); - d = RL(d + F5(e, a, b) + this.X[6] + unchecked((int)0xa953fd4e), 8) + c; a = RL(a, 10); - c = RL(c + F5(d, e, a) + this.X[15] + unchecked((int)0xa953fd4e), 5) + b; e = RL(e, 10); - b = RL(b + F5(c, d, e) + this.X[13] + unchecked((int)0xa953fd4e), 6) + a; d = RL(d, 10); - - // right - bb = RL(bb + F1(cc, dd, ee) + this.X[12], 8) + aa; dd = RL(dd, 10); - aa = RL(aa + F1(bb, cc, dd) + this.X[15], 5) + ee; cc = RL(cc, 10); - ee = RL(ee + F1(aa, bb, cc) + this.X[10], 12) + dd; bb = RL(bb, 10); - dd = RL(dd + F1(ee, aa, bb) + this.X[4], 9) + cc; aa = RL(aa, 10); - cc = RL(cc + F1(dd, ee, aa) + this.X[1], 12) + bb; ee = RL(ee, 10); - bb = RL(bb + F1(cc, dd, ee) + this.X[5], 5) + aa; dd = RL(dd, 10); - aa = RL(aa + F1(bb, cc, dd) + this.X[8], 14) + ee; cc = RL(cc, 10); - ee = RL(ee + F1(aa, bb, cc) + this.X[7], 6) + dd; bb = RL(bb, 10); - dd = RL(dd + F1(ee, aa, bb) + this.X[6], 8) + cc; aa = RL(aa, 10); - cc = RL(cc + F1(dd, ee, aa) + this.X[2], 13) + bb; ee = RL(ee, 10); - bb = RL(bb + F1(cc, dd, ee) + this.X[13], 6) + aa; dd = RL(dd, 10); - aa = RL(aa + F1(bb, cc, dd) + this.X[14], 5) + ee; cc = RL(cc, 10); - ee = RL(ee + F1(aa, bb, cc) + this.X[0], 15) + dd; bb = RL(bb, 10); - dd = RL(dd + F1(ee, aa, bb) + this.X[3], 13) + cc; aa = RL(aa, 10); - cc = RL(cc + F1(dd, ee, aa) + this.X[9], 11) + bb; ee = RL(ee, 10); - bb = RL(bb + F1(cc, dd, ee) + this.X[11], 11) + aa; dd = RL(dd, 10); - - dd += c + H1; - H1 = H2 + d + ee; - H2 = H3 + e + aa; - H3 = H4 + a + bb; - H4 = H0 + b + cc; - H0 = dd; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i < X.Length; i++) - { - this.X[i] = 0; - } - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA1Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA1Hash.cs deleted file mode 100644 index ad2aa66..0000000 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA1Hash.cs +++ /dev/null @@ -1,571 +0,0 @@ -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// SHA1 algorithm implementation - /// - public sealed class SHA1Hash : HashAlgorithm - { - private const int DIGEST_SIZE = 20; - - private const uint Y1 = 0x5a827999; - - private const uint Y2 = 0x6ed9eba1; - - private const uint Y3 = 0x8f1bbcdc; - - private const uint Y4 = 0xca62c1d6; - - private uint H1, H2, H3, H4, H5; - - private readonly uint[] _hashValue = new uint[80]; - - private int _offset; - - private readonly byte[] _buffer; - - private int _bufferOffset; - - private long _byteCount; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public SHA1Hash() - { - this._buffer = new byte[4]; - this.InternalInitialize(); - } - - /// - /// Routes data written to the object into the hash algorithm for computing the hash. - /// - /// The input to compute the hash code for. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - long bitLength = (this._byteCount << 3); - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - this.Update((byte)0); - - if (this._offset > 14) - { - this.ProcessBlock(); - } - - this._hashValue[14] = (uint)((ulong)bitLength >> 32); - this._hashValue[15] = (uint)((ulong)bitLength); - - - this.ProcessBlock(); - - UInt32ToBigEndian(H1, output, 0); - UInt32ToBigEndian(H2, output, 4); - UInt32ToBigEndian(H3, output, 8); - UInt32ToBigEndian(H4, output, 12); - UInt32ToBigEndian(H5, output, 16); - - this.Initialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - private void InternalInitialize() - { - this._byteCount = 0; - this._bufferOffset = 0; - for (var i = 0; i < 4; i++) - { - this._buffer[i] = 0; - } - - H1 = 0x67452301; - H2 = 0xefcdab89; - H3 = 0x98badcfe; - H4 = 0x10325476; - H5 = 0xc3d2e1f0; - - this._offset = 0; - for (var i = 0; i != this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - private void ProcessWord(byte[] input, int inOff) - { - this._hashValue[this._offset] = BigEndianToUInt32(input, inOff); - - if (++this._offset == 16) - { - this.ProcessBlock(); - } - } - - private static uint F(uint u, uint v, uint w) - { - return (u & v) | (~u & w); - } - - private static uint H(uint u, uint v, uint w) - { - return u ^ v ^ w; - } - - private static uint G(uint u, uint v, uint w) - { - return (u & v) | (u & w) | (v & w); - } - - private void ProcessBlock() - { - // - // expand 16 word block into 80 word block. - // - for (int i = 16; i < 80; i++) - { - uint t = _hashValue[i - 3] ^ _hashValue[i - 8] ^ _hashValue[i - 14] ^ _hashValue[i - 16]; - _hashValue[i] = t << 1 | t >> 31; - } - - // - // set up working variables. - // - uint A = H1; - uint B = H2; - uint C = H3; - uint D = H4; - uint E = H5; - - // - // round 1 - // - int idx = 0; - - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + F(B, C, D) + E + X[idx++] + Y1 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + F(B, C, D) + _hashValue[idx++] + Y1; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + F(A, B, C) + _hashValue[idx++] + Y1; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + F(E, A, B) + _hashValue[idx++] + Y1; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + F(D, E, A) + _hashValue[idx++] + Y1; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + F(C, D, E) + _hashValue[idx++] + Y1; - C = C << 30 | (C >> 2); - // - // round 2 - // - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y2 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y2; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y2; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y2; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y2; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y2; - C = C << 30 | (C >> 2); - - // - // round 3 - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + G(B, C, D) + E + X[idx++] + Y3 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + G(B, C, D) + _hashValue[idx++] + Y3; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + G(A, B, C) + _hashValue[idx++] + Y3; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + G(E, A, B) + _hashValue[idx++] + Y3; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + G(D, E, A) + _hashValue[idx++] + Y3; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + G(C, D, E) + _hashValue[idx++] + Y3; - C = C << 30 | (C >> 2); - - // - // round 4 - // - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - // E = rotateLeft(A, 5) + H(B, C, D) + E + X[idx++] + Y4 - // B = rotateLeft(B, 30) - E += (A << 5 | (A >> 27)) + H(B, C, D) + _hashValue[idx++] + Y4; - B = B << 30 | (B >> 2); - - D += (E << 5 | (E >> 27)) + H(A, B, C) + _hashValue[idx++] + Y4; - A = A << 30 | (A >> 2); - - C += (D << 5 | (D >> 27)) + H(E, A, B) + _hashValue[idx++] + Y4; - E = E << 30 | (E >> 2); - - B += (C << 5 | (C >> 27)) + H(D, E, A) + _hashValue[idx++] + Y4; - D = D << 30 | (D >> 2); - - A += (B << 5 | (B >> 27)) + H(C, D, E) + _hashValue[idx++] + Y4; - C = C << 30 | (C >> 2); - - H1 += A; - H2 += B; - H3 += C; - H4 += D; - H5 += E; - - // - // reset start of the buffer. - // - this._offset = 0; - for (int i = 0; i < this._hashValue.Length; i++) - { - this._hashValue[i] = 0; - } - } - - private static uint BigEndianToUInt32(byte[] bs, int off) - { - uint n = (uint)bs[off] << 24; - n |= (uint)bs[++off] << 16; - n |= (uint)bs[++off] << 8; - n |= (uint)bs[++off]; - return n; - } - - private static void UInt32ToBigEndian(uint n, byte[] bs, int off) - { - bs[off] = (byte)(n >> 24); - bs[++off] = (byte)(n >> 16); - bs[++off] = (byte)(n >> 8); - bs[++off] = (byte)(n); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA256Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA256Hash.cs deleted file mode 100644 index 85cbdda..0000000 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA256Hash.cs +++ /dev/null @@ -1,396 +0,0 @@ -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// SHA256 algorithm implementation. - /// - public class SHA256Hash : HashAlgorithm - { - private const int DIGEST_SIZE = 32; - - private uint H1, H2, H3, H4, H5, H6, H7, H8; - - private readonly uint[] X = new uint[64]; - - private int _offset; - - private readonly byte[] _buffer; - - private int _bufferOffset; - - private long _byteCount; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// Gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return 64; - } - } - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public SHA256Hash() - { - this._buffer = new byte[4]; - this.InternalInitialize(); - } - - /// - /// Routes data written to the object into the hash algorithm for computing the hash. - /// - /// The input to compute the hash code for. - /// The offset into the byte array from which to begin using data. - /// The number of bytes in the byte array to use as data. - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - this.ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - /// - /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. - /// - /// - /// The computed hash code. - /// - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - long bitLength = (this._byteCount << 3); - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - this.Update((byte)0); - - if (this._offset > 14) - { - this.ProcessBlock(); - } - - X[14] = (uint)((ulong)bitLength >> 32); - X[15] = (uint)((ulong)bitLength); - - - this.ProcessBlock(); - - UInt32_To_BE((uint)H1, output, 0); - UInt32_To_BE((uint)H2, output, 0 + 4); - UInt32_To_BE((uint)H3, output, 0 + 8); - UInt32_To_BE((uint)H4, output, 0 + 12); - UInt32_To_BE((uint)H5, output, 0 + 16); - UInt32_To_BE((uint)H6, output, 0 + 20); - UInt32_To_BE((uint)H7, output, 0 + 24); - UInt32_To_BE((uint)H8, output, 0 + 28); - - this.Initialize(); - - return output; - } - - /// - /// Initializes an implementation of the class. - /// - public override void Initialize() - { - this.InternalInitialize(); - } - - private void InternalInitialize() - { - this._byteCount = 0; - this._bufferOffset = 0; - for (int i = 0; i < this._buffer.Length; i++) - { - this._buffer[i] = 0; - } - - H1 = 0x6a09e667; - H2 = 0xbb67ae85; - H3 = 0x3c6ef372; - H4 = 0xa54ff53a; - H5 = 0x510e527f; - H6 = 0x9b05688c; - H7 = 0x1f83d9ab; - H8 = 0x5be0cd19; - - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - private void Update(byte input) - { - this._buffer[this._bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount++; - } - - private static uint BE_To_UInt32(byte[] bs, int off) - { - uint n = (uint)bs[off] << 24; - n |= (uint)bs[++off] << 16; - n |= (uint)bs[++off] << 8; - n |= (uint)bs[++off]; - return n; - } - - private static void UInt32_To_BE(uint n, byte[] bs, int off) - { - bs[off] = (byte)(n >> 24); - bs[++off] = (byte)(n >> 16); - bs[++off] = (byte)(n >> 8); - bs[++off] = (byte)(n); - } - - private void ProcessWord(byte[] input, int inOff) - { - X[this._offset] = BE_To_UInt32(input, inOff); - - if (++this._offset == 16) - { - ProcessBlock(); - } - } - - private void ProcessLength(long bitLength) - { - if (this._offset > 14) - { - ProcessBlock(); - } - - X[14] = (uint)((ulong)bitLength >> 32); - X[15] = (uint)((ulong)bitLength); - } - - private void ProcessBlock() - { - // - // expand 16 word block into 64 word blocks. - // - for (int ti = 16; ti <= 63; ti++) - { - X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16]; - } - - // - // set up working variables. - // - uint a = H1; - uint b = H2; - uint c = H3; - uint d = H4; - uint e = H5; - uint f = H6; - uint g = H7; - uint h = H8; - - int t = 0; - for (int i = 0; i < 8; ++i) - { - // t = 8 * i - h += Sum1Ch(e, f, g) + K[t] + X[t]; - d += h; - h += Sum0Maj(a, b, c); - ++t; - - // t = 8 * i + 1 - g += Sum1Ch(d, e, f) + K[t] + X[t]; - c += g; - g += Sum0Maj(h, a, b); - ++t; - - // t = 8 * i + 2 - f += Sum1Ch(c, d, e) + K[t] + X[t]; - b += f; - f += Sum0Maj(g, h, a); - ++t; - - // t = 8 * i + 3 - e += Sum1Ch(b, c, d) + K[t] + X[t]; - a += e; - e += Sum0Maj(f, g, h); - ++t; - - // t = 8 * i + 4 - d += Sum1Ch(a, b, c) + K[t] + X[t]; - h += d; - d += Sum0Maj(e, f, g); - ++t; - - // t = 8 * i + 5 - c += Sum1Ch(h, a, b) + K[t] + X[t]; - g += c; - c += Sum0Maj(d, e, f); - ++t; - - // t = 8 * i + 6 - b += Sum1Ch(g, h, a) + K[t] + X[t]; - f += b; - b += Sum0Maj(c, d, e); - ++t; - - // t = 8 * i + 7 - a += Sum1Ch(f, g, h) + K[t] + X[t]; - e += a; - a += Sum0Maj(b, c, d); - ++t; - } - - H1 += a; - H2 += b; - H3 += c; - H4 += d; - H5 += e; - H6 += f; - H7 += g; - H8 += h; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - private static uint Sum1Ch(uint x, uint y, uint z) - { - // return Sum1(x) + Ch(x, y, z); - return (((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7))) - + ((x & y) ^ ((~x) & z)); - } - - private static uint Sum0Maj(uint x, uint y, uint z) - { - // return Sum0(x) + Maj(x, y, z); - return (((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10))) - + ((x & y) ^ (x & z) ^ (y & z)); - } - - private static uint Theta0(uint x) - { - return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3); - } - - private static uint Theta1(uint x) - { - return ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10); - } - - /// - /// The SHA-256 Constants (represent the first 32 bits of the fractional parts of the cube roots of the first sixty-four prime numbers) - /// - private static readonly uint[] K = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - }; - } -} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs deleted file mode 100644 index e45115a..0000000 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA2HashBase.cs +++ /dev/null @@ -1,362 +0,0 @@ -using System.Security.Cryptography; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// SHA256 algorithm implementation. - /// - public abstract class SHA2HashBase : HashAlgorithm - { - protected ulong H1, H2, H3, H4, H5, H6, H7, H8; - - private readonly ulong[] X = new ulong[80]; - - private int _offset; - - private readonly byte[] _buffer; - - private int _bufferOffset; - - private long _byteCount1; - - private long _byteCount2; - - /// - /// Gets a value indicating whether the current transform can be reused. - /// - /// Always true. - public override bool CanReuseTransform - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether multiple blocks can be transformed. - /// - /// true if multiple blocks can be transformed; otherwise, false. - public override bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public SHA2HashBase() - { - this._buffer = new byte[8]; - - this.Initialize(); - } - - protected override void HashCore(byte[] array, int ibStart, int cbSize) - { - // Fill the current word - while ((this._bufferOffset != 0) && (cbSize > 0)) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - - // Process whole words. - while (cbSize > this._buffer.Length) - { - ProcessWord(array, ibStart); - - ibStart += this._buffer.Length; - cbSize -= this._buffer.Length; - this._byteCount1 += this._buffer.Length; - } - - // Load in the remainder. - while (cbSize > 0) - { - this.Update(array[ibStart]); - - ibStart++; - cbSize--; - } - } - - public override void Initialize() - { - this._byteCount1 = 0; - this._byteCount2 = 0; - - this._bufferOffset = 0; - for (int i = 0; i < this._buffer.Length; i++) - { - this._buffer[i] = 0; - } - - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - protected void Finish() - { - this.AdjustByteCounts(); - - long lowBitLength = this._byteCount1 << 3; - long hiBitLength = this._byteCount2; - - // - // add the pad bytes. - // - this.Update((byte)128); - - while (this._bufferOffset != 0) - { - this.Update((byte)0); - } - - this.ProcessLength(lowBitLength, hiBitLength); - - this.ProcessBlock(); - } - - private void Update(byte input) - { - this._buffer[_bufferOffset++] = input; - - if (this._bufferOffset == this._buffer.Length) - { - this.ProcessWord(this._buffer, 0); - this._bufferOffset = 0; - } - - this._byteCount1++; - } - - private void ProcessWord(byte[] input, int inOff) - { - this.X[_offset] = SHA512Hash.BE_To_UInt64(input, inOff); - - if (++_offset == 16) - { - ProcessBlock(); - } - } - - internal void ProcessLength(long lowW, long hiW) - { - if (_offset > 14) - { - this.ProcessBlock(); - } - - this.X[14] = (ulong)hiW; - this.X[15] = (ulong)lowW; - } - - private void ProcessBlock() - { - this.AdjustByteCounts(); - - // - // expand 16 word block into 80 word blocks. - // - for (int ti = 16; ti <= 79; ++ti) - { - X[ti] = Sigma1(X[ti - 2]) + X[ti - 7] + Sigma0(X[ti - 15]) + X[ti - 16]; - } - - // - // set up working variables. - // - ulong a = H1; - ulong b = H2; - ulong c = H3; - ulong d = H4; - ulong e = H5; - ulong f = H6; - ulong g = H7; - ulong h = H8; - - int t = 0; - for (int i = 0; i < 10; i++) - { - // t = 8 * i - h += Sum1(e) + Ch(e, f, g) + K[t] + X[t++]; - d += h; - h += Sum0(a) + Maj(a, b, c); - - // t = 8 * i + 1 - g += Sum1(d) + Ch(d, e, f) + K[t] + X[t++]; - c += g; - g += Sum0(h) + Maj(h, a, b); - - // t = 8 * i + 2 - f += Sum1(c) + Ch(c, d, e) + K[t] + X[t++]; - b += f; - f += Sum0(g) + Maj(g, h, a); - - // t = 8 * i + 3 - e += Sum1(b) + Ch(b, c, d) + K[t] + X[t++]; - a += e; - e += Sum0(f) + Maj(f, g, h); - - // t = 8 * i + 4 - d += Sum1(a) + Ch(a, b, c) + K[t] + X[t++]; - h += d; - d += Sum0(e) + Maj(e, f, g); - - // t = 8 * i + 5 - c += Sum1(h) + Ch(h, a, b) + K[t] + X[t++]; - g += c; - c += Sum0(d) + Maj(d, e, f); - - // t = 8 * i + 6 - b += Sum1(g) + Ch(g, h, a) + K[t] + X[t++]; - f += b; - b += Sum0(c) + Maj(c, d, e); - - // t = 8 * i + 7 - a += Sum1(f) + Ch(f, g, h) + K[t] + X[t++]; - e += a; - a += Sum0(b) + Maj(b, c, d); - } - - H1 += a; - H2 += b; - H3 += c; - H4 += d; - H5 += e; - H6 += f; - H7 += g; - H8 += h; - - // - // reset the offset and clean out the word buffer. - // - this._offset = 0; - for (int i = 0; i < this.X.Length; i++) - { - this.X[i] = 0; - } - } - - /// - /// Adjust the byte counts so that byteCount2 represents the upper long (less 3 bits) word of the byte count. - /// - private void AdjustByteCounts() - { - if (this._byteCount1 > 0x1fffffffffffffffL) - { - this._byteCount2 += (long)((ulong)this._byteCount1 >> 61); - this._byteCount1 &= 0x1fffffffffffffffL; - } - } - - /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ - private static ulong Ch(ulong x, ulong y, ulong z) - { - return (x & y) ^ (~x & z); - } - - private static ulong Maj(ulong x, ulong y, ulong z) - { - return (x & y) ^ (x & z) ^ (y & z); - } - - private static ulong Sum0(ulong x) - { - return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); - } - - private static ulong Sum1(ulong x) - { - return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); - } - - private static ulong Sigma0(ulong x) - { - return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); - } - - private static ulong Sigma1(ulong x) - { - return ((x << 45) | (x >> 19)) ^ ((x << 3) | (x >> 61)) ^ (x >> 6); - } - - /* SHA-384 and SHA-512 Constants - * (represent the first 64 bits of the fractional parts of the - * cube roots of the first sixty-four prime numbers) - */ - private static readonly ulong[] K = - { - 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, - 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, - 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, - 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, - 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, - 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, - 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, - 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, - 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, - 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, - 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, - 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, - 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, - 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, - 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, - 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, - 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, - 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, - 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, - 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 - }; - - protected static void UInt32_To_BE(uint n, byte[] bs, int off) - { - bs[off] = (byte)(n >> 24); - bs[++off] = (byte)(n >> 16); - bs[++off] = (byte)(n >> 8); - bs[++off] = (byte)(n); - } - protected static void UInt64_To_BE(ulong n, byte[] bs, int off) - { - UInt32_To_BE((uint)(n >> 32), bs, off); - UInt32_To_BE((uint)(n), bs, off + 4); - } - protected static ulong BE_To_UInt64(byte[] bs) - { - uint hi = BE_To_UInt32(bs); - uint lo = BE_To_UInt32(bs, 4); - return ((ulong)hi << 32) | (ulong)lo; - } - protected static ulong BE_To_UInt64(byte[] bs, int off) - { - uint hi = BE_To_UInt32(bs, off); - uint lo = BE_To_UInt32(bs, off + 4); - return ((ulong)hi << 32) | (ulong)lo; - } - protected static uint BE_To_UInt32(byte[] bs, int off) - { - uint n = (uint)bs[off] << 24; - n |= (uint)bs[++off] << 16; - n |= (uint)bs[++off] << 8; - n |= (uint)bs[++off]; - return n; - } - protected static uint BE_To_UInt32(byte[] bs) - { - uint n = (uint)bs[0] << 24; - n |= (uint)bs[1] << 16; - n |= (uint)bs[2] << 8; - n |= (uint)bs[3]; - return n; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs deleted file mode 100644 index 1c5d2fe..0000000 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA384Hash.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace Renci.SshNet.Security.Cryptography -{ - public class SHA384Hash : SHA2HashBase - { - private const int DIGEST_SIZE = 48; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// When overridden in a derived class, gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - /// - /// When overridden in a derived class, gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - - this.Finish(); - - SHA2HashBase.UInt64_To_BE(H1, output, 0); - SHA2HashBase.UInt64_To_BE(H2, output, 8); - SHA2HashBase.UInt64_To_BE(H3, output, 16); - SHA2HashBase.UInt64_To_BE(H4, output, 24); - SHA2HashBase.UInt64_To_BE(H5, output, 32); - SHA2HashBase.UInt64_To_BE(H6, output, 40); - - this.Initialize(); - - return output; - } - - public override void Initialize() - { - base.Initialize(); - - /* SHA-384 initial hash value - * The first 64 bits of the fractional parts of the square roots - * of the 9th through 16th prime numbers - */ - H1 = 0xcbbb9d5dc1059ed8; - H2 = 0x629a292a367cd507; - H3 = 0x9159015a3070dd17; - H4 = 0x152fecd8f70e5939; - H5 = 0x67332667ffc00b31; - H6 = 0x8eb44a8768581511; - H7 = 0xdb0c2e0d64f98fa7; - H8 = 0x47b5481dbefa4fa4; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs b/Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs deleted file mode 100644 index cf5f7df..0000000 --- a/Renci.SshNet/Security/Cryptography/Hashes/SHA512Hash.cs +++ /dev/null @@ -1,81 +0,0 @@ -namespace Renci.SshNet.Security.Cryptography -{ - public class SHA512Hash : SHA2HashBase - { - private const int DIGEST_SIZE = 64; - - /// - /// Gets the size, in bits, of the computed hash code. - /// - /// The size, in bits, of the computed hash code. - public override int HashSize - { - get - { - return DIGEST_SIZE * 8; - } - } - - /// - /// When overridden in a derived class, gets the input block size. - /// - /// The input block size. - public override int InputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - /// - /// When overridden in a derived class, gets the output block size. - /// - /// The output block size. - public override int OutputBlockSize - { - get - { - return DIGEST_SIZE * 2; - } - } - - protected override byte[] HashFinal() - { - var output = new byte[DIGEST_SIZE]; - - this.Finish(); - - SHA2HashBase.UInt64_To_BE(H1, output, 0); - SHA2HashBase.UInt64_To_BE(H2, output, 8); - SHA2HashBase.UInt64_To_BE(H3, output, 16); - SHA2HashBase.UInt64_To_BE(H4, output, 24); - SHA2HashBase.UInt64_To_BE(H5, output, 32); - SHA2HashBase.UInt64_To_BE(H6, output, 40); - SHA2HashBase.UInt64_To_BE(H7, output, 48); - SHA2HashBase.UInt64_To_BE(H8, output, 56); - - this.Initialize(); - - return output; - } - - public override void Initialize() - { - base.Initialize(); - - /* SHA-512 initial hash value - * The first 64 bits of the fractional parts of the square roots - * of the first eight prime numbers - */ - H1 = 0x6a09e667f3bcc908; - H2 = 0xbb67ae8584caa73b; - H3 = 0x3c6ef372fe94f82b; - H4 = 0xa54ff53a5f1d36f1; - H5 = 0x510e527fade682d1; - H6 = 0x9b05688c2b3e6c1f; - H7 = 0x1f83d9abfb41bd6b; - H8 = 0x5be0cd19137e2179; - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/Key.cs b/Renci.SshNet/Security/Cryptography/Key.cs deleted file mode 100644 index c13ede0..0000000 --- a/Renci.SshNet/Security/Cryptography/Key.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Base class for asymmetric cipher algorithms - /// - public abstract class Key - { - /// - /// Specifies array of big integers that represent private key - /// - protected BigInteger[] _privateKey; - - /// - /// Gets the key specific digital signature. - /// - protected abstract DigitalSignature DigitalSignature { get; } - - /// - /// Gets or sets the public key. - /// - /// - /// The public. - /// - public abstract BigInteger[] Public { get; set; } - - /// - /// Gets the length of the key. - /// - /// - /// The length of the key. - /// - public abstract int KeyLength { get; } - - /// - /// Initializes a new instance of the class. - /// - /// DER encoded private key data. - public Key(byte[] data) - { - if (data == null) - throw new ArgumentNullException("data"); - - var der = new DerData(data); - var version = der.ReadBigInteger(); - - var keys = new List(); - while (!der.IsEndOfData) - { - keys.Add(der.ReadBigInteger()); - } - - this._privateKey = keys.ToArray(); - } - - /// - /// Initializes a new instance of the class. - /// - public Key() - { - } - - /// - /// Signs the specified data with the key. - /// - /// The data to sign. - /// - /// Signed data. - /// - public byte[] Sign(byte[] data) - { - return this.DigitalSignature.Sign(data); - } - - /// - /// Verifies the signature. - /// - /// The data to verify. - /// The signature to verify against. - /// True is signature was successfully verifies; otherwise false. - public bool VerifySignature(byte[] data, byte[] signature) - { - return this.DigitalSignature.Verify(data, signature); - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs b/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs deleted file mode 100644 index bf3dedc..0000000 --- a/Renci.SshNet/Security/Cryptography/RsaDigitalSignature.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography.Ciphers; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Implements RSA digital signature algorithm. - /// - public class RsaDigitalSignature : CipherDigitalSignature, IDisposable - { - private HashAlgorithm _hash; - - /// - /// Initializes a new instance of the class. - /// - /// The RSA key. - public RsaDigitalSignature(RsaKey rsaKey) - : base(new ObjectIdentifier(1, 3, 14, 3, 2, 26), new RsaCipher(rsaKey)) - { - this._hash = new SHA1Hash(); - } - - /// - /// Hashes the specified input. - /// - /// The input. - /// - /// Hashed data. - /// - protected override byte[] Hash(byte[] input) - { - return this._hash.ComputeHash(input); - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._hash != null) - { - this._hash.Clear(); - this._hash = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~RsaDigitalSignature() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/Security/Cryptography/RsaKey.cs b/Renci.SshNet/Security/Cryptography/RsaKey.cs deleted file mode 100644 index c293800..0000000 --- a/Renci.SshNet/Security/Cryptography/RsaKey.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Contains RSA private and public key - /// - public class RsaKey : Key, IDisposable - { - /// - /// Gets the modulus. - /// - public BigInteger Modulus - { - get - { - return this._privateKey[0]; - } - } - - /// - /// Gets the exponent. - /// - public BigInteger Exponent - { - get - { - return this._privateKey[1]; - } - } - - /// - /// Gets the D. - /// - public BigInteger D - { - get - { - if (this._privateKey.Length > 2) - return this._privateKey[2]; - return BigInteger.Zero; - } - } - - /// - /// Gets the P. - /// - public BigInteger P - { - get - { - if (this._privateKey.Length > 3) - return this._privateKey[3]; - return BigInteger.Zero; - } - } - - /// - /// Gets the Q. - /// - public BigInteger Q - { - get - { - if (this._privateKey.Length > 4) - return this._privateKey[4]; - return BigInteger.Zero; - } - } - - /// - /// Gets the DP. - /// - public BigInteger DP - { - get - { - if (this._privateKey.Length > 5) - return this._privateKey[5]; - return BigInteger.Zero; - } - } - - /// - /// Gets the DQ. - /// - public BigInteger DQ - { - get - { - if (this._privateKey.Length > 6) - return this._privateKey[6]; - return BigInteger.Zero; - } - } - - /// - /// Gets the inverse Q. - /// - public BigInteger InverseQ - { - get - { - if (this._privateKey.Length > 7) - return this._privateKey[7]; - return BigInteger.Zero; - } - } - - /// - /// Gets the length of the key. - /// - /// - /// The length of the key. - /// - public override int KeyLength - { - get - { - return this.Modulus.BitLength; - } - } - - private RsaDigitalSignature _digitalSignature; - /// - /// Gets the digital signature. - /// - protected override DigitalSignature DigitalSignature - { - get - { - if (this._digitalSignature == null) - { - this._digitalSignature = new RsaDigitalSignature(this); - } - return this._digitalSignature; - } - } - - /// - /// Gets or sets the public. - /// - /// - /// The public. - /// - public override BigInteger[] Public - { - get - { - return new BigInteger[] { this.Exponent, this.Modulus }; - } - set - { - if (value.Length != 2) - throw new InvalidOperationException("Invalid private key."); - - this._privateKey = new BigInteger[] { value[1], value[0] }; - } - } - - /// - /// Initializes a new instance of the class. - /// - public RsaKey() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// DER encoded private key data. - public RsaKey(byte[] data) - : base(data) - { - if (this._privateKey.Length != 8) - throw new InvalidOperationException("Invalid private key."); - } - - /// - /// Initializes a new instance of the class. - /// - /// The modulus. - /// The exponent. - /// The d. - /// The p. - /// The q. - /// The inverse Q. - public RsaKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q, BigInteger inverseQ) - { - this._privateKey = new BigInteger[8]; - this._privateKey[0] = modulus; - this._privateKey[1] = exponent; - this._privateKey[2] = d; - this._privateKey[3] = p; - this._privateKey[4] = q; - this._privateKey[5] = PrimeExponent(d, p); - this._privateKey[6] = PrimeExponent(d, q); - this._privateKey[7] = inverseQ; - } - - private static BigInteger PrimeExponent(BigInteger privateExponent, BigInteger prime) - { - BigInteger pe = prime - new BigInteger(1); - return privateExponent % pe; - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - // Dispose managed ResourceMessages. - if (this._digitalSignature != null) - { - this._digitalSignature.Dispose(); - this._digitalSignature = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~RsaKey() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/Security/Cryptography/StreamCipher.cs b/Renci.SshNet/Security/Cryptography/StreamCipher.cs deleted file mode 100644 index a0f3442..0000000 --- a/Renci.SshNet/Security/Cryptography/StreamCipher.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class of stream cipher algorithms. - /// - public abstract class StreamCipher : SymmetricCipher - { - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// is null. - protected StreamCipher(byte[] key) - : base(key) - { - } - } -} diff --git a/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs b/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs deleted file mode 100644 index 4eb3396..0000000 --- a/Renci.SshNet/Security/Cryptography/SymmetricCipher.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -namespace Renci.SshNet.Security.Cryptography -{ - /// - /// Base class for symmetric cipher implementations. - /// - public abstract class SymmetricCipher : Cipher - { - /// - /// Gets the key. - /// - protected byte[] Key { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The key. - /// is null. - protected SymmetricCipher(byte[] key) - { - if (key == null) - throw new ArgumentNullException("key"); - - this.Key = key; - } - - /// - /// Encrypts the specified region of the input byte array and copies the encrypted data to the specified region of the output byte array. - /// - /// The input data to encrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write encrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes encrypted. - /// - public abstract int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - - /// - /// Decrypts the specified region of the input byte array and copies the decrypted data to the specified region of the output byte array. - /// - /// The input data to decrypt. - /// The offset into the input byte array from which to begin using data. - /// The number of bytes in the input byte array to use as data. - /// The output to which to write decrypted data. - /// The offset into the output byte array from which to begin writing data. - /// - /// The number of bytes decrypted. - /// - public abstract int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); - } -} diff --git a/Renci.SshNet/Security/HostAlgorithm.cs b/Renci.SshNet/Security/HostAlgorithm.cs deleted file mode 100644 index 14474d9..0000000 --- a/Renci.SshNet/Security/HostAlgorithm.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Renci.SshNet.Security -{ - /// - /// Base class for SSH host algorithms. - /// - public abstract class HostAlgorithm - { - /// - /// Gets the host key name. - /// - public string Name { get; private set; } - - /// - /// Gets the host key data. - /// - public abstract byte[] Data { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The host key name. - public HostAlgorithm(string name) - { - this.Name = name; - } - - /// - /// Signs the specified data. - /// - /// The data. - /// Signed data. - public abstract byte[] Sign(byte[] data); - - /// - /// Verifies the signature. - /// - /// The data. - /// The signature. - /// True is signature was successfully verifies; otherwise false. - public abstract bool VerifySignature(byte[] data, byte[] signature); - } -} diff --git a/Renci.SshNet/Security/KeyExchange.cs b/Renci.SshNet/Security/KeyExchange.cs deleted file mode 100644 index a2bbe70..0000000 --- a/Renci.SshNet/Security/KeyExchange.cs +++ /dev/null @@ -1,456 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using Renci.SshNet.Common; -using Renci.SshNet.Compression; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Represents base class for different key exchange algorithm implementations - /// - public abstract class KeyExchange : Algorithm, IDisposable - { - private CipherInfo _clientCipherInfo; - - private CipherInfo _serverCipherInfo; - - private HashInfo _clientHashInfo; - - private HashInfo _serverHashInfo; - - private Type _compressionType; - - private Type _decompressionType; - - /// - /// Gets or sets the session. - /// - /// - /// The session. - /// - protected Session Session { get; private set; } - - /// - /// Gets or sets key exchange shared key. - /// - /// - /// The shared key. - /// - public BigInteger SharedKey { get; protected set; } - - private byte[] _exchangeHash; - /// - /// Gets the exchange hash. - /// - /// The exchange hash. - public byte[] ExchangeHash - { - get - { - if (this._exchangeHash == null) - { - this._exchangeHash = this.CalculateHash(); - } - return this._exchangeHash; - } - } - - /// - /// Occurs when host key received. - /// - public event EventHandler HostKeyReceived; - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public virtual void Start(Session session, KeyExchangeInitMessage message) - { - this.Session = session; - - this.SendMessage(session.ClientInitMessage); - - // Determine encryption algorithm - var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys - from a in message.EncryptionAlgorithmsClientToServer - where a == b - select a).FirstOrDefault(); - - if (string.IsNullOrEmpty(clientEncryptionAlgorithmName)) - { - throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentClientEncryption = clientEncryptionAlgorithmName; - - // Determine encryption algorithm - var serverDecryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys - from a in message.EncryptionAlgorithmsServerToClient - where a == b - select a).FirstOrDefault(); - if (string.IsNullOrEmpty(serverDecryptionAlgorithmName)) - { - throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentServerEncryption = serverDecryptionAlgorithmName; - - // Determine client hmac algorithm - var clientHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys - from a in message.MacAlgorithmsClientToServer - where a == b - select a).FirstOrDefault(); - if (string.IsNullOrEmpty(clientHmacAlgorithmName)) - { - throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentClientHmacAlgorithm = clientHmacAlgorithmName; - - // Determine server hmac algorithm - var serverHmacAlgorithmName = (from b in session.ConnectionInfo.HmacAlgorithms.Keys - from a in message.MacAlgorithmsServerToClient - where a == b - select a).FirstOrDefault(); - if (string.IsNullOrEmpty(serverHmacAlgorithmName)) - { - throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentServerHmacAlgorithm = serverHmacAlgorithmName; - - // Determine compression algorithm - var compressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys - from a in message.CompressionAlgorithmsClientToServer - where a == b - select a).LastOrDefault(); - if (string.IsNullOrEmpty(compressionAlgorithmName)) - { - throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentClientCompressionAlgorithm = compressionAlgorithmName; - - // Determine decompression algorithm - var decompressionAlgorithmName = (from b in session.ConnectionInfo.CompressionAlgorithms.Keys - from a in message.CompressionAlgorithmsServerToClient - where a == b - select a).LastOrDefault(); - if (string.IsNullOrEmpty(decompressionAlgorithmName)) - { - throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed); - } - - session.ConnectionInfo.CurrentServerCompressionAlgorithm = decompressionAlgorithmName; - - this._clientCipherInfo = session.ConnectionInfo.Encryptions[clientEncryptionAlgorithmName]; - this._serverCipherInfo = session.ConnectionInfo.Encryptions[serverDecryptionAlgorithmName]; - this._clientHashInfo = session.ConnectionInfo.HmacAlgorithms[clientHmacAlgorithmName]; - this._serverHashInfo = session.ConnectionInfo.HmacAlgorithms[serverHmacAlgorithmName]; - this._compressionType = session.ConnectionInfo.CompressionAlgorithms[compressionAlgorithmName]; - this._decompressionType = session.ConnectionInfo.CompressionAlgorithms[decompressionAlgorithmName]; - } - - /// - /// Finishes key exchange algorithm. - /// - public virtual void Finish() - { - // Validate hash - if (this.ValidateExchangeHash()) - { - this.SendMessage(new NewKeysMessage()); - } - else - { - throw new SshConnectionException("Key exchange negotiation failed.", DisconnectReason.KeyExchangeFailed); - } - } - - /// - /// Creates the server side cipher to use. - /// - /// Server cipher. - public Cipher CreateServerCipher() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - // Calculate server to client initial IV - var serverVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'B', sessionId)); - - // Calculate server to client encryption - var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'D', sessionId)); - - serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, this._serverCipherInfo.KeySize / 8); - - // Create server cipher - return this._serverCipherInfo.Cipher(serverKey, serverVector); - } - - /// - /// Creates the client side cipher to use. - /// - /// Client cipher. - public Cipher CreateClientCipher() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - // Calculate client to server initial IV - var clientVector = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'A', sessionId)); - - // Calculate client to server encryption - var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'C', sessionId)); - - clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, this._clientCipherInfo.KeySize / 8); - - // Create client cipher - return this._clientCipherInfo.Cipher(clientKey, clientVector); - } - - /// - /// Creates the server side hash algorithm to use. - /// - /// Hash algorithm - public HashAlgorithm CreateServerHash() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - var serverKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'F', sessionId)); - - serverKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, serverKey, this._serverHashInfo.KeySize / 8); - - //return serverHMac; - return this._serverHashInfo.HashAlgorithm(serverKey); - } - - /// - /// Creates the client side hash algorithm to use. - /// - /// Hash algorithm - public HashAlgorithm CreateClientHash() - { - // Resolve Session ID - var sessionId = this.Session.SessionId ?? this.ExchangeHash; - - var clientKey = this.Hash(this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, 'E', sessionId)); - - clientKey = this.GenerateSessionKey(this.SharedKey, this.ExchangeHash, clientKey, this._clientHashInfo.KeySize / 8); - - //return clientHMac; - return this._clientHashInfo.HashAlgorithm(clientKey); - } - - /// - /// Creates the compression algorithm to use to deflate data. - /// - /// Compression method. - public Compressor CreateCompressor() - { - if (this._compressionType == null) - return null; - - var compressor = this._compressionType.CreateInstance(); - - compressor.Init(this.Session); - - return compressor; - } - - /// - /// Creates the compression algorithm to use to inflate data. - /// - /// Compression method. - public Compressor CreateDecompressor() - { - if (this._compressionType == null) - return null; - - var decompressor = this._decompressionType.CreateInstance(); - - decompressor.Init(this.Session); - - return decompressor; - } - - /// - /// Determines whether the specified host key can be trusted. - /// - /// The host algorithm. - /// - /// true if the specified host can be trusted; otherwise, false. - /// - protected bool CanTrustHostKey(KeyHostAlgorithm host) - { - var args = new HostKeyEventArgs(host); - - if (this.HostKeyReceived != null) - { - this.HostKeyReceived(this, args); - } - - return args.CanTrust; - } - - /// - /// Validates the exchange hash. - /// - /// true if exchange hash is valid; otherwise false. - protected abstract bool ValidateExchangeHash(); - - /// - /// Calculates key exchange hash value. - /// - /// Key exchange hash. - protected abstract byte[] CalculateHash(); - - /// - /// Hashes the specified data bytes. - /// - /// The hash data. - /// - /// Hashed bytes - /// - protected virtual byte[] Hash(byte[] hashData) - { - using (var sha1 = new SHA1Hash()) - { - return sha1.ComputeHash(hashData, 0, hashData.Length); - } - } - - /// - /// Sends SSH message to the server - /// - /// The message. - protected void SendMessage(Message message) - { - this.Session.SendMessage(message); - } - - /// - /// Generates the session key. - /// - /// The shared key. - /// The exchange hash. - /// The key. - /// The size. - /// - private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, byte[] key, int size) - { - var result = new List(key); - while (size > result.Count) - { - result.AddRange(this.Hash(new _SessionKeyAdjustment - { - SharedKey = sharedKey, - ExcahngeHash = exchangeHash, - Key = key, - }.GetBytes())); - } - - return result.ToArray(); - } - - /// - /// Generates the session key. - /// - /// The shared key. - /// The exchange hash. - /// The p. - /// The session id. - /// - private byte[] GenerateSessionKey(BigInteger sharedKey, byte[] exchangeHash, char p, byte[] sessionId) - { - return new _SessionKeyGeneration - { - SharedKey = sharedKey, - ExchangeHash = exchangeHash, - Char = p, - SessionId = sessionId, - }.GetBytes(); - } - - private class _SessionKeyGeneration : SshData - { - public BigInteger SharedKey { get; set; } - public byte[] ExchangeHash { get; set; } - public char Char { get; set; } - public byte[] SessionId { get; set; } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.SharedKey); - this.Write(this.ExchangeHash); - this.Write((byte)this.Char); - this.Write(this.SessionId); - } - } - - private class _SessionKeyAdjustment : SshData - { - public BigInteger SharedKey { get; set; } - public byte[] ExcahngeHash { get; set; } - public byte[] Key { get; set; } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.SharedKey); - this.Write(this.ExcahngeHash); - this.Write(this.Key); - } - } - - #region IDisposable Members - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~KeyExchange() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs deleted file mode 100644 index 20b2f1c..0000000 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security -{ - /// - /// Represents base class for Diffie Hellman key exchange algorithm - /// - public abstract class KeyExchangeDiffieHellman : KeyExchange - { - /// - /// Specifies key exchange group number. - /// - protected BigInteger _group; - - /// - /// Specifies key exchange prime number. - /// - protected BigInteger _prime; - - /// - /// Specifies client payload - /// - protected byte[] _clientPayload; - - /// - /// Specifies server payload - /// - protected byte[] _serverPayload; - - /// - /// Specifies client exchange number. - /// - protected BigInteger _clientExchangeValue; - - /// - /// Specifies server exchange number. - /// - protected BigInteger _serverExchangeValue; - - /// - /// Specifies random generated number. - /// - protected BigInteger _randomValue; - - /// - /// Specifies host key data. - /// - protected byte[] _hostKey; - - /// - /// Specifies signature data. - /// - protected byte[] _signature; - - /// - /// Validates the exchange hash. - /// - /// - /// true if exchange hash is valid; otherwise false. - /// - protected override bool ValidateExchangeHash() - { - var exchangeHash = this.CalculateHash(); - - var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]); - - var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length); - - var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey); - - this.Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; - - if (this.CanTrustHostKey(key)) - { - - return key.VerifySignature(exchangeHash, this._signature); - } - return false; - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this._serverPayload = message.GetBytes().ToArray(); - this._clientPayload = this.Session.ClientInitMessage.GetBytes().ToArray(); - } - - /// - /// Populates the client exchange value. - /// - protected void PopulateClientExchangeValue() - { - if (this._group.IsZero) - throw new ArgumentNullException("_group"); - - if (this._prime.IsZero) - throw new ArgumentNullException("_prime"); - - var bitLength = this._prime.BitLength; - - do - { - this._randomValue = BigInteger.Random(bitLength); - - this._clientExchangeValue = BigInteger.ModPow(this._group, this._randomValue, this._prime); - - } while (this._clientExchangeValue < 1 || this._clientExchangeValue > ((this._prime - 1))); - } - - /// - /// Handles the server DH reply message. - /// - /// The host key. - /// The server exchange value. - /// The signature. - protected virtual void HandleServerDhReply(byte[] hostKey, BigInteger serverExchangeValue, byte[] signature) - { - this._serverExchangeValue = serverExchangeValue; - this._hostKey = hostKey; - this.SharedKey = BigInteger.ModPow(serverExchangeValue, this._randomValue, this._prime); - this._signature = signature; - } - } -} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs deleted file mode 100644 index a64efbf..0000000 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup14Sha1.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Renci.SshNet.Common; -using System.Globalization; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group14-sha1" algorithm implementation. - /// - internal class KeyExchangeDiffieHellmanGroup14Sha1 : KeyExchangeDiffieHellmanGroupSha1 - { - private const string SecondOkleyGroup = "00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"; - - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group14-sha1"; } - } - - /// - /// Gets the group prime. - /// - /// - /// The group prime. - /// - public override BigInteger GroupPrime - { - get - { - BigInteger prime; - BigInteger.TryParse(SecondOkleyGroup, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out prime); - return prime; - } - } - } -} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs deleted file mode 100644 index 25613a4..0000000 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroup1Sha1.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Renci.SshNet.Common; -using System.Globalization; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group1-sha1" algorithm implementation. - /// - public class KeyExchangeDiffieHellmanGroup1Sha1 : KeyExchangeDiffieHellmanGroupSha1 - { - private const string SecondOkleyGroup = @"00FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"; - - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group1-sha1"; } - } - - /// - /// Gets the group prime. - /// - /// - /// The group prime. - /// - public override BigInteger GroupPrime - { - get - { - BigInteger prime; - BigInteger.TryParse(SecondOkleyGroup, NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out prime); - return prime; - } - } - } -} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs deleted file mode 100644 index 2e91396..0000000 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha1.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group-exchange-sha1" algorithm implementation. - /// - internal class KeyExchangeDiffieHellmanGroupExchangeSha1 : KeyExchangeDiffieHellman - { - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group-exchange-sha1"; } - } - - /// - /// Calculates key exchange hash value. - /// - /// - /// Key exchange hash. - /// - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - MinimumGroupSize = 1024, - PreferredGroupSize = 1024, - MaximumGroupSize = 1024, - Prime = this._prime, - SubGroup = this._group, - ClientExchangeValue = this._clientExchangeValue, - ServerExchangeValue = this._serverExchangeValue, - SharedKey = this.SharedKey, - }.GetBytes(); - - return this.Hash(hashData); - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - // 1. send SSH_MSG_KEY_DH_GEX_REQUEST - this.SendMessage(new KeyExchangeDhGroupExchangeRequest(1024, 1024, 1024)); - } - - /// - /// Finishes key exchange algorithm. - /// - public override void Finish() - { - base.Finish(); - - this.Session.MessageReceived -= Session_MessageReceived; - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var groupMessage = e.Message as KeyExchangeDhGroupExchangeGroup; - - if (groupMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - - // 2. Receive SSH_MSG_KEX_DH_GEX_GROUP - this._prime = groupMessage.SafePrime; - this._group = groupMessage.SubGroup; - - this.PopulateClientExchangeValue(); - - // 3. Send SSH_MSG_KEX_DH_GEX_INIT - this.SendMessage(new KeyExchangeDhGroupExchangeInit(this._clientExchangeValue)); - - } - var replyMessage = e.Message as KeyExchangeDhGroupExchangeReply; - - if (replyMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.HandleServerDhReply(replyMessage.HostKey, replyMessage.F, replyMessage.Signature); - - // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed - this.Finish(); - } - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public UInt32 MinimumGroupSize { get; set; } - - public UInt32 PreferredGroupSize { get; set; } - - public UInt32 MaximumGroupSize { get; set; } - - public BigInteger Prime { get; set; } - - public BigInteger SubGroup { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.MinimumGroupSize); - this.Write(this.PreferredGroupSize); - this.Write(this.MaximumGroupSize); - this.Write(this.Prime); - this.Write(this.SubGroup); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - } -} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs deleted file mode 100644 index 5cc9c23..0000000 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupExchangeSha256.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group-exchange-sha256" algorithm implementation. - /// - public class KeyExchangeDiffieHellmanGroupExchangeSha256 : KeyExchangeDiffieHellman - { - /// - /// Gets algorithm name. - /// - public override string Name - { - get { return "diffie-hellman-group-exchange-sha256"; } - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - this.Session.RegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - // 1. send SSH_MSG_KEY_DH_GEX_REQUEST - this.SendMessage(new KeyExchangeDhGroupExchangeRequest(1024,1024,1024)); - - } - - /// - /// Finishes key exchange algorithm. - /// - public override void Finish() - { - base.Finish(); - - this.Session.MessageReceived -= Session_MessageReceived; - } - - /// - /// Calculates key exchange hash value. - /// - /// - /// Key exchange hash. - /// - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - MinimumGroupSize = 1024, - PreferredGroupSize = 1024, - MaximumGroupSize = 1024, - Prime = this._prime, - SubGroup = this._group, - ClientExchangeValue = this._clientExchangeValue, - ServerExchangeValue = this._serverExchangeValue, - SharedKey = this.SharedKey, - }.GetBytes(); - - return this.Hash(hashData); - } - - /// - /// Hashes the specified data bytes. - /// - /// Data to hash. - /// - /// Hashed bytes - /// - protected override byte[] Hash(byte[] hashBytes) - { - using (var md = new SHA256Hash()) - { - return md.ComputeHash(hashBytes); - } - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var groupMessage = e.Message as KeyExchangeDhGroupExchangeGroup; - - if (groupMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_GROUP"); - - // 2. Receive SSH_MSG_KEX_DH_GEX_GROUP - this._prime = groupMessage.SafePrime; - this._group = groupMessage.SubGroup; - - this.PopulateClientExchangeValue(); - - // 3. Send SSH_MSG_KEX_DH_GEX_INIT - this.SendMessage(new KeyExchangeDhGroupExchangeInit(this._clientExchangeValue)); - } - var replyMessage = e.Message as KeyExchangeDhGroupExchangeReply; - - if (replyMessage != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEX_DH_GEX_REPLY"); - - this.HandleServerDhReply(replyMessage.HostKey, replyMessage.F, replyMessage.Signature); - - // When SSH_MSG_KEX_DH_GEX_REPLY received key exchange is completed - this.Finish(); - } - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public UInt32 MinimumGroupSize { get; set; } - - public UInt32 PreferredGroupSize { get; set; } - - public UInt32 MaximumGroupSize { get; set; } - - public BigInteger Prime { get; set; } - - public BigInteger SubGroup { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.MinimumGroupSize); - this.Write(this.PreferredGroupSize); - this.Write(this.MaximumGroupSize); - this.Write(this.Prime); - this.Write(this.SubGroup); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - } -} diff --git a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs b/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs deleted file mode 100644 index d66bff5..0000000 --- a/Renci.SshNet/Security/KeyExchangeDiffieHellmanGroupSha1.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Renci.SshNet.Common; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Transport; - -namespace Renci.SshNet.Security -{ - /// - /// Represents "diffie-hellman-group1-sha1" algorithm implementation. - /// - public abstract class KeyExchangeDiffieHellmanGroupSha1 : KeyExchangeDiffieHellman - { - /// - /// Gets the group prime. - /// - /// - /// The group prime. - /// - public abstract BigInteger GroupPrime { get; } - - /// - /// Calculates key exchange hash value. - /// - /// - /// Key exchange hash. - /// - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - ClientExchangeValue = this._clientExchangeValue, - ServerExchangeValue = this._serverExchangeValue, - SharedKey = this.SharedKey, - }.GetBytes(); - - return this.Hash(hashData); - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this.Session.RegisterMessage("SSH_MSG_KEXDH_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - this._prime = this.GroupPrime; - - this._group = new BigInteger(new byte[] { 2 }); - - this.PopulateClientExchangeValue(); - - this.SendMessage(new KeyExchangeDhInitMessage(this._clientExchangeValue)); - - } - - /// - /// Finishes key exchange algorithm. - /// - public override void Finish() - { - base.Finish(); - - this.Session.MessageReceived -= Session_MessageReceived; - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var message = e.Message as KeyExchangeDhReplyMessage; - if (message != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEXDH_REPLY"); - - this.HandleServerDhReply(message.HostKey, message.F, message.Signature); - - // When SSH_MSG_KEXDH_REPLY received key exchange is completed - this.Finish(); - } - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new System.NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - } -} diff --git a/Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs b/Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs deleted file mode 100644 index 50a2cb5..0000000 --- a/Renci.SshNet/Security/KeyExchangeEllipticCurveDiffieHellman.cs +++ /dev/null @@ -1,279 +0,0 @@ -using System; -using System.Linq; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Messages; -using Renci.SshNet.Common; -using System.Globalization; - -namespace Renci.SshNet.Security -{ - /// - /// Represents base class for Diffie Hellman key exchange algorithm - /// - public class KeyExchangeEllipticCurveDiffieHellman : KeyExchange - { - /// - /// Specifies client payload - /// - protected byte[] _clientPayload; - - /// - /// Specifies server payload - /// - protected byte[] _serverPayload; - - /// - /// Specifies client exchange number. - /// - protected BigInteger _clientExchangeValue; - - /// - /// Specifies server exchange number. - /// - protected BigInteger _serverExchangeValue; - - /// - /// Specifies random generated number. - /// - protected BigInteger _randomValue; - - /// - /// Specifies host key data. - /// - protected byte[] _hostKey; - - /// - /// Specifies signature data. - /// - protected byte[] _signature; - - //// 256 - //p = FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFF - //a = FFFFFFFF 00000001 00000000 00000000 00000000 FFFFFFFF FFFFFFFF FFFFFFFC - //b = 5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 3BCE3C3E 27D2604B - //S = C49D3608 86E70493 6A6678E1 139D26B7 819F7E90 - //The base point G in compressed form is: - //G = 03 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 F4A13945 D898C296 - //and in uncompressed form is: - //G = 04 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 F4A13945 D898C296 4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE CBB64068 37BF51F5 - //n = FFFFFFFF 00000000 FFFFFFFF FFFFFFFF BCE6FAAD A7179E84 F3B9CAC2 FC632551 - //h = 01 - - //// 384 - //p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF 00000000 00000000 FFFFFFFF - //a = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF 00000000 00000000 FFFFFFFC - //b = B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF - //S = A335926A A319A27A 1D00896A 6773A482 7ACDAC73 - //The base point G in compressed form is: - //G = 03 AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7 - //and in uncompressed form is: - //G = 04 AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7 3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F - //n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF C7634D81 F4372DDF 581A0DB2 48B0A77A ECEC196A CCC52973 - //h = 01 - - public override string Name - { - get { return "ecdh-sha2-nistp256"; } - } - - /// - /// Starts key exchange algorithm - /// - /// The session. - /// Key exchange init message. - public override void Start(Session session, KeyExchangeInitMessage message) - { - base.Start(session, message); - - this._serverPayload = message.GetBytes().ToArray(); - this._clientPayload = this.Session.ClientInitMessage.GetBytes().ToArray(); - - this.Session.RegisterMessage("SSH_MSG_KEXECDH_REPLY"); - - this.Session.MessageReceived += Session_MessageReceived; - - //3.2.1 Elliptic Curve Key Pair Generation Primitive - //Elliptic curve key pairs should be generated as follows: - //Input: Valid elliptic curve domain parameters T = (p, a, b, G, n, h) or (m, f(x), a, b,G, n, h). - //Output: An elliptic curve key pair (d,Q) associated with T. - //Actions: Generate an elliptic curve key pair as follows: - //1. Randomly or pseudorandomly select an integer d in the interval [1, n − 1]. - //2. Compute Q = dG. - //3. Output (d,Q). - - BigInteger p; - BigInteger.TryParse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out p); - - - - BigInteger n; - BigInteger.TryParse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out n); - BigInteger G; - BigInteger.TryParse("00036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out G); - - BigInteger d; - - do - { - d = BigInteger.Random(n.BitLength); - } while (d < 1 || d > n); - - var Q = d * G; - - - this.SendMessage(new KeyExchangeEcdhInitMessage(d, Q)); - - } - - private void Session_MessageReceived(object sender, MessageEventArgs e) - { - var message = e.Message as KeyExchangeEcdhReplyMessage; - if (message != null) - { - // Unregister message once received - this.Session.UnRegisterMessage("SSH_MSG_KEXECDH_REPLY"); - - this.HandleServerEcdhReply(); - - // When SSH_MSG_KEXDH_REPLY received key exchange is completed - this.Finish(); - } - } - - /// - /// Validates the exchange hash. - /// - /// - /// true if exchange hash is valid; otherwise false. - /// - protected override bool ValidateExchangeHash() - { - //var exchangeHash = this.CalculateHash(); - - //var length = (uint)(this._hostKey[0] << 24 | this._hostKey[1] << 16 | this._hostKey[2] << 8 | this._hostKey[3]); - - //var algorithmName = Encoding.UTF8.GetString(this._hostKey, 4, (int)length); - - //var key = this.Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](this._hostKey); - - //this.Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; - - //if (this.CanTrustHostKey(key)) - //{ - - // return key.VerifySignature(exchangeHash, this._signature); - //} - //else - //{ - // return false; - //} - - return false; - } - - /// - /// Populates the client exchange value. - /// - //protected void PopulateClientExchangeValue() - //{ - // if (this._group.IsZero) - // throw new ArgumentNullException("_group"); - - // if (this._prime.IsZero) - // throw new ArgumentNullException("_prime"); - - // var bitLength = this._prime.BitLength; - - // do - // { - // this._randomValue = BigInteger.Random(bitLength); - - // this._clientExchangeValue = BigInteger.ModPow(this._group, this._randomValue, this._prime); - - // } while (this._clientExchangeValue < 1 || this._clientExchangeValue > ((this._prime - 1))); - //} - - protected virtual void HandleServerEcdhReply() - { - //this._serverExchangeValue = serverExchangeValue; - //this._hostKey = hostKey; - //this.SharedKey = BigInteger.ModPow(serverExchangeValue, this._randomValue, this._prime); - //this._signature = signature; - } - - protected override byte[] CalculateHash() - { - var hashData = new _ExchangeHashData - { - ClientVersion = this.Session.ClientVersion, - ServerVersion = this.Session.ServerVersion, - ClientPayload = this._clientPayload, - ServerPayload = this._serverPayload, - HostKey = this._hostKey, - SharedKey = this.SharedKey, - }.GetBytes(); - - //string V_C, client's identification string (CR and LF excluded) - //string V_S, server's identification string (CR and LF excluded) - //string I_C, payload of the client's SSH_MSG_KEXINIT - //string I_S, payload of the server's SSH_MSG_KEXINIT - //string K_S, server's public host key - //string Q_C, client's ephemeral public key octet string - //string Q_S, server's ephemeral public key octet string - //mpint K, shared secret - return this.Hash(hashData); - } - - private class _ExchangeHashData : SshData - { - public string ServerVersion { get; set; } - - public string ClientVersion { get; set; } - - public byte[] ClientPayload { get; set; } - - public byte[] ServerPayload { get; set; } - - public byte[] HostKey { get; set; } - - public UInt32 MinimumGroupSize { get; set; } - - public UInt32 PreferredGroupSize { get; set; } - - public UInt32 MaximumGroupSize { get; set; } - - public BigInteger Prime { get; set; } - - public BigInteger SubGroup { get; set; } - - public BigInteger ClientExchangeValue { get; set; } - - public BigInteger ServerExchangeValue { get; set; } - - public BigInteger SharedKey { get; set; } - - protected override void LoadData() - { - throw new NotImplementedException(); - } - - protected override void SaveData() - { - this.Write(this.ClientVersion); - this.Write(this.ServerVersion); - this.WriteBinaryString(this.ClientPayload); - this.WriteBinaryString(this.ServerPayload); - this.WriteBinaryString(this.HostKey); - this.Write(this.MinimumGroupSize); - this.Write(this.PreferredGroupSize); - this.Write(this.MaximumGroupSize); - this.Write(this.Prime); - this.Write(this.SubGroup); - this.Write(this.ClientExchangeValue); - this.Write(this.ServerExchangeValue); - this.Write(this.SharedKey); - } - } - - } -} diff --git a/Renci.SshNet/Security/KeyHostAlgorithm.cs b/Renci.SshNet/Security/KeyHostAlgorithm.cs deleted file mode 100644 index 261b882..0000000 --- a/Renci.SshNet/Security/KeyHostAlgorithm.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Security -{ - /// - /// Implements key support for host algorithm. - /// - public class KeyHostAlgorithm : HostAlgorithm - { - - /// - /// Gets the key. - /// - public Key Key { get; private set; } - - /// - /// Gets the public key data. - /// - public override byte[] Data - { - get - { - return new SshKeyData(this.Name, this.Key.Public).GetBytes(); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// Host key name. - /// Host key. - public KeyHostAlgorithm(string name, Key key) - : base(name) - { - this.Key = key; - } - - /// - /// Initializes a new instance of the class. - /// - /// Host key name. - /// Host key. - /// Host key encoded data. - public KeyHostAlgorithm(string name, Key key, byte[] data) - : base(name) - { - this.Key = key; - - var sshKey = new SshKeyData(); - sshKey.Load(data); - this.Key.Public = sshKey.Keys; - } - - /// - /// Signs the specified data. - /// - /// The data. - /// - /// Signed data. - /// - public override byte[] Sign(byte[] data) - { - return new SignatureKeyData(this.Name, this.Key.Sign(data)).GetBytes(); - } - - /// - /// Verifies the signature. - /// - /// The data. - /// The signature. - /// - /// True is signature was successfully verifies; otherwise false. - /// - public override bool VerifySignature(byte[] data, byte[] signature) - { - var signatureData = new SignatureKeyData(); - signatureData.Load(signature); - - return this.Key.VerifySignature(data, signatureData.Signature); - } - - private class SshKeyData : SshData - { - public BigInteger[] Keys { get; private set; } - - public string Name { get; private set; } - - public SshKeyData() - { - - } - - public SshKeyData(string name, params BigInteger[] keys) - { - this.Name = name; - this.Keys = keys; - } - - protected override void LoadData() - { - this.Name = this.ReadString(); - var keys = new List(); - while (!this.IsEndOfData) - { - keys.Add(this.ReadBigInt()); - } - this.Keys = keys.ToArray(); - } - - protected override void SaveData() - { - this.Write(this.Name); - foreach (var key in this.Keys) - { - this.Write(key); - } - } - } - - private class SignatureKeyData : SshData - { - /// - /// Gets or sets the name of the algorithm. - /// - /// - /// The name of the algorithm. - /// - public string AlgorithmName { get; private set; } - - /// - /// Gets or sets the signature. - /// - /// - /// The signature. - /// - public byte[] Signature { get; private set; } - - public SignatureKeyData() - { - - } - - public SignatureKeyData(string name, byte[] signature) - { - this.AlgorithmName = name; - this.Signature = signature; - } - - /// - /// Called when type specific data need to be loaded. - /// - protected override void LoadData() - { - this.AlgorithmName = this.ReadString(); - this.Signature = this.ReadBinaryString(); - } - - /// - /// Called when type specific data need to be saved. - /// - protected override void SaveData() - { - this.Write(this.AlgorithmName); - this.WriteBinaryString(this.Signature); - } - } - } -} diff --git a/Renci.SshNet/Session.NET.cs b/Renci.SshNet/Session.NET.cs deleted file mode 100644 index 7ffc9c9..0000000 --- a/Renci.SshNet/Session.NET.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System.Linq; -using System; -using System.Net.Sockets; -using System.Net; -using Renci.SshNet.Common; -using System.Threading; -using Renci.SshNet.Messages.Transport; -using System.Diagnostics; -using System.Collections.Generic; - -namespace Renci.SshNet -{ - public partial class Session - { - private readonly TraceSource _log = -#if DEBUG - new TraceSource("SshNet.Logging", SourceLevels.All); -#else - new TraceSource("SshNet.Logging"); -#endif - - /// - /// Gets a value indicating whether the socket is connected. - /// - /// - /// true if the socket is connected; otherwise, false. - /// - partial void IsSocketConnected(ref bool isConnected) - { - isConnected = (_socket != null && _socket.Connected); - if (isConnected) - { - var connectionClosedOrDataAvailable = _socket.Poll(1000, SelectMode.SelectRead); - isConnected = !(connectionClosedOrDataAvailable && _socket.Available == 0); - } - } - - partial void SocketConnect(string host, int port) - { - const int socketBufferSize = 2 * MaximumSshPacketSize; - - var addr = host.GetIPAddress(); - - var ep = new IPEndPoint(addr, port); - this._socket = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - this._socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, socketBufferSize); - this._socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, socketBufferSize); - - this.Log(string.Format("Initiating connect to '{0}:{1}'.", this.ConnectionInfo.Host, this.ConnectionInfo.Port)); - - // Connect socket with specified timeout - var connectResult = this._socket.BeginConnect(ep, null, null); - - if (!connectResult.AsyncWaitHandle.WaitOne(this.ConnectionInfo.Timeout, false)) - { - throw new SshOperationTimeoutException("Connection Could Not Be Established"); - } - - this._socket.EndConnect(connectResult); - } - - partial void SocketDisconnect() - { - _socket.Disconnect(true); - } - - partial void SocketReadLine(ref string response) - { - var encoding = new ASCIIEncoding(); - - // Read data one byte at a time to find end of line and leave any unhandled information in the buffer to be processed later - var buffer = new List(); - - var data = new byte[1]; - do - { - var asyncResult = this._socket.BeginReceive(data, 0, data.Length, SocketFlags.None, null, null); - - if (!asyncResult.AsyncWaitHandle.WaitOne(this.ConnectionInfo.Timeout)) - throw new SshOperationTimeoutException("Socket read operation has timed out"); - - var received = this._socket.EndReceive(asyncResult); - - // If zero bytes received then exit - if (received == 0) - break; - - buffer.Add(data[0]); - } - while (!(buffer.Count > 0 && (buffer[buffer.Count - 1] == 0x0A || buffer[buffer.Count - 1] == 0x00))); - - // Return an empty version string if the buffer consists of a 0x00 character. - if (buffer.Count > 0 && buffer[buffer.Count - 1] == 0x00) - { - response = string.Empty; - } - else if (buffer.Count > 1 && buffer[buffer.Count - 2] == 0x0D) - response = encoding.GetString(buffer.Take(buffer.Count - 2).ToArray()); - else - response = encoding.GetString(buffer.Take(buffer.Count - 1).ToArray()); - } - - /// - /// Function to read amount of data before returning, or throwing an exception. - /// - /// The amount wanted. - /// The buffer to read to. - /// Happens when the socket is closed. - /// Unhandled exception. - partial void SocketRead(int length, ref byte[] buffer) - { - var receivedTotal = 0; // how many bytes is already received - - do - { - try - { - var receivedBytes = this._socket.Receive(buffer, receivedTotal, length - receivedTotal, SocketFlags.None); - if (receivedBytes > 0) - { - receivedTotal += receivedBytes; - continue; - } - - // 2012-09-11: Kenneth_aa - // When Disconnect or Dispose is called, this throws SshConnectionException(), which... - // 1 - goes up to ReceiveMessage() - // 2 - up again to MessageListener() - // which is where there is a catch-all exception block so it can notify event listeners. - // 3 - MessageListener then again calls RaiseError(). - // There the exception is checked for the exception thrown here (ConnectionLost), and if it matches it will not call Session.SendDisconnect(). - // - // Adding a check for this._isDisconnecting causes ReceiveMessage() to throw SshConnectionException: "Bad packet length {0}". - // - - if (_isDisconnecting) - throw new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); - throw new SshConnectionException("An established connection was aborted by the server.", DisconnectReason.ConnectionLost); - } - catch (SocketException exp) - { - if (exp.SocketErrorCode == SocketError.ConnectionAborted) - { - buffer = new byte[length]; - this.Disconnect(DisconnectReason.ConnectionLost, "An established connection was aborted by the server."); - return; - } - - if (exp.SocketErrorCode == SocketError.WouldBlock || - exp.SocketErrorCode == SocketError.IOPending || - exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable) - { - // socket buffer is probably empty, wait and try again - Thread.Sleep(30); - } - else - throw; // any serious error occurred - } - } while (receivedTotal < length); - } - - partial void SocketWrite(byte[] data) - { - var sent = 0; // how many bytes is already sent - var length = data.Length; - - do - { - try - { - sent += this._socket.Send(data, sent, length - sent, SocketFlags.None); - } - catch (SocketException ex) - { - if (ex.SocketErrorCode == SocketError.WouldBlock || - ex.SocketErrorCode == SocketError.IOPending || - ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable) - { - // socket buffer is probably full, wait and try again - Thread.Sleep(30); - } - else - throw; // any serious error occurr - } - } while (sent < length); - } - - [Conditional("DEBUG")] - partial void Log(string text) - { - this._log.TraceEvent(TraceEventType.Verbose, 1, text); - } - } -} diff --git a/Renci.SshNet/Session.NET40.cs b/Renci.SshNet/Session.NET40.cs deleted file mode 100644 index 7d286a2..0000000 --- a/Renci.SshNet/Session.NET40.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Threading.Tasks; -using System.Linq; -using System; -using Renci.SshNet.Messages; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Provides functionality to connect and interact with SSH server. - /// - public partial class Session - { - partial void HandleMessageCore(Message message) - { - this.HandleMessage((dynamic)message); - } - - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - - partial void InternalRegisterMessage(string messageName) - { - lock (this._messagesMetadata) - { - Parallel.ForEach( - from m in this._messagesMetadata where m.Name == messageName select m, - item => { item.Enabled = true; item.Activated = true; }); - } - } - - partial void InternalUnRegisterMessage(string messageName) - { - lock (this._messagesMetadata) - { - Parallel.ForEach( - from m in this._messagesMetadata where m.Name == messageName select m, - item => { item.Enabled = false; item.Activated = false; }); - } - } - } -} diff --git a/Renci.SshNet/Session.cs b/Renci.SshNet/Session.cs deleted file mode 100644 index 9caa472..0000000 --- a/Renci.SshNet/Session.cs +++ /dev/null @@ -1,2126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using Renci.SshNet.Compression; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Authentication; -using Renci.SshNet.Messages.Connection; -using Renci.SshNet.Messages.Transport; -using Renci.SshNet.Security; -using System.Globalization; -using Renci.SshNet.Security.Cryptography; - -namespace Renci.SshNet -{ - /// - /// Provides functionality to connect and interact with SSH server. - /// - public partial class Session : IDisposable - { - /// - /// Specifies maximum packet size defined by the protocol. - /// - private const int MaximumSshPacketSize = LocalChannelDataPacketSize + 3000; - - /// - /// Holds the initial local window size for the channels. - /// - /// - /// 2 MB. - /// - private const int InitialLocalWindowSize = LocalChannelDataPacketSize * 32; - - /// - /// Holds the maximum size of channel data packets that we receive. - /// - /// - /// 64 KB. - /// - private const int LocalChannelDataPacketSize = 1024*64; - - private static readonly RNGCryptoServiceProvider Randomizer = new RNGCryptoServiceProvider(); - -#if SILVERLIGHT - private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+)( SP.+)?$"); -#else - private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+)( SP.+)?$", RegexOptions.Compiled); -#endif - - /// - /// Controls how many authentication attempts can take place at the same time. - /// - /// - /// Some server may restrict number to prevent authentication attacks - /// - private static readonly SemaphoreLight AuthenticationConnection = new SemaphoreLight(3); - - /// - /// Holds metada about session messages - /// - private IEnumerable _messagesMetadata; - - /// - /// Holds connection socket. - /// - private Socket _socket; - - /// - /// Holds locker object for the socket - /// - private readonly object _socketLock = new object(); - - /// - /// Holds reference to task that listens for incoming messages - /// - private EventWaitHandle _messageListenerCompleted; - - /// - /// Specifies outbound packet number - /// - private volatile UInt32 _outboundPacketSequence; - - /// - /// Specifies incoming packet number - /// - private UInt32 _inboundPacketSequence; - - /// - /// WaitHandle to signal that last service request was accepted - /// - private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); - - /// - /// WaitHandle to signal that exception was thrown by another thread. - /// - private EventWaitHandle _exceptionWaitHandle = new ManualResetEvent(false); - - /// - /// WaitHandle to signal that key exchange was completed. - /// - private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent(false); - - /// - /// WaitHandle to signal that key exchange is in progress. - /// - private bool _keyExchangeInProgress; - - /// - /// Exception that need to be thrown by waiting thread - /// - private Exception _exception; - - /// - /// Specifies whether connection is authenticated - /// - private bool _isAuthenticated; - - /// - /// Specifies whether user issued Disconnect command or not - /// - private bool _isDisconnecting; - - private KeyExchange _keyExchange; - - private HashAlgorithm _serverMac; - - private HashAlgorithm _clientMac; - - private Cipher _clientCipher; - - private Cipher _serverCipher; - - private Compressor _serverDecompression; - - private Compressor _clientCompression; - - private SemaphoreLight _sessionSemaphore; - - /// - /// Gets the session semaphore that controls session channels. - /// - /// The session semaphore. - public SemaphoreLight SessionSemaphore - { - get - { - if (this._sessionSemaphore == null) - { - lock (this) - { - if (this._sessionSemaphore == null) - { - this._sessionSemaphore = new SemaphoreLight(this.ConnectionInfo.MaxSessions); - } - } - } - - return this._sessionSemaphore; - } - } - - private bool _isDisconnectMessageSent; - - private uint _nextChannelNumber; - /// - /// Gets the next channel number. - /// - /// The next channel number. - internal uint NextChannelNumber - { - get - { - uint result; - - lock (this) - { - result = this._nextChannelNumber++; - } - - return result; - } - } - - /// - /// Gets a value indicating whether the session is connected. - /// - /// - /// true if the session is connected; otherwise, false. - /// - /// - /// This methods returns true in all but the following cases: - /// - /// - /// The SSH_MSG_DISCONNECT message - which is used to disconnect from the server - has been sent. - /// - /// - /// The client has not been authenticated successfully. - /// - /// - /// The listener thread - which is used to receive messages from the server - has stopped. - /// - /// - /// The socket used to communicate with the server is no longer connected. - /// - /// - /// - public bool IsConnected - { - get - { - if (_isDisconnectMessageSent || !this._isAuthenticated) - return false; - if (_messageListenerCompleted == null || _messageListenerCompleted.WaitOne(0)) - return false; - - var isSocketConnected = false; - IsSocketConnected(ref isSocketConnected); - return isSocketConnected; - } - } - - /// - /// Gets the session id. - /// - /// - /// The session id, or null if the client has not been authenticated. - /// - public byte[] SessionId { get; private set; } - - private Message _clientInitMessage; - /// - /// Gets the client init message. - /// - /// The client init message. - public Message ClientInitMessage - { - get - { - if (this._clientInitMessage == null) - { - this._clientInitMessage = new KeyExchangeInitMessage - { - KeyExchangeAlgorithms = this.ConnectionInfo.KeyExchangeAlgorithms.Keys.ToArray(), - ServerHostKeyAlgorithms = this.ConnectionInfo.HostKeyAlgorithms.Keys.ToArray(), - EncryptionAlgorithmsClientToServer = this.ConnectionInfo.Encryptions.Keys.ToArray(), - EncryptionAlgorithmsServerToClient = this.ConnectionInfo.Encryptions.Keys.ToArray(), - MacAlgorithmsClientToServer = this.ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - MacAlgorithmsServerToClient = this.ConnectionInfo.HmacAlgorithms.Keys.ToArray(), - CompressionAlgorithmsClientToServer = this.ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - CompressionAlgorithmsServerToClient = this.ConnectionInfo.CompressionAlgorithms.Keys.ToArray(), - LanguagesClientToServer = new[] {string.Empty}, - LanguagesServerToClient = new[] {string.Empty}, - FirstKexPacketFollows = false, - Reserved = 0 - }; - } - return this._clientInitMessage; - } - } - - /// - /// Gets or sets the server version string. - /// - /// The server version. - public string ServerVersion { get; private set; } - - /// - /// Gets or sets the client version string. - /// - /// The client version. - public string ClientVersion { get; private set; } - - /// - /// Gets or sets the connection info. - /// - /// The connection info. - public ConnectionInfo ConnectionInfo { get; private set; } - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccured; - - /// - /// Occurs when session has been disconnected form the server. - /// - public event EventHandler Disconnected; - - /// - /// Occurs when host key received. - /// - public event EventHandler HostKeyReceived; - - #region Message events - - /// - /// Occurs when message received - /// - internal event EventHandler> DisconnectReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> IgnoreReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UnimplementedReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> DebugReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ServiceRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ServiceAcceptReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> KeyExchangeInitReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> NewKeysReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationFailureReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationSuccessReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> UserAuthenticationBannerReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> GlobalRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> RequestSuccessReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> RequestFailureReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelOpenReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelOpenConfirmationReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelOpenFailureReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelWindowAdjustReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelDataReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelExtendedDataReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelEofReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelCloseReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelRequestReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelSuccessReceived; - - /// - /// Occurs when message received - /// - internal event EventHandler> ChannelFailureReceived; - - /// - /// Occurs when message received and is not handled by any of the event handlers - /// - internal event EventHandler> MessageReceived; - - #endregion - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// is null. - internal Session(ConnectionInfo connectionInfo) - { - if (connectionInfo == null) - throw new ArgumentNullException("connectionInfo"); - - this.ConnectionInfo = connectionInfo; - //this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.{0}", this.GetType().Assembly.GetName().Version); - this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.0.0.1"); - } - - /// - /// Connects to the server. - /// - public void Connect() - { - if (this.IsConnected) - return; - - try - { - AuthenticationConnection.Wait(); - - if (this.IsConnected) - return; - - lock (this) - { - // If connected don't connect again - if (this.IsConnected) - return; - - // reset connection specific information - Reset(); - - // Build list of available messages while connecting - this._messagesMetadata = GetMessagesMetadata(); - - switch (this.ConnectionInfo.ProxyType) - { - case ProxyTypes.None: - this.SocketConnect(this.ConnectionInfo.Host, this.ConnectionInfo.Port); - break; - case ProxyTypes.Socks4: - this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); - this.ConnectSocks4(); - break; - case ProxyTypes.Socks5: - this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); - this.ConnectSocks5(); - break; - case ProxyTypes.Http: - this.SocketConnect(this.ConnectionInfo.ProxyHost, this.ConnectionInfo.ProxyPort); - this.ConnectHttp(); - break; - } - - Match versionMatch; - - // Get server version from the server, - // ignore text lines which are sent before if any - while (true) - { - string serverVersion = string.Empty; - this.SocketReadLine(ref serverVersion); - - this.ServerVersion = serverVersion; - if (string.IsNullOrEmpty(this.ServerVersion)) - { - throw new InvalidOperationException("Server string is null or empty."); - } - - versionMatch = ServerVersionRe.Match(this.ServerVersion); - - if (versionMatch.Success) - { - break; - } - } - - // Set connection versions - this.ConnectionInfo.ServerVersion = this.ServerVersion; - this.ConnectionInfo.ClientVersion = this.ClientVersion; - - // Get server SSH version - var version = versionMatch.Result("${protoversion}"); - - var softwareName = versionMatch.Result("${softwareversion}"); - - this.Log(string.Format("Server version '{0}' on '{1}'.", version, softwareName)); - - if (!(version.Equals("2.0") || version.Equals("1.99"))) - { - throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Server version '{0}' is not supported.", version), DisconnectReason.ProtocolVersionNotSupported); - } - - this.SocketWrite(Encoding.UTF8.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}\x0D\x0A", this.ClientVersion))); - - // Register Transport response messages - this.RegisterMessage("SSH_MSG_DISCONNECT"); - this.RegisterMessage("SSH_MSG_IGNORE"); - this.RegisterMessage("SSH_MSG_UNIMPLEMENTED"); - this.RegisterMessage("SSH_MSG_DEBUG"); - this.RegisterMessage("SSH_MSG_SERVICE_ACCEPT"); - this.RegisterMessage("SSH_MSG_KEXINIT"); - this.RegisterMessage("SSH_MSG_NEWKEYS"); - - // Some server implementations might sent this message first, prior establishing encryption algorithm - this.RegisterMessage("SSH_MSG_USERAUTH_BANNER"); - - // Start incoming request listener - this._messageListenerCompleted = new ManualResetEvent(false); - - this.ExecuteThread(() => - { - try - { - this.MessageListener(); - } - finally - { - this._messageListenerCompleted.Set(); - } - }); - - // Wait for key exchange to be completed - this.WaitOnHandle(this._keyExchangeCompletedWaitHandle); - - // If sessionId is not set then its not connected - if (this.SessionId == null) - { - this.Disconnect(); - return; - } - - // Request user authorization service - this.SendMessage(new ServiceRequestMessage(ServiceName.UserAuthentication)); - - // Wait for service to be accepted - this.WaitOnHandle(this._serviceAccepted); - - if (string.IsNullOrEmpty(this.ConnectionInfo.Username)) - { - throw new SshException("Username is not specified."); - } - - this.ConnectionInfo.Authenticate(this); - this._isAuthenticated = true; - - // Register Connection messages - this.RegisterMessage("SSH_MSG_GLOBAL_REQUEST"); - this.RegisterMessage("SSH_MSG_REQUEST_SUCCESS"); - this.RegisterMessage("SSH_MSG_REQUEST_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_CONFIRMATION"); - this.RegisterMessage("SSH_MSG_CHANNEL_OPEN_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_WINDOW_ADJUST"); - this.RegisterMessage("SSH_MSG_CHANNEL_EXTENDED_DATA"); - this.RegisterMessage("SSH_MSG_CHANNEL_REQUEST"); - this.RegisterMessage("SSH_MSG_CHANNEL_SUCCESS"); - this.RegisterMessage("SSH_MSG_CHANNEL_FAILURE"); - this.RegisterMessage("SSH_MSG_CHANNEL_DATA"); - this.RegisterMessage("SSH_MSG_CHANNEL_EOF"); - this.RegisterMessage("SSH_MSG_CHANNEL_CLOSE"); - - Monitor.Pulse(this); - } - } - finally - { - AuthenticationConnection.Release(); - } - } - - /// - /// Disconnects from the server. - /// - /// - /// This sends a SSH_MSG_DISCONNECT message to the server, waits for the - /// server to close the socket on its end and subsequently closes the client socket. - /// - public void Disconnect() - { - Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client."); - } - - private void Disconnect(DisconnectReason reason, string message) - { - this._isDisconnecting = true; - - // send disconnect message to the server if the connection is still open - // and the disconnect message has not yet been sent - // - // note that this should also cause the listener thread to be stopped as - // the server should respond by closing the socket - SendDisconnect(reason, message); - - // disconnect socket, and dispose it - SocketDisconnectAndDispose(); - - if (reason == DisconnectReason.ConnectionLost) - { - var disconnected = Disconnected; - if (disconnected != null) - disconnected(this, new EventArgs()); - } else if ( _messageListenerCompleted != null) - { - // at this point, we are sure that the listener thread will stop - // as we've disconnected the socket - _messageListenerCompleted.WaitOne(); - _messageListenerCompleted.Dispose(); - _messageListenerCompleted = null; - } - } - - internal T CreateClientChannel() where T : ClientChannel, new() - { - var channel = new T(); - lock (this) - { - channel.Initialize(this, InitialLocalWindowSize, LocalChannelDataPacketSize); - } - return channel; - } - - internal T CreateServerChannel(uint remoteChannelNumber, uint remoteWindowSize, uint remoteChannelDataPacketSize) where T : ServerChannel, new() - { - var channel = new T(); - lock (this) - { - channel.Initialize(this, InitialLocalWindowSize, LocalChannelDataPacketSize, remoteChannelNumber, remoteWindowSize, - remoteChannelDataPacketSize); - } - return channel; - } - - /// - /// Sends "keep alive" message to keep connection alive. - /// - internal void SendKeepAlive() - { - this.SendMessage(new IgnoreMessage()); - } - - /// - /// Waits for the specified handle or the exception handle for the receive thread - /// to signal within the connection timeout. - /// - /// The wait handle. - /// A received package was invalid or failed the message integrity check. - /// None of the handles are signaled in time and the session is not disconnecting. - /// A socket error was signaled while receiving messages from the server. - /// - /// When neither handles are signaled in time and the session is not closing, then the - /// session is disconnected. - /// - /// - internal void WaitOnHandle(WaitHandle waitHandle) - { - var waitHandles = new[] - { - this._exceptionWaitHandle, - this._messageListenerCompleted, - waitHandle - }; - - switch (WaitHandle.WaitAny(waitHandles, ConnectionInfo.Timeout)) - { - case 0: - throw this._exception; - case 1: - // when the session is NOT disconnecting, the listener should actually - // never complete without setting the exception wait handle and should - // end up in case 0... - // - // when the session is disconnecting, the completion of the listener - // should not be considered an error (quite the oppposite actually) - if (!_isDisconnecting) - { - throw new SshConnectionException("Client not connected."); - } - break; - case WaitHandle.WaitTimeout: - // when the session is NOT disconnecting, then we want to disconnect - // the session altogether in case of a timeout, and throw a - // SshOperationTimeoutException - // - // when the session is disconnecting, a timeout is likely when no - // network connectivity is available; depending on the configured - // timeout either the WaitAny times out first or a SocketException - // detailing a timeout thrown hereby completing the listener thread - // (which makes us end up in case 1). Either way, we do not want to - // disconnect while we're already disconnecting and we do not want - // to report an exception to the client when we're disconnecting - // anyway - if (!_isDisconnecting) - { - this.Disconnect(DisconnectReason.ByApplication, "Operation timeout"); - throw new SshOperationTimeoutException("Session operation has timed out"); - } - break; - } - } - - /// - /// Sends packet message to the server. - /// - /// The message. - internal void SendMessage(Message message) - { - if (this._socket == null || !this._socket.CanWrite()) - throw new SshConnectionException("Client not connected."); - - if (this._keyExchangeInProgress && !(message is IKeyExchangedAllowed)) - { - // Wait for key exchange to be completed - this.WaitOnHandle(this._keyExchangeCompletedWaitHandle); - } - - this.Log(string.Format("SendMessage to server '{0}': '{1}'.", message.GetType().Name, message)); - - // Messages can be sent by different thread so we need to synchronize it - var paddingMultiplier = this._clientCipher == null ? (byte)8 : Math.Max((byte)8, this._serverCipher.MinimumSize); // Should be recalculate base on cipher min length if cipher specified - - var messageData = message.GetBytes(); - - if (this._clientCompression != null) - { - messageData = this._clientCompression.Compress(messageData); - } - - var packetLength = messageData.Length + 4 + 1; // add length bytes and padding byte - var paddingLength = (byte)((-packetLength) & (paddingMultiplier - 1)); - if (paddingLength < paddingMultiplier) - { - paddingLength += paddingMultiplier; - } - - // Build Packet data - var packetData = new byte[4 + 1 + messageData.Length + paddingLength]; - - // Add packet length - ((uint)packetData.Length - 4).GetBytes().CopyTo(packetData, 0); - - // Add packet padding length - packetData[4] = paddingLength; - - // Add packet payload - messageData.CopyTo(packetData, 4 + 1); - - // Add random padding - var paddingBytes = new byte[paddingLength]; - Randomizer.GetBytes(paddingBytes); - paddingBytes.CopyTo(packetData, 4 + 1 + messageData.Length); - - // Lock handling of _outboundPacketSequence since it must be sent sequently to server - lock (this._socketLock) - { - if (this._socket == null || !this._socket.Connected) - throw new SshConnectionException("Client not connected."); - - // Calculate packet hash - var hashData = new byte[4 + packetData.Length]; - this._outboundPacketSequence.GetBytes().CopyTo(hashData, 0); - packetData.CopyTo(hashData, 4); - - // Encrypt packet data - if (this._clientCipher != null) - { - packetData = this._clientCipher.Encrypt(packetData); - } - - if (packetData.Length > MaximumSshPacketSize) - { - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Packet is too big. Maximum packet size is {0} bytes.", MaximumSshPacketSize)); - } - - if (this._clientMac == null) - { - this.SocketWrite(packetData); - } - else - { - var hash = this._clientMac.ComputeHash(hashData.ToArray()); - - var data = new byte[packetData.Length + this._clientMac.HashSize / 8]; - packetData.CopyTo(data, 0); - hash.CopyTo(data, packetData.Length); - - this.SocketWrite(data); - } - - this._outboundPacketSequence++; - - Monitor.Pulse(this._socketLock); - } - } - - private static IEnumerable GetMessagesMetadata() - { - return new [] - { - new MessageMetadata { Name = "SSH_MSG_NEWKEYS", Number = 21, Type = typeof(NewKeysMessage) }, - new MessageMetadata { Name = "SSH_MSG_REQUEST_FAILURE", Number = 82, Type = typeof(RequestFailureMessage) }, - new MessageMetadata { Name = "SSH_MSG_KEXINIT", Number = 20, Type = typeof(KeyExchangeInitMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN_FAILURE", Number = 92, Type = typeof(ChannelOpenFailureMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_FAILURE", Number = 100, Type = typeof(ChannelFailureMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_EXTENDED_DATA", Number = 95, Type = typeof(ChannelExtendedDataMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_DATA", Number = 94, Type = typeof(ChannelDataMessage) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_REQUEST", Number = 50, Type = typeof(RequestMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_REQUEST", Number = 98, Type = typeof(ChannelRequestMessage) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_BANNER", Number = 53, Type = typeof(BannerMessage) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_INFO_RESPONSE", Number = 61, Type = typeof(InformationResponseMessage) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_FAILURE", Number = 51, Type = typeof(FailureMessage) }, - new MessageMetadata { Name = "SSH_MSG_DEBUG", Number = 4, Type = typeof(DebugMessage), }, - new MessageMetadata { Name = "SSH_MSG_KEXDH_INIT", Number = 30, Type = typeof(KeyExchangeDhInitMessage) }, - new MessageMetadata { Name = "SSH_MSG_GLOBAL_REQUEST", Number = 80, Type = typeof(GlobalRequestMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN", Number = 90, Type = typeof(ChannelOpenMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION", Number = 91, Type = typeof(ChannelOpenConfirmationMessage) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_INFO_REQUEST", Number = 60, Type = typeof(InformationRequestMessage) }, - new MessageMetadata { Name = "SSH_MSG_UNIMPLEMENTED", Number = 3, Type = typeof(UnimplementedMessage) }, - new MessageMetadata { Name = "SSH_MSG_REQUEST_SUCCESS", Number = 81, Type = typeof(RequestSuccessMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_SUCCESS", Number = 99, Type = typeof(ChannelSuccessMessage) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ", Number = 60, Type = typeof(PasswordChangeRequiredMessage) }, - new MessageMetadata { Name = "SSH_MSG_DISCONNECT", Number = 1, Type = typeof(DisconnectMessage) }, - new MessageMetadata { Name = "SSH_MSG_SERVICE_REQUEST", Number = 5, Type = typeof(ServiceRequestMessage) }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_REQUEST", Number = 34, Type = typeof(KeyExchangeDhGroupExchangeRequest) }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_GROUP", Number = 31, Type = typeof(KeyExchangeDhGroupExchangeGroup) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_SUCCESS", Number = 52, Type = typeof(SuccessMessage) }, - new MessageMetadata { Name = "SSH_MSG_USERAUTH_PK_OK", Number = 60, Type = typeof(PublicKeyMessage) }, - new MessageMetadata { Name = "SSH_MSG_IGNORE", Number = 2, Type = typeof(IgnoreMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_WINDOW_ADJUST", Number = 93, Type = typeof(ChannelWindowAdjustMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_EOF", Number = 96, Type = typeof(ChannelEofMessage) }, - new MessageMetadata { Name = "SSH_MSG_CHANNEL_CLOSE", Number = 97, Type = typeof(ChannelCloseMessage) }, - new MessageMetadata { Name = "SSH_MSG_SERVICE_ACCEPT", Number = 6, Type = typeof(ServiceAcceptMessage) }, - new MessageMetadata { Name = "SSH_MSG_KEXDH_REPLY", Number = 31, Type = typeof(KeyExchangeDhReplyMessage) }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_INIT", Number = 32, Type = typeof(KeyExchangeDhGroupExchangeInit) }, - new MessageMetadata { Name = "SSH_MSG_KEX_DH_GEX_REPLY", Number = 33, Type = typeof(KeyExchangeDhGroupExchangeReply) } - }; - } - - /// - /// Receives the message from the server. - /// - /// Incoming SSH message. - /// - private Message ReceiveMessage() - { - // No lock needed since all messages read by only one thread - var blockSize = this._serverCipher == null ? (byte)8 : Math.Max((byte)8, this._serverCipher.MinimumSize); - - // Read packet length first - var firstBlock = this.Read(blockSize); - - if (this._serverCipher != null) - { - firstBlock = this._serverCipher.Decrypt(firstBlock); - } - - var packetLength = (uint)(firstBlock[0] << 24 | firstBlock[1] << 16 | firstBlock[2] << 8 | firstBlock[3]); - - // Test packet minimum and maximum boundaries - if (packetLength < Math.Max((byte)16, blockSize) - 4 || packetLength > MaximumSshPacketSize - 4) - throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length {0}", packetLength), DisconnectReason.ProtocolError); - - // Read rest of the packet data - var bytesToRead = (int)(packetLength - (blockSize - 4)); - - var data = new byte[bytesToRead + blockSize]; - - firstBlock.CopyTo(data, 0); - - byte[] serverHash = null; - if (this._serverMac != null) - { - serverHash = new byte[this._serverMac.HashSize / 8]; - bytesToRead += serverHash.Length; - } - - if (bytesToRead > 0) - { - var nextBlocks = this.Read(bytesToRead); - - if (serverHash != null) - { - Buffer.BlockCopy(nextBlocks, nextBlocks.Length - serverHash.Length, serverHash, 0, serverHash.Length); - nextBlocks = nextBlocks.Take(nextBlocks.Length - serverHash.Length).ToArray(); - } - - if (nextBlocks.Length > 0) - { - if (this._serverCipher != null) - { - nextBlocks = this._serverCipher.Decrypt(nextBlocks); - } - nextBlocks.CopyTo(data, blockSize); - } - } - - var paddingLength = data[4]; - - var messagePayload = new byte[packetLength - paddingLength - 1]; - Buffer.BlockCopy(data, 5, messagePayload, 0, messagePayload.Length); - - if (this._serverDecompression != null) - { - messagePayload = this._serverDecompression.Decompress(messagePayload); - } - - // Validate message against MAC - if (this._serverMac != null) - { - var clientHashData = new byte[4 + data.Length]; - var lengthBytes = this._inboundPacketSequence.GetBytes(); - - lengthBytes.CopyTo(clientHashData, 0); - data.CopyTo(clientHashData, 4); - - // Calculate packet hash - var clientHash = this._serverMac.ComputeHash(clientHashData); - - if (!serverHash.SequenceEqual(clientHash)) - { - throw new SshConnectionException("MAC error", DisconnectReason.MacError); - } - } - - this._inboundPacketSequence++; - - return this.LoadMessage(messagePayload); - } - - private void SendDisconnect(DisconnectReason reasonCode, string message) - { - // only send a disconnect message if it wasn't already sent, and we're - // still connected - if (this._isDisconnectMessageSent || !IsConnected) - return; - - var disconnectMessage = new DisconnectMessage(reasonCode, message); - - this.SendMessage(disconnectMessage); - - this._isDisconnectMessageSent = true; - } - - partial void HandleMessageCore(Message message); - - /// - /// Handles the message. - /// - /// - /// The message. - private void HandleMessage(T message) where T : Message - { - this.OnMessageReceived(message); - } - - #region Handle transport messages - - private void HandleMessage(DisconnectMessage message) - { - this.OnDisconnectReceived(message); - - // disconnect from the socket, and dispose it - SocketDisconnectAndDispose(); - } - - private void HandleMessage(IgnoreMessage message) - { - this.OnIgnoreReceived(message); - } - - private void HandleMessage(UnimplementedMessage message) - { - this.OnUnimplementedReceived(message); - } - - private void HandleMessage(DebugMessage message) - { - this.OnDebugReceived(message); - } - - private void HandleMessage(ServiceRequestMessage message) - { - this.OnServiceRequestReceived(message); - } - - private void HandleMessage(ServiceAcceptMessage message) - { - // TODO: Refactor to avoid this method here - this.OnServiceAcceptReceived(message); - - this._serviceAccepted.Set(); - } - - private void HandleMessage(KeyExchangeInitMessage message) - { - this.OnKeyExchangeInitReceived(message); - } - - private void HandleMessage(NewKeysMessage message) - { - this.OnNewKeysReceived(message); - } - - #endregion - - #region Handle User Authentication messages - - private void HandleMessage(RequestMessage message) - { - this.OnUserAuthenticationRequestReceived(message); - } - - private void HandleMessage(FailureMessage message) - { - this.OnUserAuthenticationFailureReceived(message); - } - - private void HandleMessage(SuccessMessage message) - { - this.OnUserAuthenticationSuccessReceived(message); - } - - private void HandleMessage(BannerMessage message) - { - this.OnUserAuthenticationBannerReceived(message); - } - - #endregion - - #region Handle connection messages - - private void HandleMessage(GlobalRequestMessage message) - { - this.OnGlobalRequestReceived(message); - } - - private void HandleMessage(RequestSuccessMessage message) - { - this.OnRequestSuccessReceived(message); - } - - private void HandleMessage(RequestFailureMessage message) - { - this.OnRequestFailureReceived(message); - } - - private void HandleMessage(ChannelOpenMessage message) - { - this.OnChannelOpenReceived(message); - } - - private void HandleMessage(ChannelOpenConfirmationMessage message) - { - this.OnChannelOpenConfirmationReceived(message); - } - - private void HandleMessage(ChannelOpenFailureMessage message) - { - this.OnChannelOpenFailureReceived(message); - } - - private void HandleMessage(ChannelWindowAdjustMessage message) - { - this.OnChannelWindowAdjustReceived(message); - } - - private void HandleMessage(ChannelDataMessage message) - { - this.OnChannelDataReceived(message); - } - - private void HandleMessage(ChannelExtendedDataMessage message) - { - this.OnChannelExtendedDataReceived(message); - } - - private void HandleMessage(ChannelEofMessage message) - { - this.OnChannelEofReceived(message); - } - - private void HandleMessage(ChannelCloseMessage message) - { - this.OnChannelCloseReceived(message); - } - - private void HandleMessage(ChannelRequestMessage message) - { - this.OnChannelRequestReceived(message); - } - - private void HandleMessage(ChannelSuccessMessage message) - { - this.OnChannelSuccessReceived(message); - } - - private void HandleMessage(ChannelFailureMessage message) - { - this.OnChannelFailureReceived(message); - } - - #endregion - - #region Handle received message events - - /// - /// Called when received. - /// - /// message. - protected virtual void OnDisconnectReceived(DisconnectMessage message) - { - this.Log(string.Format("Disconnect received: {0} {1}", message.ReasonCode, message.Description)); - - var disconnectReceived = DisconnectReceived; - if (disconnectReceived != null) - disconnectReceived(this, new MessageEventArgs(message)); - - var disconnected = Disconnected; - if (disconnected != null) - disconnected(this, new EventArgs()); - } - - /// - /// Called when received. - /// - /// message. - protected virtual void OnIgnoreReceived(IgnoreMessage message) - { - var handlers = IgnoreReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUnimplementedReceived(UnimplementedMessage message) - { - var handlers = UnimplementedReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnDebugReceived(DebugMessage message) - { - var handlers = DebugReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnServiceRequestReceived(ServiceRequestMessage message) - { - var handlers = ServiceRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnServiceAcceptReceived(ServiceAcceptMessage message) - { - var handlers = ServiceAcceptReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnKeyExchangeInitReceived(KeyExchangeInitMessage message) - { - this._keyExchangeInProgress = true; - - this._keyExchangeCompletedWaitHandle.Reset(); - - // Disable all registered messages except key exchange related - foreach (var messageMetadata in this._messagesMetadata) - { - if (messageMetadata.Activated && messageMetadata.Number > 2 && (messageMetadata.Number < 20 || messageMetadata.Number > 30)) - messageMetadata.Enabled = false; - } - - var keyExchangeAlgorithmName = (from c in this.ConnectionInfo.KeyExchangeAlgorithms.Keys - from s in message.KeyExchangeAlgorithms - where s == c - select c).FirstOrDefault(); - - if (keyExchangeAlgorithmName == null) - { - throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed); - } - - // Create instance of key exchange algorithm that will be used - this._keyExchange = this.ConnectionInfo.KeyExchangeAlgorithms[keyExchangeAlgorithmName].CreateInstance(); - - this.ConnectionInfo.CurrentKeyExchangeAlgorithm = keyExchangeAlgorithmName; - - this._keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived; - - // Start the algorithm implementation - this._keyExchange.Start(this, message); - - var keyExchangeInitReceived = KeyExchangeInitReceived; - if (keyExchangeInitReceived != null) - keyExchangeInitReceived(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnNewKeysReceived(NewKeysMessage message) - { - // Update sessionId - if (this.SessionId == null) - { - this.SessionId = this._keyExchange.ExchangeHash; - } - - // Dispose of old ciphers and hash algorithms - if (this._serverMac != null) - { - this._serverMac.Clear(); - this._serverMac = null; - } - - if (this._clientMac != null) - { - this._clientMac.Clear(); - this._clientMac = null; - } - - // Update negotiated algorithms - this._serverCipher = this._keyExchange.CreateServerCipher(); - this._clientCipher = this._keyExchange.CreateClientCipher(); - this._serverMac = this._keyExchange.CreateServerHash(); - this._clientMac = this._keyExchange.CreateClientHash(); - this._clientCompression = this._keyExchange.CreateCompressor(); - this._serverDecompression = this._keyExchange.CreateDecompressor(); - - // Dispose of old KeyExchange object as it is no longer needed. - if (this._keyExchange != null) - { - this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; - this._keyExchange.Dispose(); - this._keyExchange = null; - } - - // Enable all active registered messages - foreach (var messageMetadata in this._messagesMetadata) - { - if (messageMetadata.Activated) - messageMetadata.Enabled = true; - } - - var newKeysReceived = NewKeysReceived; - if (newKeysReceived != null) - newKeysReceived(this, new MessageEventArgs(message)); - - // Signal that key exchange completed - this._keyExchangeCompletedWaitHandle.Set(); - - this._keyExchangeInProgress = false; - } - - /// - /// Called when client is disconnecting from the server. - /// - internal void OnDisconnecting() - { - _isDisconnecting = true; - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationRequestReceived(RequestMessage message) - { - var handlers = UserAuthenticationRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationFailureReceived(FailureMessage message) - { - var handlers = UserAuthenticationFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationSuccessReceived(SuccessMessage message) - { - var handlers = UserAuthenticationSuccessReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnUserAuthenticationBannerReceived(BannerMessage message) - { - var handlers = UserAuthenticationBannerReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnGlobalRequestReceived(GlobalRequestMessage message) - { - var handlers = GlobalRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnRequestSuccessReceived(RequestSuccessMessage message) - { - var handlers = RequestSuccessReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnRequestFailureReceived(RequestFailureMessage message) - { - var handlers = RequestFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelOpenReceived(ChannelOpenMessage message) - { - var handlers = ChannelOpenReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelOpenConfirmationReceived(ChannelOpenConfirmationMessage message) - { - var handlers = ChannelOpenConfirmationReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelOpenFailureReceived(ChannelOpenFailureMessage message) - { - var handlers = ChannelOpenFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelWindowAdjustReceived(ChannelWindowAdjustMessage message) - { - var handlers = ChannelWindowAdjustReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelDataReceived(ChannelDataMessage message) - { - var handlers = ChannelDataReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelExtendedDataReceived(ChannelExtendedDataMessage message) - { - var handlers = ChannelExtendedDataReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelEofReceived(ChannelEofMessage message) - { - var handlers = ChannelEofReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelCloseReceived(ChannelCloseMessage message) - { - var handlers = ChannelCloseReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelRequestReceived(ChannelRequestMessage message) - { - var handlers = ChannelRequestReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelSuccessReceived(ChannelSuccessMessage message) - { - var handlers = ChannelSuccessReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnChannelFailureReceived(ChannelFailureMessage message) - { - var handlers = ChannelFailureReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - /// - /// Called when message received. - /// - /// message. - protected virtual void OnMessageReceived(Message message) - { - var handlers = MessageReceived; - if (handlers != null) - handlers(this, new MessageEventArgs(message)); - } - - #endregion - - private void KeyExchange_HostKeyReceived(object sender, HostKeyEventArgs e) - { - var handlers = HostKeyReceived; - if (handlers != null) - handlers(this, e); - } - - /// - /// Reads the specified length of bytes from the server. - /// - /// The length. - /// - /// The bytes read from the server. - /// - private byte[] Read(int length) - { - var buffer = new byte[length]; - - this.SocketRead(length, ref buffer); - - return buffer; - } - - #region Message loading functions - - /// - /// Registers SSH Message with the session. - /// - /// Name of the message. - public void RegisterMessage(string messageName) - { - this.InternalRegisterMessage(messageName); - } - - /// - /// Removes SSH message from the session - /// - /// Name of the message. - public void UnRegisterMessage(string messageName) - { - this.InternalUnRegisterMessage(messageName); - } - - /// - /// Loads the message. - /// - /// Message data. - /// New message - private Message LoadMessage(byte[] data) - { - var messageType = data[0]; - var messageMetadata = (from m in this._messagesMetadata where m.Number == messageType && m.Enabled && m.Activated select m).SingleOrDefault(); - if (messageMetadata == null) - throw new SshException(string.Format(CultureInfo.CurrentCulture, "Message type {0} is not valid.", messageType)); - - var message = messageMetadata.Type.CreateInstance(); - - message.Load(data); - - this.Log(string.Format("ReceiveMessage from server: '{0}': '{1}'.", message.GetType().Name, message)); - - return message; - } - - partial void InternalRegisterMessage(string messageName); - - partial void InternalUnRegisterMessage(string messageName); - - #endregion - - partial void ExecuteThread(Action action); - - /// - /// Gets a value indicating whether the socket is connected. - /// - /// - /// true if the socket is connected; otherwise, false. - /// - partial void IsSocketConnected(ref bool isConnected); - - partial void SocketConnect(string host, int port); - - partial void SocketDisconnect(); - - partial void SocketRead(int length, ref byte[] buffer); - - partial void SocketReadLine(ref string response); - - partial void Log(string text); - - /// - /// Writes the specified data to the server. - /// - /// The data. - partial void SocketWrite(byte[] data); - - /// - /// Disconnects and disposes the socket. - /// - private void SocketDisconnectAndDispose() - { - if (_socket != null) - { - lock (_socketLock) - { - if (_socket != null) - { - SocketDisconnect(); - _socket.Dispose(); - _socket = null; - } - } - } - } - - /// - /// Listens for incoming message from the server and handles them. This method run as a task on separate thread. - /// - private void MessageListener() - { - try - { - while (this._socket != null && this._socket.Connected) - { - var message = this.ReceiveMessage(); - this.HandleMessageCore(message); - } - } - catch (Exception exp) - { - this.RaiseError(exp); - } - } - - private byte SocketReadByte() - { - var buffer = new byte[1]; - - this.SocketRead(1, ref buffer); - - return buffer[0]; - } - - private void SocketWriteByte(byte data) - { - this.SocketWrite(new[] {data}); - } - - private void ConnectSocks4() - { - // Send socks version number - this.SocketWriteByte(0x04); - - // Send command code - this.SocketWriteByte(0x01); - - // Send port - this.SocketWriteByte((byte)(this.ConnectionInfo.Port / 0xFF)); - this.SocketWriteByte((byte)(this.ConnectionInfo.Port % 0xFF)); - - // Send IP - IPAddress ipAddress = this.ConnectionInfo.Host.GetIPAddress(); - this.SocketWrite(ipAddress.GetAddressBytes()); - - // Send username - var username = new Common.ASCIIEncoding().GetBytes(this.ConnectionInfo.ProxyUsername); - this.SocketWrite(username); - this.SocketWriteByte(0x00); - - // Read 0 - if (this.SocketReadByte() != 0) - { - throw new ProxyException("SOCKS4: Null is expected."); - } - - // Read response code - var code = this.SocketReadByte(); - - switch (code) - { - case 0x5a: - break; - case 0x5b: - throw new ProxyException("SOCKS4: Connection rejected."); - case 0x5c: - throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server."); - case 0x5d: - throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request."); - default: - throw new ProxyException("SOCKS4: Not valid response."); - } - - var dummyBuffer = new byte[4]; - - // Read 2 bytes to be ignored - this.SocketRead(2, ref dummyBuffer); - - // Read 4 bytes to be ignored - this.SocketRead(4, ref dummyBuffer); - } - - private void ConnectSocks5() - { - // Send socks version number - this.SocketWriteByte(0x05); - - // Send number of supported authentication methods - this.SocketWriteByte(0x02); - - // Send supported authentication methods - this.SocketWriteByte(0x00); // No authentication - this.SocketWriteByte(0x02); // Username/Password - - var socksVersion = this.SocketReadByte(); - if (socksVersion != 0x05) - throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion)); - - var authenticationMethod = this.SocketReadByte(); - switch (authenticationMethod) - { - case 0x00: - break; - case 0x02: - - // Send version - this.SocketWriteByte(0x01); - - var encoding = new Common.ASCIIEncoding(); - - var username = encoding.GetBytes(this.ConnectionInfo.ProxyUsername); - - if (username.Length > byte.MaxValue) - throw new ProxyException("Proxy username is too long."); - - // Send username length - this.SocketWriteByte((byte)username.Length); - - // Send username - this.SocketWrite(username); - - var password = encoding.GetBytes(this.ConnectionInfo.ProxyPassword); - - if (password.Length > byte.MaxValue) - throw new ProxyException("Proxy password is too long."); - - // Send username length - this.SocketWriteByte((byte)password.Length); - - // Send username - this.SocketWrite(password); - - var serverVersion = this.SocketReadByte(); - - if (serverVersion != 1) - throw new ProxyException("SOCKS5: Server authentication version is not valid."); - - var statusCode = this.SocketReadByte(); - if (statusCode != 0) - throw new ProxyException("SOCKS5: Username/Password authentication failed."); - - break; - case 0xFF: - throw new ProxyException("SOCKS5: No acceptable authentication methods were offered."); - } - - // Send socks version number - this.SocketWriteByte(0x05); - - // Send command code - this.SocketWriteByte(0x01); // establish a TCP/IP stream connection - - // Send reserved, must be 0x00 - this.SocketWriteByte(0x00); - - IPAddress ip = this.ConnectionInfo.Host.GetIPAddress(); - - // Send address type and address - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - this.SocketWriteByte(0x01); - var address = ip.GetAddressBytes(); - this.SocketWrite(address); - } - else if (ip.AddressFamily == AddressFamily.InterNetworkV6) - { - this.SocketWriteByte(0x04); - var address = ip.GetAddressBytes(); - this.SocketWrite(address); - } - else - { - throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", ip)); - } - - // Send port - this.SocketWriteByte((byte)(this.ConnectionInfo.Port / 0xFF)); - this.SocketWriteByte((byte)(this.ConnectionInfo.Port % 0xFF)); - - // Read Server SOCKS5 version - if (this.SocketReadByte() != 5) - { - throw new ProxyException("SOCKS5: Version 5 is expected."); - } - - // Read response code - var status = this.SocketReadByte(); - - switch (status) - { - case 0x00: - break; - case 0x01: - throw new ProxyException("SOCKS5: General failure."); - case 0x02: - throw new ProxyException("SOCKS5: Connection not allowed by ruleset."); - case 0x03: - throw new ProxyException("SOCKS5: Network unreachable."); - case 0x04: - throw new ProxyException("SOCKS5: Host unreachable."); - case 0x05: - throw new ProxyException("SOCKS5: Connection refused by destination host."); - case 0x06: - throw new ProxyException("SOCKS5: TTL expired."); - case 0x07: - throw new ProxyException("SOCKS5: Command not supported or protocol error."); - case 0x08: - throw new ProxyException("SOCKS5: Address type not supported."); - default: - throw new ProxyException("SOCKS4: Not valid response."); - } - - // Read 0 - if (this.SocketReadByte() != 0) - { - throw new ProxyException("SOCKS5: 0 byte is expected."); - } - - var addressType = this.SocketReadByte(); - var responseIp = new byte[16]; - - switch (addressType) - { - case 0x01: - this.SocketRead(4, ref responseIp); - break; - case 0x04: - this.SocketRead(16, ref responseIp); - break; - default: - throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType)); - } - - var port = new byte[2]; - - // Read 2 bytes to be ignored - this.SocketRead(2, ref port); - } - - private void ConnectHttp() - { - var httpResponseRe = new Regex(@"HTTP/(?\d[.]\d) (?\d{3}) (?.+)$"); - var httpHeaderRe = new Regex(@"(?[^\[\]()<>@,;:\""/?={} \t]+):(?.+)?"); - - var encoding = new Common.ASCIIEncoding(); - - this.SocketWrite(encoding.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.0\r\n", this.ConnectionInfo.Host, this.ConnectionInfo.Port))); - - // Sent proxy authorization is specified - if (!string.IsNullOrEmpty(this.ConnectionInfo.ProxyUsername)) - { - var authorization = string.Format("Proxy-Authorization: Basic {0}\r\n", - Convert.ToBase64String(encoding.GetBytes(string.Format("{0}:{1}", this.ConnectionInfo.ProxyUsername, this.ConnectionInfo.ProxyPassword))) - ); - this.SocketWrite(encoding.GetBytes(authorization)); - } - - this.SocketWrite(encoding.GetBytes("\r\n")); - - HttpStatusCode? statusCode = null; - var response = string.Empty; - var contentLength = 0; - - while (true) - { - this.SocketReadLine(ref response); - - if (statusCode == null) - { - var statusMatch = httpResponseRe.Match(response); - if (statusMatch.Success) - { - var httpStatusCode = statusMatch.Result("${statusCode}"); - statusCode = (HttpStatusCode) int.Parse(httpStatusCode); - if (statusCode != HttpStatusCode.OK) - { - var reasonPhrase = statusMatch.Result("${reasonPhrase}"); - throw new ProxyException(string.Format("HTTP: Status code {0}, \"{1}\"", httpStatusCode, - reasonPhrase)); - } - continue; - } - } - - // continue on parsing message headers coming from the server - var headerMatch = httpHeaderRe.Match(response); - if (headerMatch.Success) - { - var fieldName = headerMatch.Result("${fieldName}"); - if (fieldName.Equals("Content-Length", StringComparison.InvariantCultureIgnoreCase)) - { - contentLength = int.Parse(headerMatch.Result("${fieldValue}")); - } - continue; - } - - // check if we've reached the CRLF which separates request line and headers from the message body - if (response.Length == 0) - { - // read response body if specified - if (contentLength > 0) - { - var contentBody = new byte[contentLength]; - SocketRead(contentLength, ref contentBody); - } - break; - } - } - } - - /// - /// Raises the event. - /// - /// The exp. - private void RaiseError(Exception exp) - { - var connectionException = exp as SshConnectionException; - - if (_isDisconnecting) - { - // a connection exception which is raised while isDisconnecting is normal and - // should be ignored - if (connectionException != null) - return; - - // any timeout while disconnecting can be caused by loss of connectivity - // altogether and should be ignored - var socketException = exp as SocketException; - if (socketException != null && socketException.SocketErrorCode == SocketError.TimedOut) - return; - } - - this._exception = exp; - - this._exceptionWaitHandle.Set(); - - var errorOccured = ErrorOccured; - if (errorOccured != null) - errorOccured(this, new ExceptionEventArgs(exp)); - - if (connectionException != null && connectionException.DisconnectReason != DisconnectReason.ConnectionLost) - { - this.Disconnect(connectionException.DisconnectReason, exp.ToString()); - } - } - - /// - /// Resets connection-specific information to ensure state of a previous connection - /// does not affect new connections. - /// - private void Reset() - { - if (_exceptionWaitHandle != null) - _exceptionWaitHandle.Reset(); - if (_keyExchangeCompletedWaitHandle != null) - _keyExchangeCompletedWaitHandle.Reset(); - if (_messageListenerCompleted != null) - _messageListenerCompleted.Reset(); - - SessionId = null; - _isDisconnectMessageSent = false; - _isDisconnecting = false; - _isAuthenticated = false; - _exception = null; - _keyExchangeInProgress = false; - } - - #region IDisposable Members - - private bool _disposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._disposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - Disconnect(); - - if (this._serviceAccepted != null) - { - this._serviceAccepted.Dispose(); - this._serviceAccepted = null; - } - - if (this._exceptionWaitHandle != null) - { - this._exceptionWaitHandle.Dispose(); - this._exceptionWaitHandle = null; - } - - if (this._keyExchangeCompletedWaitHandle != null) - { - this._keyExchangeCompletedWaitHandle.Dispose(); - this._keyExchangeCompletedWaitHandle = null; - } - - if (this._serverMac != null) - { - this._serverMac.Clear(); - this._serverMac = null; - } - - if (this._clientMac != null) - { - this._clientMac.Clear(); - this._clientMac = null; - } - - if (this._keyExchange != null) - { - this._keyExchange.HostKeyReceived -= KeyExchange_HostKeyReceived; - this._keyExchange.Dispose(); - this._keyExchange = null; - } - } - - // Note disposing has been done. - this._disposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~Session() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - private class MessageMetadata - { - public string Name { get; set; } - - public byte Number { get; set; } - - public bool Enabled { get; set; } - - public bool Activated { get; set; } - - public Type Type { get; set; } - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Flags.cs b/Renci.SshNet/Sftp/Flags.cs deleted file mode 100644 index d8cbe17..0000000 --- a/Renci.SshNet/Sftp/Flags.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Renci.SshNet.Sftp -{ - internal enum Flags - { - None = 0x00000000, - /// - /// SSH_FXF_READ - /// - Read = 0x00000001, - /// - /// SSH_FXF_WRITE - /// - Write = 0x00000002, - /// - /// SSH_FXF_APPEND - /// - Append = 0x00000004, - /// - /// SSH_FXF_CREAT - /// - CreateNewOrOpen = 0x00000008, - /// - /// SSH_FXF_TRUNC - /// - Truncate = 0x00000010, - /// - /// SSH_FXF_EXCL - /// - CreateNew = 0x00000028 - } -} diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs deleted file mode 100644 index c9ebd0e..0000000 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/FStatVfsRequest.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class FStatVfsRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "fstatvfs@openssh.com"; } - } - - public byte[] Handle { get; private set; } - - public FStatVfsRequest(uint protocolVersion, uint requestId, byte[] handle, Action extendedAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.SetAction(extendedAction); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs deleted file mode 100644 index 188bc1b..0000000 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/HardLinkRequest.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class HardLinkRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "hardlink@openssh.com"; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public HardLinkRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.OldPath); - this.Write(this.NewPath); - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs deleted file mode 100644 index 3a3681f..0000000 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/PosixRenameRequest.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; -using System.Text; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class PosixRenameRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "posix-rename@openssh.com"; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public Encoding Encoding { get; private set; } - - public PosixRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - this.Encoding = encoding; - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.OldPath, this.Encoding); - this.Write(this.NewPath, this.Encoding); - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs b/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs deleted file mode 100644 index cdeadb2..0000000 --- a/Renci.SshNet/Sftp/Requests/ExtendedRequests/StatVfsRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; -using System.Text; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class StatVfsRequest : SftpExtendedRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public override string Name - { - get { return "statvfs@openssh.com"; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public StatVfsRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action extendedAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(extendedAction); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs b/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs deleted file mode 100644 index b272aa1..0000000 --- a/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class PosixRenameRequest : SftpRequest - { - public const string NAME = "posix-rename@openssh.com"; - - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public PosixRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(PosixRenameRequest.NAME); - this.Write(this.OldPath); - this.Write(this.NewPath); - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs b/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs deleted file mode 100644 index d17e6f1..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpBlockRequest.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpBlockRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Block; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public UInt64 Length { get; private set; } - - public UInt32 LockMask { get; private set; } - - public SftpBlockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, UInt32 lockMask, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Length = length; - this.LockMask = lockMask; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Length = this.ReadUInt64(); - this.LockMask = this.ReadUInt32(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.Write(this.Length); - this.Write(this.LockMask); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs b/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs deleted file mode 100644 index 3ceb1fd..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpCloseRequest.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpCloseRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Close; } - } - - public byte[] Handle { get; private set; } - - public SftpCloseRequest(uint protocolVersion, uint requestId, byte[] handle, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs b/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs deleted file mode 100644 index e500e51..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpExtendedRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal abstract class SftpExtendedRequest : SftpRequest - { - public const string NAME = "posix-rename@openssh.com"; - - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public abstract string Name { get; } - - public SftpExtendedRequest(uint protocolVersion, uint requestId, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Name); - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs deleted file mode 100644 index 64795d0..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpFSetStatRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpFSetStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.FSetStat; } - } - - public byte[] Handle { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpFSetStatRequest(uint protocolVersion, uint requestId, byte[] handle, SftpFileAttributes attributes, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Attributes = attributes; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Attributes = this.ReadAttributes(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Attributes); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs deleted file mode 100644 index 9265891..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpFStatRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpFStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.FStat; } - } - - public byte[] Handle { get; private set; } - - public SftpFStatRequest(uint protocolVersion, uint requestId, byte[] handle, Action attrsAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.SetAction(attrsAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs b/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs deleted file mode 100644 index 7d05694..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpInitRequest.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpInitRequest : SftpMessage - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Init; } - } - - public uint Version { get; private set; } - - public SftpInitRequest(uint version) - { - this.Version = version; - } - - protected override void LoadData() - { - base.LoadData(); - this.Version = this.ReadUInt32(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Version); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs deleted file mode 100644 index b03545c..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpLStatRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpLStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.LStat; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpLStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action attrsAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(attrsAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs b/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs deleted file mode 100644 index d62d0be..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpLinkRequest.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Renci.SshNet.Sftp.Responses; -using System; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpLinkRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Link; } - } - - public string NewLinkPath { get; private set; } - - public string ExistingPath { get; private set; } - - public bool IsSymLink { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The protocol version. - /// The request id. - /// Specifies the path name of the new link to create. - /// Specifies the path of a target object to which the newly created link will refer. In the case of a symbolic link, this path may not exist. - /// if set to false the link should be a hard link, or a second directory entry referring to the same file or directory object. - /// The status action. - public SftpLinkRequest(uint protocolVersion, uint requestId, string newLinkPath, string existingPath, bool isSymLink, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.NewLinkPath = newLinkPath; - this.ExistingPath = existingPath; - this.IsSymLink = isSymLink; - } - - protected override void LoadData() - { - base.LoadData(); - this.NewLinkPath = this.ReadString(); - this.ExistingPath = this.ReadString(); - this.IsSymLink = this.ReadBoolean(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.NewLinkPath); - this.Write(this.ExistingPath); - this.Write(this.IsSymLink); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs deleted file mode 100644 index ef7a690..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpMkDirRequest.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpMkDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.MkDir; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpMkDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action statusAction) - : this(protocolVersion, requestId, path, encoding, null, statusAction) - { - } - - public SftpMkDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, SftpFileAttributes attributes, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.Attributes = attributes; - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - this.Attributes = this.ReadAttributes(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - this.Write(this.Attributes); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs deleted file mode 100644 index 6792031..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpOpenDirRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpOpenDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.OpenDir; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpOpenDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action handleAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(handleAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs b/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs deleted file mode 100644 index 9fa3333..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpOpenRequest.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpOpenRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Open; } - } - - public string Filename { get; private set; } - - public Flags Flags { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpOpenRequest(uint protocolVersion, uint requestId, string fileName, Encoding encoding, Flags flags, Action handleAction, Action statusAction) - : this(protocolVersion, requestId, fileName, encoding, flags, new SftpFileAttributes(), handleAction, statusAction) - { - } - - public SftpOpenRequest(uint protocolVersion, uint requestId, string fileName, Encoding encoding, Flags flags, SftpFileAttributes attributes, Action handleAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Filename = fileName; - this.Flags = flags; - this.Attributes = attributes; - this.Encoding = encoding; - - this.SetAction(handleAction); - } - - protected override void LoadData() - { - base.LoadData(); - throw new NotSupportedException(); - } - - protected override void SaveData() - { - base.SaveData(); - - this.Write(this.Filename, this.Encoding); - this.Write((uint)this.Flags); - this.Write(this.Attributes); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs deleted file mode 100644 index 90806b8..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpReadDirRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpReadDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.ReadDir; } - } - - public byte[] Handle { get; private set; } - - public SftpReadDirRequest(uint protocolVersion, uint requestId, byte[] handle, Action nameAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.SetAction(nameAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs b/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs deleted file mode 100644 index e567a11..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpReadLinkRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpReadLinkRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.ReadLink; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpReadLinkRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action nameAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(nameAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs b/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs deleted file mode 100644 index 745035d..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpReadRequest.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpReadRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Read; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public UInt32 Length { get; private set; } - - public SftpReadRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt32 length, Action dataAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Length = length; - this.SetAction(dataAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Length = this.ReadUInt32(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.Write(this.Length); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs deleted file mode 100644 index e0e193c..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpRealPathRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRealPathRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.RealPath; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRealPathRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action nameAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - if (nameAction == null) - throw new ArgumentNullException("nameAction"); - if (statusAction == null) - throw new ArgumentNullException("statusAction"); - - this.Path = path; - this.Encoding = encoding; - this.SetAction(nameAction); - - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs deleted file mode 100644 index 8eefa27..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpRemoveRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRemoveRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Remove; } - } - - public string Filename { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRemoveRequest(uint protocolVersion, uint requestId, string filename, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Filename = filename; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.Filename = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Filename, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs deleted file mode 100644 index 07aa990..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpRenameRequest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRenameRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Rename; } - } - - public string OldPath { get; private set; } - - public string NewPath { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRenameRequest(uint protocolVersion, uint requestId, string oldPath, string newPath, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.OldPath = oldPath; - this.NewPath = newPath; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.OldPath = this.ReadString(this.Encoding); - this.NewPath = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.OldPath, this.Encoding); - this.Write(this.NewPath, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRequest.cs deleted file mode 100644 index f1964b5..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpRequest.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal abstract class SftpRequest : SftpMessage - { - private readonly Action _statusAction; - private Action _attrsAction; - private Action _dataAction; - private Action _extendedReplyAction; - private Action _handleAction; - private Action _nameAction; - - public uint RequestId { get; private set; } - - public uint ProtocolVersion { get; private set; } - - public SftpRequest(uint protocolVersion, uint requestId, Action statusAction) - { - this.RequestId = requestId; - this.ProtocolVersion = protocolVersion; - this._statusAction = statusAction; - } - - public void Complete(SftpResponse response) - { - if (response is SftpStatusResponse) - { - this._statusAction(response as SftpStatusResponse); - } - else if (response is SftpAttrsResponse) - { - this._attrsAction(response as SftpAttrsResponse); - } - else if (response is SftpDataResponse) - { - this._dataAction(response as SftpDataResponse); - } - else if (response is SftpExtendedReplyResponse) - { - this._extendedReplyAction(response as SftpExtendedReplyResponse); - } - else if (response is SftpHandleResponse) - { - this._handleAction(response as SftpHandleResponse); - } - else if (response is SftpNameResponse) - { - this._nameAction(response as SftpNameResponse); - } - else - { - throw new InvalidOperationException(string.Format("Response of type '{0}' is not expected.", response.GetType().Name)); - } - } - - protected void SetAction(Action action) - { - this._attrsAction = action; - } - - protected void SetAction(Action action) - { - this._dataAction = action; - } - - protected void SetAction(Action action) - { - this._extendedReplyAction = action; - } - - protected void SetAction(Action action) - { - this._handleAction = action; - } - - protected void SetAction(Action action) - { - this._nameAction = action; - } - - protected override void LoadData() - { - throw new InvalidOperationException("Request cannot be saved."); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.RequestId); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs b/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs deleted file mode 100644 index f8e577c..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpRmDirRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpRmDirRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.RmDir; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpRmDirRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs deleted file mode 100644 index cca618d..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpSetStatRequest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpSetStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.SetStat; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpSetStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, SftpFileAttributes attributes, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.Attributes = attributes; - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - this.Attributes = this.ReadAttributes(); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - this.Write(this.Attributes); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs b/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs deleted file mode 100644 index 562b80d..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpStatRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpStatRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Stat; } - } - - public string Path { get; private set; } - - public Encoding Encoding { get; private set; } - - public SftpStatRequest(uint protocolVersion, uint requestId, string path, Encoding encoding, Action attrsAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.Encoding = encoding; - this.SetAction(attrsAction); - } - - protected override void LoadData() - { - base.LoadData(); - this.Path = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.Path, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs b/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs deleted file mode 100644 index 5111135..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpSymLinkRequest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Text; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpSymLinkRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.SymLink; } - } - - public string NewLinkPath { get; set; } - - public string ExistingPath { get; set; } - - public Encoding Encoding { get; set; } - - public SftpSymLinkRequest(uint protocolVersion, uint requestId, string newLinkPath, string existingPath, Encoding encoding, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.NewLinkPath = newLinkPath; - this.ExistingPath = existingPath; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - this.NewLinkPath = this.ReadString(this.Encoding); - this.ExistingPath = this.ReadString(this.Encoding); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(this.NewLinkPath, this.Encoding); - this.Write(this.ExistingPath, this.Encoding); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs b/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs deleted file mode 100644 index e918359..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpUnblockRequest.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpUnblockRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Block; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public UInt64 Length { get; private set; } - - public SftpUnblockRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, UInt64 length, UInt32 lockMask, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Length = length; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Length = this.ReadUInt64(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.Write(this.Length); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs b/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs deleted file mode 100644 index 559b746..0000000 --- a/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class SftpWriteRequest : SftpRequest - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Write; } - } - - public byte[] Handle { get; private set; } - - public UInt64 Offset { get; private set; } - - public byte[] Data { get; private set; } - - public SftpWriteRequest(uint protocolVersion, uint requestId, byte[] handle, UInt64 offset, byte[] data, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Handle = handle; - this.Offset = offset; - this.Data = data; - } - - protected override void LoadData() - { - base.LoadData(); - this.Handle = this.ReadBinaryString(); - this.Offset = this.ReadUInt64(); - this.Data = this.ReadBinaryString(); - } - - protected override void SaveData() - { - base.SaveData(); - this.WriteBinaryString(this.Handle); - this.Write(this.Offset); - this.WriteBinaryString(this.Data); - } - } -} diff --git a/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs b/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs deleted file mode 100644 index 1e65283..0000000 --- a/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using Renci.SshNet.Sftp.Responses; - -namespace Renci.SshNet.Sftp.Requests -{ - internal class StatVfsRequest : SftpRequest - { - public const string NAME = "statvfs@openssh.com"; - - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Extended; } - } - - public string Path { get; private set; } - - public StatVfsRequest(uint protocolVersion, uint requestId, string path, Action extendedAction, Action statusAction) - : base(protocolVersion, requestId, statusAction) - { - this.Path = path; - this.SetAction(extendedAction); - } - - protected override void SaveData() - { - base.SaveData(); - this.Write(StatVfsRequest.NAME); - this.Write(this.Path); - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs b/Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs deleted file mode 100644 index 4e53692..0000000 --- a/Renci.SshNet/Sftp/Responses/ExtendedReplies/ExtendedReplyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Renci.SshNet.Common; -using System; - -namespace Renci.SshNet.Sftp.Responses -{ - internal abstract class ExtendedReplyInfo : SshData - { - protected override void LoadData() - { - // Read Message Type - var messageType = this.ReadByte(); - - // Read Response ID - var responseId = this.ReadUInt32(); - } - - protected override void SaveData() - { - throw new NotImplementedException(); - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs b/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs deleted file mode 100644 index 6ad8188..0000000 --- a/Renci.SshNet/Sftp/Responses/ExtendedReplies/StatVfsReplyInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Renci.SshNet.Sftp.Responses -{ - internal class StatVfsReplyInfo : ExtendedReplyInfo - { - public SftpFileSytemInformation Information { get; private set; } - - protected override void LoadData() - { - base.LoadData(); - - this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64()); - } - - protected override void SaveData() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs b/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs deleted file mode 100644 index d1e33e1..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpAttrsResponse.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpAttrsResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Attrs; } - } - - public SftpFileAttributes Attributes { get; private set; } - - public SftpAttrsResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - protected override void LoadData() - { - base.LoadData(); - this.Attributes = this.ReadAttributes(); - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs b/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs deleted file mode 100644 index 57b172d..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpDataResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Data; } - } - - public byte[] Data { get; set; } - - public bool IsEof { get; set; } - - public SftpDataResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - protected override void LoadData() - { - base.LoadData(); - - this.Data = this.ReadBinaryString(); - - if (!this.IsEndOfData) - { - this.IsEof = this.ReadBoolean(); - } - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs b/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs deleted file mode 100644 index 3aa8eba..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpExtendedReplyResponse.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpExtendedReplyResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.ExtendedReply; } - } - - public SftpExtendedReplyResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - public T GetReply() where T : SshData, new() - { - return this.OfType(); - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs b/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs deleted file mode 100644 index 15a85e8..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpHandleResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Handle; } - } - - public byte[] Handle { get; private set; } - - public SftpHandleResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - protected override void LoadData() - { - base.LoadData(); - - this.Handle = this.ReadBinaryString(); - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs b/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs deleted file mode 100644 index 7034713..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpNameResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Name; } - } - - public uint Count { get; private set; } - - public Encoding Encoding { get; private set; } - - public KeyValuePair[] Files { get; private set; } - - public SftpNameResponse(uint protocolVersion, Encoding encoding) - : base(protocolVersion) - { - this.Files = new KeyValuePair[0]; - this.Encoding = encoding; - } - - protected override void LoadData() - { - base.LoadData(); - - this.Count = this.ReadUInt32(); - this.Files = new KeyValuePair[this.Count]; - - for (int i = 0; i < this.Count; i++) - { - var fileName = this.ReadString(this.Encoding); - this.ReadString(); // This field value has meaningless information - var attributes = this.ReadAttributes(); - this.Files[i] = new KeyValuePair(fileName, attributes); - } - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/SftpResponse.cs b/Renci.SshNet/Sftp/Responses/SftpResponse.cs deleted file mode 100644 index 988dfcc..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace Renci.SshNet.Sftp.Responses -{ - internal abstract class SftpResponse : SftpMessage - { - public uint ResponseId { get; private set; } - - public uint ProtocolVersion { get; private set; } - - public SftpResponse(uint protocolVersion) - { - this.ProtocolVersion = protocolVersion; - } - - protected override void LoadData() - { - base.LoadData(); - - this.ResponseId = this.ReadUInt32(); - } - - protected override void SaveData() - { - throw new InvalidOperationException("Response cannot be saved."); - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs b/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs deleted file mode 100644 index d4a8593..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpStatusResponse.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpStatusResponse : SftpResponse - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Status; } - } - - public SftpStatusResponse(uint protocolVersion) - : base(protocolVersion) - { - } - - public StatusCodes StatusCode { get; private set; } - - public string ErrorMessage { get; private set; } - - public string Language { get; private set; } - - protected override void LoadData() - { - base.LoadData(); - - this.StatusCode = (StatusCodes)this.ReadUInt32(); - - if (this.ProtocolVersion < 3) - { - return; - } - - if (!this.IsEndOfData) - { - this.ErrorMessage = this.ReadString(); - this.Language = this.ReadString(); - } - } - } -} diff --git a/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs b/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs deleted file mode 100644 index e39b312..0000000 --- a/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Renci.SshNet.Sftp.Responses -{ - internal class SftpVersionResponse : SftpMessage - { - public override SftpMessageTypes SftpMessageType - { - get { return SftpMessageTypes.Version; } - } - - public uint Version { get; private set; } - - public IDictionary Extentions { get; private set; } - - protected override void LoadData() - { - base.LoadData(); - this.Version = this.ReadUInt32(); - this.Extentions = this.ReadExtensionPair(); - } - - protected override void SaveData() - { - throw new InvalidOperationException(); - } - - } -} diff --git a/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs b/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs deleted file mode 100644 index 8280c87..0000000 --- a/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Renci.SshNet.Sftp.Responses -{ - internal class StatVfsResponse : SftpExtendedReplyResponse - { - public SftpFileSytemInformation Information { get; private set; } - - public StatVfsResponse() - : base(0) - { - } - - protected override void LoadData() - { - base.LoadData(); - - this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64()); - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpDataMessage.cs b/Renci.SshNet/Sftp/SftpDataMessage.cs deleted file mode 100644 index bd29c79..0000000 --- a/Renci.SshNet/Sftp/SftpDataMessage.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Renci.SshNet.Messages.Connection; - -namespace Renci.SshNet.Sftp -{ - internal class SftpDataMessage : ChannelDataMessage - { - public SftpDataMessage(uint localChannelNumber, SftpMessage sftpMessage) - { - this.LocalChannelNumber = localChannelNumber; - - var messageData = sftpMessage.GetBytes(); - - var data = new byte[4 + messageData.Length]; - - ((uint)messageData.Length).GetBytes().CopyTo(data, 0); - messageData.CopyTo(data, 4); - this.Data = data; - } - } -} diff --git a/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs b/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs deleted file mode 100644 index 91e98c2..0000000 --- a/Renci.SshNet/Sftp/SftpDownloadAsyncResult.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous download operation. - /// - public class SftpDownloadAsyncResult : AsyncResult - { - /// - /// Gets or sets a value indicating whether to cancel asynchronous download operation. - /// - /// - /// true if download operation to be canceled; otherwise, false. - /// - /// - /// Download operation will be canceled after finishing uploading current buffer. - /// - public bool IsDownloadCanceled { get; set; } - - /// - /// Gets the number of downloaded bytes. - /// - public ulong DownloadedBytes { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpDownloadAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - } - - /// - /// Updates asynchronous operation status information. - /// - /// Number of downloaded bytes. - internal void Update(ulong downloadedBytes) - { - this.DownloadedBytes = downloadedBytes; - } - } -} diff --git a/Renci.SshNet/Sftp/SftpFile.cs b/Renci.SshNet/Sftp/SftpFile.cs deleted file mode 100644 index d94c533..0000000 --- a/Renci.SshNet/Sftp/SftpFile.cs +++ /dev/null @@ -1,510 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Represents SFTP file information - /// - public class SftpFile - { - private readonly SftpSession _sftpSession; - - /// - /// Gets the file attributes. - /// - public SftpFileAttributes Attributes { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The SFTP session. - /// Full path of the directory or file. - /// Attributes of the directory or file. - /// or is null. - internal SftpFile(SftpSession sftpSession, string fullName, SftpFileAttributes attributes) - { - if (sftpSession == null) - throw new SshConnectionException("Client not connected."); - - if (attributes == null) - throw new ArgumentNullException("attributes"); - - if (fullName == null) - throw new ArgumentNullException("fullName"); - - this._sftpSession = sftpSession; - this.Attributes = attributes; - - this.Name = fullName.Substring(fullName.LastIndexOf('/') + 1); - - this.FullName = fullName; - } - - /// - /// Gets the full path of the directory or file. - /// - public string FullName { get; private set; } - - /// - /// For files, gets the name of the file. For directories, gets the name of the last directory in the hierarchy if a hierarchy exists. - /// Otherwise, the Name property gets the name of the directory. - /// - public string Name { get; private set; } - - /// - /// Gets or sets the time the current file or directory was last accessed. - /// - /// - /// The time that the current file or directory was last accessed. - /// - public DateTime LastAccessTime - { - get - { - return this.Attributes.LastAccessTime; - } - set - { - this.Attributes.LastAccessTime = value; - } - } - - /// - /// Gets or sets the time when the current file or directory was last written to. - /// - /// - /// The time the current file was last written. - /// - public DateTime LastWriteTime - { - get - { - return this.Attributes.LastWriteTime; - } - set - { - this.Attributes.LastWriteTime = value; - } - } - - /// - /// Gets or sets the time, in coordinated universal time (UTC), the current file or directory was last accessed. - /// - /// - /// The time that the current file or directory was last accessed. - /// - public DateTime LastAccessTimeUtc - { - get - { - return this.Attributes.LastAccessTime.ToUniversalTime(); - } - set - { - this.Attributes.LastAccessTime = value.ToLocalTime(); - } - } - - /// - /// Gets or sets the time, in coordinated universal time (UTC), when the current file or directory was last written to. - /// - /// - /// The time the current file was last written. - /// - public DateTime LastWriteTimeUtc - { - get - { - return this.Attributes.LastWriteTime.ToUniversalTime(); - } - set - { - this.Attributes.LastWriteTime = value.ToLocalTime(); - } - } - - /// - /// Gets or sets the size, in bytes, of the current file. - /// - /// - /// The size of the current file in bytes. - /// - public long Length - { - get - { - return this.Attributes.Size; - } - } - - /// - /// Gets or sets file user id. - /// - /// - /// File user id. - /// - public int UserId - { - get - { - return this.Attributes.UserId; - } - set - { - this.Attributes.UserId = value; - } - } - - /// - /// Gets or sets file group id. - /// - /// - /// File group id. - /// - public int GroupId - { - get - { - return this.Attributes.GroupId; - } - set - { - this.Attributes.GroupId = value; - } - } - - /// - /// Gets a value indicating whether file represents a socket. - /// - /// - /// true if file represents a socket; otherwise, false. - /// - public bool IsSocket - { - get - { - return this.Attributes.IsSocket; - } - } - - /// - /// Gets a value indicating whether file represents a symbolic link. - /// - /// - /// true if file represents a symbolic link; otherwise, false. - /// - public bool IsSymbolicLink - { - get - { - return this.Attributes.IsSymbolicLink; - } - } - - /// - /// Gets a value indicating whether file represents a regular file. - /// - /// - /// true if file represents a regular file; otherwise, false. - /// - public bool IsRegularFile - { - get - { - return this.Attributes.IsRegularFile; - } - } - - /// - /// Gets a value indicating whether file represents a block device. - /// - /// - /// true if file represents a block device; otherwise, false. - /// - public bool IsBlockDevice - { - get - { - return this.Attributes.IsBlockDevice; - } - } - - /// - /// Gets a value indicating whether file represents a directory. - /// - /// - /// true if file represents a directory; otherwise, false. - /// - public bool IsDirectory - { - get - { - return this.Attributes.IsDirectory; - } - } - - /// - /// Gets a value indicating whether file represents a character device. - /// - /// - /// true if file represents a character device; otherwise, false. - /// - public bool IsCharacterDevice - { - get - { - return this.Attributes.IsCharacterDevice; - } - } - - /// - /// Gets a value indicating whether file represents a named pipe. - /// - /// - /// true if file represents a named pipe; otherwise, false. - /// - public bool IsNamedPipe - { - get - { - return this.Attributes.IsNamedPipe; - } - } - - /// - /// Gets or sets a value indicating whether the owner can read from this file. - /// - /// - /// true if owner can read from this file; otherwise, false. - /// - public bool OwnerCanRead - { - get - { - return this.Attributes.OwnerCanRead; - } - set - { - this.Attributes.OwnerCanRead = value; - } - } - - /// - /// Gets or sets a value indicating whether the owner can write into this file. - /// - /// - /// true if owner can write into this file; otherwise, false. - /// - public bool OwnerCanWrite - { - get - { - return this.Attributes.OwnerCanWrite; - } - set - { - this.Attributes.OwnerCanWrite = value; - } - } - - /// - /// Gets or sets a value indicating whether the owner can execute this file. - /// - /// - /// true if owner can execute this file; otherwise, false. - /// - public bool OwnerCanExecute - { - get - { - return this.Attributes.OwnerCanExecute; - } - set - { - this.Attributes.OwnerCanExecute = value; - } - } - - /// - /// Gets or sets a value indicating whether the group members can read from this file. - /// - /// - /// true if group members can read from this file; otherwise, false. - /// - public bool GroupCanRead - { - get - { - return this.Attributes.GroupCanRead; - } - set - { - this.Attributes.GroupCanRead = value; - } - } - - /// - /// Gets or sets a value indicating whether the group members can write into this file. - /// - /// - /// true if group members can write into this file; otherwise, false. - /// - public bool GroupCanWrite - { - get - { - return this.Attributes.GroupCanWrite; - } - set - { - this.Attributes.GroupCanWrite = value; - } - } - - /// - /// Gets or sets a value indicating whether the group members can execute this file. - /// - /// - /// true if group members can execute this file; otherwise, false. - /// - public bool GroupCanExecute - { - get - { - return this.Attributes.GroupCanExecute; - } - set - { - this.Attributes.GroupCanExecute = value; - } - } - - /// - /// Gets or sets a value indicating whether the others can read from this file. - /// - /// - /// true if others can read from this file; otherwise, false. - /// - public bool OthersCanRead - { - get - { - return this.Attributes.OthersCanRead; - } - set - { - this.Attributes.OthersCanRead = value; - } - } - - /// - /// Gets or sets a value indicating whether the others can write into this file. - /// - /// - /// true if others can write into this file; otherwise, false. - /// - public bool OthersCanWrite - { - get - { - return this.Attributes.OthersCanWrite; - } - set - { - this.Attributes.OthersCanWrite = value; - } - } - - /// - /// Gets or sets a value indicating whether the others can execute this file. - /// - /// - /// true if others can execute this file; otherwise, false. - /// - public bool OthersCanExecute - { - get - { - return this.Attributes.OthersCanExecute; - } - set - { - this.Attributes.OthersCanExecute = value; - } - } - - /// - /// Gets the extension part of the file. - /// - /// - /// File extensions. - /// - public IDictionary Extensions { get; private set; } - - /// - /// Sets file permissions. - /// - /// The mode. - public void SetPermissions(short mode) - { - this.Attributes.SetPermissions(mode); - - this.UpdateStatus(); - } - - /// - /// Permanently deletes a file on remote machine. - /// - public void Delete() - { - if (this.IsDirectory) - { - this._sftpSession.RequestRmDir(this.FullName); - } - else - { - this._sftpSession.RequestRemove(this.FullName); - } - } - - /// - /// Moves a specified file to a new location on remote machine, providing the option to specify a new file name. - /// - /// The path to move the file to, which can specify a different file name. - /// is null. - public void MoveTo(string destFileName) - { - if (destFileName == null) - throw new ArgumentNullException("destFileName"); - this._sftpSession.RequestRename(this.FullName, destFileName); - - var fullPath = this._sftpSession.GetCanonicalPath(destFileName); - - this.Name = fullPath.Substring(fullPath.LastIndexOf('/') + 1); - - this.FullName = fullPath; - } - - /// - /// Updates file status on the server. - /// - public void UpdateStatus() - { - this._sftpSession.RequestSetStat(this.FullName, this.Attributes); - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "Name {0}, Length {1}, User ID {2}, Group ID {3}, Accessed {4}, Modified {5}", this.Name, this.Length, this.UserId, this.GroupId, this.LastAccessTime, this.LastWriteTime); - } - } -} diff --git a/Renci.SshNet/Sftp/SftpFileAttributes.cs b/Renci.SshNet/Sftp/SftpFileAttributes.cs deleted file mode 100644 index ab53808..0000000 --- a/Renci.SshNet/Sftp/SftpFileAttributes.cs +++ /dev/null @@ -1,437 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Globalization; -using System.Runtime.CompilerServices; - -namespace Renci.SshNet.Sftp -{ - /// - /// Contains SFTP file attributes. - /// - public class SftpFileAttributes - { - #region Bitmask constats - - private const UInt32 S_IFMT = 0xF000; // bitmask for the file type bitfields - - private const UInt32 S_IFSOCK = 0xC000; // socket - - private const UInt32 S_IFLNK = 0xA000; // symbolic link - - private const UInt32 S_IFREG = 0x8000; // regular file - - private const UInt32 S_IFBLK = 0x6000; // block device - - private const UInt32 S_IFDIR = 0x4000; // directory - - private const UInt32 S_IFCHR = 0x2000; // character device - - private const UInt32 S_IFIFO = 0x1000; // FIFO - - private const UInt32 S_ISUID = 0x0800; // set UID bit - - private const UInt32 S_ISGID = 0x0400; // set-group-ID bit (see below) - - private const UInt32 S_ISVTX = 0x0200; // sticky bit (see below) - - private const UInt32 S_IRUSR = 0x0100; // owner has read permission - - private const UInt32 S_IWUSR = 0x0080; // owner has write permission - - private const UInt32 S_IXUSR = 0x0040; // owner has execute permission - - private const UInt32 S_IRGRP = 0x0020; // group has read permission - - private const UInt32 S_IWGRP = 0x0010; // group has write permission - - private const UInt32 S_IXGRP = 0x0008; // group has execute permission - - private const UInt32 S_IROTH = 0x0004; // others have read permission - - private const UInt32 S_IWOTH = 0x0002; // others have write permission - - private const UInt32 S_IXOTH = 0x0001; // others have execute permission - - #endregion - - private bool _isBitFiledsBitSet; - private bool _isUIDBitSet; - private bool _isGroupIDBitSet; - private bool _isStickyBitSet; - - private readonly DateTime _originalLastAccessTime; - private readonly DateTime _originalLastWriteTime; - private readonly long _originalSize; - private readonly int _originalUserId; - private readonly int _originalGroupId; - private readonly uint _originalPermissions; - private readonly IDictionary _originalExtensions; - - internal bool IsLastAccessTimeChanged - { - get { return this._originalLastAccessTime != this.LastAccessTime; } - } - - internal bool IsLastWriteTimeChanged - { - get { return this._originalLastWriteTime != this.LastWriteTime; } - } - - internal bool IsSizeChanged - { - get { return this._originalSize != this.Size; } - } - - internal bool IsUserIdChanged - { - get { return this._originalUserId != this.UserId; } - } - - internal bool IsGroupIdChanged - { - get { return this._originalGroupId != this.GroupId; } - } - - internal bool IsPermissionsChanged - { - get { return this._originalPermissions != this.Permissions; } - } - - internal bool IsExtensionsChanged - { - get { return this._originalExtensions != null && this.Extensions != null && !this._originalExtensions.SequenceEqual(this.Extensions); } - } - - /// - /// Gets or sets the time the current file or directory was last accessed. - /// - /// - /// The time that the current file or directory was last accessed. - /// - public DateTime LastAccessTime { get; set; } - - /// - /// Gets or sets the time when the current file or directory was last written to. - /// - /// - /// The time the current file was last written. - /// - public DateTime LastWriteTime { get; set; } - - /// - /// Gets or sets the size, in bytes, of the current file. - /// - /// - /// The size of the current file in bytes. - /// - public long Size { get; set; } - - /// - /// Gets or sets file user id. - /// - /// - /// File user id. - /// - public int UserId { get; set; } - - /// - /// Gets or sets file group id. - /// - /// - /// File group id. - /// - public int GroupId { get; set; } - - /// - /// Gets a value indicating whether file represents a socket. - /// - /// - /// true if file represents a socket; otherwise, false. - /// - public bool IsSocket { get; private set; } - - /// - /// Gets a value indicating whether file represents a symbolic link. - /// - /// - /// true if file represents a symbolic link; otherwise, false. - /// - public bool IsSymbolicLink { get; private set; } - - /// - /// Gets a value indicating whether file represents a regular file. - /// - /// - /// true if file represents a regular file; otherwise, false. - /// - public bool IsRegularFile { get; private set; } - - /// - /// Gets a value indicating whether file represents a block device. - /// - /// - /// true if file represents a block device; otherwise, false. - /// - public bool IsBlockDevice { get; private set; } - - /// - /// Gets a value indicating whether file represents a directory. - /// - /// - /// true if file represents a directory; otherwise, false. - /// - public bool IsDirectory { get; private set; } - - /// - /// Gets a value indicating whether file represents a character device. - /// - /// - /// true if file represents a character device; otherwise, false. - /// - public bool IsCharacterDevice { get; private set; } - - /// - /// Gets a value indicating whether file represents a named pipe. - /// - /// - /// true if file represents a named pipe; otherwise, false. - /// - public bool IsNamedPipe { get; private set; } - - /// - /// Gets a value indicating whether the owner can read from this file. - /// - /// - /// true if owner can read from this file; otherwise, false. - /// - public bool OwnerCanRead { get; set; } - - /// - /// Gets a value indicating whether the owner can write into this file. - /// - /// - /// true if owner can write into this file; otherwise, false. - /// - public bool OwnerCanWrite { get; set; } - - /// - /// Gets a value indicating whether the owner can execute this file. - /// - /// - /// true if owner can execute this file; otherwise, false. - /// - public bool OwnerCanExecute { get; set; } - - /// - /// Gets a value indicating whether the group members can read from this file. - /// - /// - /// true if group members can read from this file; otherwise, false. - /// - public bool GroupCanRead { get; set; } - - /// - /// Gets a value indicating whether the group members can write into this file. - /// - /// - /// true if group members can write into this file; otherwise, false. - /// - public bool GroupCanWrite { get; set; } - - /// - /// Gets a value indicating whether the group members can execute this file. - /// - /// - /// true if group members can execute this file; otherwise, false. - /// - public bool GroupCanExecute { get; set; } - - /// - /// Gets a value indicating whether the others can read from this file. - /// - /// - /// true if others can read from this file; otherwise, false. - /// - public bool OthersCanRead { get; set; } - - /// - /// Gets a value indicating whether the others can write into this file. - /// - /// - /// true if others can write into this file; otherwise, false. - /// - public bool OthersCanWrite { get; set; } - - /// - /// Gets a value indicating whether the others can execute this file. - /// - /// - /// true if others can execute this file; otherwise, false. - /// - public bool OthersCanExecute { get; set; } - - /// - /// Gets or sets the extensions. - /// - /// - /// The extensions. - /// - public IDictionary Extensions { get; private set; } - - internal uint Permissions - { - get - { - uint permission = 0; - - if (this._isBitFiledsBitSet) - permission = permission | S_IFMT; - - if (this.IsSocket) - permission = permission | S_IFSOCK; - - if (this.IsSymbolicLink) - permission = permission | S_IFLNK; - - if (this.IsRegularFile) - permission = permission | S_IFREG; - - if (this.IsBlockDevice) - permission = permission | S_IFBLK; - - if (this.IsDirectory) - permission = permission | S_IFDIR; - - if (this.IsCharacterDevice) - permission = permission | S_IFCHR; - - if (this.IsNamedPipe) - permission = permission | S_IFIFO; - - if (this._isUIDBitSet) - permission = permission | S_ISUID; - - if (this._isGroupIDBitSet) - permission = permission | S_ISGID; - - if (this._isStickyBitSet) - permission = permission | S_ISVTX; - - if (this.OwnerCanRead) - permission = permission | S_IRUSR; - - if (this.OwnerCanWrite) - permission = permission | S_IWUSR; - - if (this.OwnerCanExecute) - permission = permission | S_IXUSR; - - if (this.GroupCanRead) - permission = permission | S_IRGRP; - - if (this.GroupCanWrite) - permission = permission | S_IWGRP; - - if (this.GroupCanExecute) - permission = permission | S_IXGRP; - - if (this.OthersCanRead) - permission = permission | S_IROTH; - - if (this.OthersCanWrite) - permission = permission | S_IWOTH; - - if (this.OthersCanExecute) - permission = permission | S_IXOTH; - - return permission; - } - private set - { - this._isBitFiledsBitSet = ((value & S_IFMT) == S_IFMT); - - this.IsSocket = ((value & S_IFSOCK) == S_IFSOCK); - - this.IsSymbolicLink = ((value & S_IFLNK) == S_IFLNK); - - this.IsRegularFile = ((value & S_IFREG) == S_IFREG); - - this.IsBlockDevice = ((value & S_IFBLK) == S_IFBLK); - - this.IsDirectory = ((value & S_IFDIR) == S_IFDIR); - - this.IsCharacterDevice = ((value & S_IFCHR) == S_IFCHR); - - this.IsNamedPipe = ((value & S_IFIFO) == S_IFIFO); - - this._isUIDBitSet = ((value & S_ISUID) == S_ISUID); - - this._isGroupIDBitSet = ((value & S_ISGID) == S_ISGID); - - this._isStickyBitSet = ((value & S_ISVTX) == S_ISVTX); - - this.OwnerCanRead = ((value & S_IRUSR) == S_IRUSR); - - this.OwnerCanWrite = ((value & S_IWUSR) == S_IWUSR); - - this.OwnerCanExecute = ((value & S_IXUSR) == S_IXUSR); - - this.GroupCanRead = ((value & S_IRGRP) == S_IRGRP); - - this.GroupCanWrite = ((value & S_IWGRP) == S_IWGRP); - - this.GroupCanExecute = ((value & S_IXGRP) == S_IXGRP); - - this.OthersCanRead = ((value & S_IROTH) == S_IROTH); - - this.OthersCanWrite = ((value & S_IWOTH) == S_IWOTH); - - this.OthersCanExecute = ((value & S_IXOTH) == S_IXOTH); - } - } - - internal SftpFileAttributes() - { - } - - internal SftpFileAttributes(DateTime lastAccessTime, DateTime lastWriteTime, long size, int userId, int groupId, uint permissions, IDictionary extensions) - { - this.LastAccessTime = this._originalLastAccessTime = lastAccessTime; - this.LastWriteTime = this._originalLastWriteTime = lastWriteTime; - this.Size = this._originalSize = size; - this.UserId = this._originalUserId = userId; - this.GroupId = this._originalGroupId = groupId; - this.Permissions = this._originalPermissions = permissions; - this.Extensions = this._originalExtensions = extensions; - } - - /// - /// Sets the permissions. - /// - /// The mode. - public void SetPermissions(short mode) - { - if (mode < 0 || mode > 999) - { - throw new ArgumentOutOfRangeException("mode"); - } - - var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToArray(); - - var permission = (modeBytes[0] & 0x0F) * 8 * 8 + (modeBytes[1] & 0x0F) * 8 + (modeBytes[2] & 0x0F); - - this.OwnerCanRead = (permission & S_IRUSR) == S_IRUSR; - this.OwnerCanWrite = (permission & S_IWUSR) == S_IWUSR; - this.OwnerCanExecute = (permission & S_IXUSR) == S_IXUSR; - - this.GroupCanRead = (permission & S_IRGRP) == S_IRGRP; - this.GroupCanWrite = (permission & S_IWGRP) == S_IWGRP; - this.GroupCanExecute = (permission & S_IXGRP) == S_IXGRP; - - this.OthersCanRead = (permission & S_IROTH) == S_IROTH; - this.OthersCanWrite = (permission & S_IWOTH) == S_IWOTH; - this.OthersCanExecute = (permission & S_IXOTH) == S_IXOTH; - } - } -} diff --git a/Renci.SshNet/Sftp/SftpFileStream.cs b/Renci.SshNet/Sftp/SftpFileStream.cs deleted file mode 100644 index c8bd83d..0000000 --- a/Renci.SshNet/Sftp/SftpFileStream.cs +++ /dev/null @@ -1,899 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Diagnostics.CodeAnalysis; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Exposes a System.IO.Stream around a remote SFTP file, supporting both synchronous and asynchronous read and write operations. - /// - public class SftpFileStream : Stream - { - // TODO: Add security method to set userid, groupid and other permission settings - // Internal state. - private byte[] _handle; - private readonly FileAccess _access; - private readonly bool _ownsHandle; - private readonly bool _isAsync; - private SftpSession _session; - - // Buffer information. - private readonly int _readBufferSize; - private readonly byte[] _readBuffer; - private readonly int _writeBufferSize; - private readonly byte[] _writeBuffer; - private int _bufferPosition; - private int _bufferLen; - private long _position; - private bool _bufferOwnedByWrite; - private readonly bool _canSeek; - private ulong _serverFilePosition; - - private SftpFileAttributes _attributes; - - private readonly object _lock = new object(); - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get - { - return ((this._access & FileAccess.Read) != 0); - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get - { - return this._canSeek; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get - { - return ((this._access & FileAccess.Write) != 0); - } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// A long value representing the length of the stream in bytes. - /// A class derived from Stream does not support seeking. - /// Methods were called after the stream was closed. - /// IO operation failed. - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Be design this is the exception that stream need to throw.")] - public override long Length - { - get - { - // Validate that the object can actually do this. - if (!this._canSeek) - { - throw new NotSupportedException("Seek operation is not supported."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - if (this._handle == null) - { - // ECMA says this should be IOException even though - // everywhere else uses ObjectDisposedException. - throw new IOException("Stream is closed."); - } - - // Flush the write buffer, because it may - // affect the length of the stream. - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - } - - // Update file attributes - this._attributes = this._session.RequestFStat(this._handle); - - if (this._attributes != null && this._attributes.Size > -1) - { - return this._attributes.Size; - } - throw new IOException("Seek operation failed."); - } - } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// - /// An I/O error occurs. - /// - /// The stream does not support seeking. - /// - /// Methods were called after the stream was closed. - public override long Position - { - get - { - if (!this._canSeek) - { - throw new NotSupportedException("Seek operation not supported."); - } - return this._position; - } - set - { - this.Seek(value, SeekOrigin.Begin); - } - } - - /// - /// Gets a value indicating whether the FileStream was opened asynchronously or synchronously. - /// - /// - /// true if this instance is async; otherwise, false. - /// - public virtual bool IsAsync - { - get - { - return this._isAsync; - } - } - - /// - /// Gets the name of the FileStream that was passed to the constructor. - /// - public string Name { get; private set; } - - /// - /// Gets the operating system file handle for the file that the current SftpFileStream object encapsulates. - /// - public virtual byte[] Handle - { - get - { - this.Flush(); - return this._handle; - } - } - - /// - /// Gets or sets the operation timeout. - /// - /// - /// The timeout. - /// - public TimeSpan Timeout { get; set; } - - /// - /// Initializes a new instance with a read and write buffer - /// of 4 KB. - /// - internal SftpFileStream(SftpSession session, string path, FileMode mode) - : this(session, path, mode, FileAccess.ReadWrite) - { - } - - internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access) - : this(session, path, mode, access, 4096) - { - } - - internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access, int bufferSize) - : this(session, path, mode, access, bufferSize, false) - { - } - - internal SftpFileStream(SftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, bool useAsync) - { - // Validate the parameters. - if (session == null) - throw new SshConnectionException("Client not connected."); - - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (bufferSize <= 0) - { - throw new ArgumentOutOfRangeException("bufferSize"); - } - if (access < FileAccess.Read || access > FileAccess.ReadWrite) - { - throw new ArgumentOutOfRangeException("access"); - } - if (mode < FileMode.CreateNew || mode > FileMode.Append) - { - throw new ArgumentOutOfRangeException("mode"); - } - - this.Timeout = TimeSpan.FromSeconds(30); - this.Name = path; - - // Initialize the object state. - this._session = session; - this._access = access; - this._ownsHandle = true; - this._isAsync = useAsync; - this._bufferPosition = 0; - this._bufferLen = 0; - this._bufferOwnedByWrite = false; - this._canSeek = true; - this._position = 0; - this._serverFilePosition = 0; - this._session.Disconnected += Session_Disconnected; - - var flags = Flags.None; - - switch (access) - { - case FileAccess.Read: - flags |= Flags.Read; - break; - case FileAccess.Write: - flags |= Flags.Write; - break; - case FileAccess.ReadWrite: - flags |= Flags.Read; - flags |= Flags.Write; - break; - } - - switch (mode) - { - case FileMode.Append: - flags |= Flags.Append; - break; - case FileMode.Create: - this._handle = this._session.RequestOpen(path, flags | Flags.Truncate, true); - if (this._handle == null) - { - flags |= Flags.CreateNew; - } - else - { - flags |= Flags.Truncate; - } - break; - case FileMode.CreateNew: - flags |= Flags.CreateNew; - break; - case FileMode.Open: - break; - case FileMode.OpenOrCreate: - flags |= Flags.CreateNewOrOpen; - break; - case FileMode.Truncate: - flags |= Flags.Truncate; - break; - } - - if (this._handle == null) - this._handle = this._session.RequestOpen(path, flags); - - this._attributes = this._session.RequestFStat(this._handle); - - this._readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize); - this._readBuffer = new byte[_readBufferSize]; - this._writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, _handle); - this._writeBuffer = new byte[_writeBufferSize]; - - if (mode == FileMode.Append) - { - this._position = this._attributes.Size; - this._serverFilePosition = (ulong)this._attributes.Size; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~SftpFileStream() - { - this.Dispose(false); - } - - /// - /// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. - /// - public override void Close() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the file. - /// - /// An I/O error occurs. - /// Stream is closed. - public override void Flush() - { - lock (this._lock) - { - if (this._handle != null) - { - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - } - else - { - this.FlushReadBuffer(); - } - } - else - { - throw new ObjectDisposedException("Stream is closed."); - } - } - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// - /// The sum of and is larger than the buffer length. - /// is null. - /// or is negative. - /// An I/O error occurs. - /// The stream does not support reading. - /// Methods were called after the stream was closed. - public override int Read(byte[] buffer, int offset, int count) - { - int readLen = 0; - - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset"); - if (count < 0) - throw new ArgumentOutOfRangeException("count"); - if ((buffer.Length - offset) < count) - throw new ArgumentException("Invalid array range."); - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Set up for the read operation. - this.SetupRead(); - - // Read data into the caller's buffer. - while (count > 0) - { - // How much data do we have available in the buffer? - var tempLen = this._bufferLen - this._bufferPosition; - if (tempLen <= 0) - { - this._bufferPosition = 0; - - var data = this._session.RequestRead(this._handle, (ulong)this._position, (uint)this._readBufferSize); - - this._bufferLen = data.Length; - - Buffer.BlockCopy(data, 0, this._readBuffer, 0, this._bufferLen); - this._serverFilePosition = (ulong)this._position; - - if (this._bufferLen < 0) - { - this._bufferLen = 0; - // TODO: Add SFTP error code or message if possible - throw new IOException("Read operation failed."); - } - if (this._bufferLen == 0) - { - break; - } - tempLen = this._bufferLen; - } - - // Don't read more than the caller wants. - if (tempLen > count) - { - tempLen = count; - } - - // Copy stream data to the caller's buffer. - Buffer.BlockCopy(this._readBuffer, this._bufferPosition, buffer, offset, tempLen); - - // Advance to the next buffer positions. - readLen += tempLen; - offset += tempLen; - count -= tempLen; - this._bufferPosition += tempLen; - this._position += tempLen; - } - } - - // Return the number of bytes that were read to the caller. - return readLen; - } - - /// - /// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. - /// - /// - /// The unsigned byte cast to an Int32, or -1 if at the end of the stream. - /// - /// The stream does not support reading. - /// Methods were called after the stream was closed. - /// Read operation failed. - public override int ReadByte() - { - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup the object for reading. - this.SetupRead(); - - // Read more data into the internal buffer if necessary. - if (this._bufferPosition >= this._bufferLen) - { - this._bufferPosition = 0; - - var data = this._session.RequestRead(this._handle, (ulong)this._position, (uint)this._readBufferSize); - - this._bufferLen = data.Length; - Buffer.BlockCopy(data, 0, this._readBuffer, 0, this._readBufferSize); - this._serverFilePosition = (ulong)this._position; - - if (this._bufferLen < 0) - { - this._bufferLen = 0; - // TODO: Add SFTP error code or message if possible - throw new IOException("Read operation failed."); - } - if (this._bufferLen == 0) - { - // We've reached EOF. - return -1; - } - } - - // Extract the next byte from the buffer. - ++this._position; - return this._readBuffer[this._bufferPosition++]; - } - } - - /// - /// Sets the position within the current stream. - /// - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// - /// The new position within the current stream. - /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. - public override long Seek(long offset, SeekOrigin origin) - { - long newPosn = -1; - - // Bail out if this stream is not capable of seeking. - if (!this._canSeek) - { - throw new NotSupportedException("Seek is not supported."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Bail out if the handle is invalid. - if (this._handle == null) - { - throw new ObjectDisposedException("Stream is closed."); - } - - // Don't do anything if the position won't be moving. - if (origin == SeekOrigin.Begin && offset == this._position) - { - return offset; - } - if (origin == SeekOrigin.Current && offset == 0) - { - return this._position; - } - - this._attributes = this._session.RequestFStat(this._handle); - - // The behaviour depends upon the read/write mode. - if (this._bufferOwnedByWrite) - { - // Flush the write buffer and then seek. - this.FlushWriteBuffer(); - - switch (origin) - { - case SeekOrigin.Begin: - newPosn = offset; - break; - case SeekOrigin.Current: - newPosn = this._position + offset; - break; - case SeekOrigin.End: - newPosn = this._attributes.Size - offset; - break; - } - - if (newPosn == -1) - { - throw new EndOfStreamException("End of stream."); - } - this._position = newPosn; - this._serverFilePosition = (ulong)newPosn; - } - else - { - // Determine if the seek is to somewhere inside - // the current read buffer bounds. - if (origin == SeekOrigin.Begin) - { - newPosn = this._position - this._bufferPosition; - if (offset >= newPosn && offset < - (newPosn + this._bufferLen)) - { - this._bufferPosition = (int)(offset - newPosn); - this._position = offset; - return this._position; - } - } - else if (origin == SeekOrigin.Current) - { - newPosn = this._position + offset; - if (newPosn >= (this._position - this._bufferPosition) && - newPosn < (this._position - this._bufferPosition + this._bufferLen)) - { - this._bufferPosition = - (int)(newPosn - (this._position - this._bufferPosition)); - this._position = newPosn; - return this._position; - } - } - - // Abandon the read buffer. - this._bufferPosition = 0; - this._bufferLen = 0; - - // Seek to the new position. - switch (origin) - { - case SeekOrigin.Begin: - newPosn = offset; - break; - case SeekOrigin.Current: - newPosn = this._position + offset; - break; - case SeekOrigin.End: - newPosn = this._attributes.Size - offset; - break; - } - - if (newPosn < 0) - { - throw new EndOfStreamException(); - } - - this._position = newPosn; - } - return this._position; - } - } - - /// - /// When overridden in a derived class, sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - /// An I/O error occurs. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. - /// must be greater than zero. - public override void SetLength(long value) - { - // Validate the parameters and setup the object for writing. - if (value < 0) - { - throw new ArgumentOutOfRangeException("value"); - } - if (!this._canSeek) - { - throw new NotSupportedException("Seek is not supported."); - } - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup this object for writing. - this.SetupWrite(); - - this._attributes.Size = value; - - this._session.RequestFSetStat(this._handle, this._attributes); - } - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies bytes from to the current stream. - /// The zero-based byte offset in at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// The sum of and is greater than the buffer length. - /// is null. - /// or is negative. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. - public override void Write(byte[] buffer, int offset, int count) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - if (offset < 0) - throw new ArgumentOutOfRangeException("offset"); - if (count < 0) - throw new ArgumentOutOfRangeException("count"); - if ((buffer.Length - offset) < count) - throw new ArgumentException("Invalid array range."); - - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup this object for writing. - this.SetupWrite(); - - // Write data to the file stream. - while (count > 0) - { - // Determine how many bytes we can write to the buffer. - var tempLen = this._writeBufferSize - this._bufferPosition; - if (tempLen <= 0) - { - var data = new byte[this._bufferPosition]; - - Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosition = 0; - tempLen = this._writeBufferSize; - } - if (tempLen > count) - { - tempLen = count; - } - - // Can we short-cut the internal buffer? - if (this._bufferPosition == 0 && tempLen == this._writeBufferSize) - { - // Yes: write the data directly to the file. - var data = new byte[tempLen]; - - Buffer.BlockCopy(buffer, offset, data, 0, tempLen); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - } - else - { - // No: copy the data to the write buffer first. - Buffer.BlockCopy(buffer, offset, _writeBuffer, this._bufferPosition, tempLen); - this._bufferPosition += tempLen; - } - - // Advance the buffer and stream positions. - this._position += tempLen; - offset += tempLen; - count -= tempLen; - } - - // If the buffer is full, then do a speculative flush now, - // rather than waiting for the next call to this method. - if (this._bufferPosition >= _writeBufferSize) - { - var data = new byte[this._bufferPosition]; - - Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosition = 0; - } - } - } - - /// - /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. - /// - /// The byte to write to the stream. - /// An I/O error occurs. - /// The stream does not support writing, or the stream is already closed. - /// Methods were called after the stream was closed. - public override void WriteByte(byte value) - { - // Lock down the file stream while we do this. - lock (this._lock) - { - // Setup the object for writing. - this.SetupWrite(); - - // Flush the current buffer if it is full. - if (this._bufferPosition >= this._writeBufferSize) - { - var data = new byte[this._bufferPosition]; - - Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosition = 0; - } - - // Write the byte into the buffer and advance the posn. - _writeBuffer[this._bufferPosition++] = value; - ++this._position; - } - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._session != null) - { - lock (this._lock) - { - if (this._session != null) - { - if (this._handle != null) - { - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - } - - if (this._ownsHandle) - { - this._session.RequestClose(this._handle); - } - - this._handle = null; - } - - this._session.Disconnected -= Session_Disconnected; - this._session = null; - } - } - } - } - - /// - /// Flushes the read data from the buffer. - /// - private void FlushReadBuffer() - { - if (this._canSeek) - { - if (this._bufferPosition < this._bufferLen) - { - this._position -= this._bufferPosition; - } - this._bufferPosition = 0; - this._bufferLen = 0; - } - } - - /// - /// Flush any buffered write data to the file. - /// - private void FlushWriteBuffer() - { - if (this._bufferPosition > 0) - { - var data = new byte[this._bufferPosition]; - - Buffer.BlockCopy(this._writeBuffer, 0, data, 0, this._bufferPosition); - - using (var wait = new AutoResetEvent(false)) - { - this._session.RequestWrite(this._handle, this._serverFilePosition, data, wait); - this._serverFilePosition += (ulong)data.Length; - } - - this._bufferPosition = 0; - } - } - - /// - /// Setups the read. - /// - private void SetupRead() - { - if ((this._access & FileAccess.Read) == 0) - { - throw new NotSupportedException("Read not supported."); - } - if (this._handle == null) - { - throw new ObjectDisposedException("Stream is closed."); - } - if (this._bufferOwnedByWrite) - { - this.FlushWriteBuffer(); - this._bufferOwnedByWrite = false; - } - } - - /// - /// Setups the write. - /// - private void SetupWrite() - { - if ((this._access & FileAccess.Write) == 0) - { - throw new NotSupportedException("Write not supported."); - } - if (this._handle == null) - { - throw new ObjectDisposedException("Stream is closed."); - } - if (!this._bufferOwnedByWrite) - { - this.FlushReadBuffer(); - this._bufferOwnedByWrite = true; - } - } - - private void Session_Disconnected(object sender, EventArgs e) - { - lock (this._lock) - { - this._session.Disconnected -= Session_Disconnected; - this._session = null; - } - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpFileSystemInformation.cs b/Renci.SshNet/Sftp/SftpFileSystemInformation.cs deleted file mode 100644 index e5bb503..0000000 --- a/Renci.SshNet/Sftp/SftpFileSystemInformation.cs +++ /dev/null @@ -1,136 +0,0 @@ -namespace Renci.SshNet.Sftp -{ - /// - /// Contains File system information exposed by statvfs@openssh.com request. - /// - public class SftpFileSytemInformation - { - private readonly ulong _flag; - - private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; - - private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - public ulong BlockSize { get; private set; } - - /// - /// Gets the total blocks. - /// - /// - /// The total blocks. - /// - public ulong TotalBlocks { get; private set; } - - /// - /// Gets the free blocks. - /// - /// - /// The free blocks. - /// - public ulong FreeBlocks { get; private set; } - - /// - /// Gets the available blocks. - /// - /// - /// The available blocks. - /// - public ulong AvailableBlocks { get; private set; } - - /// - /// Gets the total nodes. - /// - /// - /// The total nodes. - /// - public ulong TotalNodes { get; private set; } - - /// - /// Gets the free nodes. - /// - /// - /// The free nodes. - /// - public ulong FreeNodes { get; private set; } - - /// - /// Gets the available nodes. - /// - /// - /// The available nodes. - /// - public ulong AvailableNodes { get; private set; } - - /// - /// Gets the sid. - /// - /// - /// The sid. - /// - public ulong Sid { get; private set; } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } - } - - /// - /// Gets a value indicating whether [supports set uid]. - /// - /// - /// true if [supports set uid]; otherwise, false. - /// - public bool SupportsSetUid - { - get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } - } - - /// - /// Gets the max name lenght. - /// - /// - /// The max name lenght. - /// - public ulong MaxNameLenght { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bsize. - /// The frsize. - /// The blocks. - /// The bfree. - /// The bavail. - /// The files. - /// The ffree. - /// The favail. - /// The sid. - /// The flag. - /// The namemax. - internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) - { - this.BlockSize = frsize; - this.TotalBlocks = blocks; - this.FreeBlocks = bfree; - this.AvailableBlocks = bavail; - this.TotalNodes = files; - this.FreeNodes = ffree; - this.AvailableNodes = favail; - this.Sid = sid; - this._flag = flag; - this.MaxNameLenght = namemax; - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpFilesystemInformation.cs b/Renci.SshNet/Sftp/SftpFilesystemInformation.cs deleted file mode 100644 index e5bb503..0000000 --- a/Renci.SshNet/Sftp/SftpFilesystemInformation.cs +++ /dev/null @@ -1,136 +0,0 @@ -namespace Renci.SshNet.Sftp -{ - /// - /// Contains File system information exposed by statvfs@openssh.com request. - /// - public class SftpFileSytemInformation - { - private readonly ulong _flag; - - private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1; - - private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2; - - /// - /// Gets the size of the block. - /// - /// - /// The size of the block. - /// - public ulong BlockSize { get; private set; } - - /// - /// Gets the total blocks. - /// - /// - /// The total blocks. - /// - public ulong TotalBlocks { get; private set; } - - /// - /// Gets the free blocks. - /// - /// - /// The free blocks. - /// - public ulong FreeBlocks { get; private set; } - - /// - /// Gets the available blocks. - /// - /// - /// The available blocks. - /// - public ulong AvailableBlocks { get; private set; } - - /// - /// Gets the total nodes. - /// - /// - /// The total nodes. - /// - public ulong TotalNodes { get; private set; } - - /// - /// Gets the free nodes. - /// - /// - /// The free nodes. - /// - public ulong FreeNodes { get; private set; } - - /// - /// Gets the available nodes. - /// - /// - /// The available nodes. - /// - public ulong AvailableNodes { get; private set; } - - /// - /// Gets the sid. - /// - /// - /// The sid. - /// - public ulong Sid { get; private set; } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; } - } - - /// - /// Gets a value indicating whether [supports set uid]. - /// - /// - /// true if [supports set uid]; otherwise, false. - /// - public bool SupportsSetUid - { - get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; } - } - - /// - /// Gets the max name lenght. - /// - /// - /// The max name lenght. - /// - public ulong MaxNameLenght { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The bsize. - /// The frsize. - /// The blocks. - /// The bfree. - /// The bavail. - /// The files. - /// The ffree. - /// The favail. - /// The sid. - /// The flag. - /// The namemax. - internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax) - { - this.BlockSize = frsize; - this.TotalBlocks = blocks; - this.FreeBlocks = bfree; - this.AvailableBlocks = bavail; - this.TotalNodes = files; - this.FreeNodes = ffree; - this.AvailableNodes = favail; - this.Sid = sid; - this._flag = flag; - this.MaxNameLenght = namemax; - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs b/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs deleted file mode 100644 index 13093d2..0000000 --- a/Renci.SshNet/Sftp/SftpListDirectoryAsyncResult.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous directory list operation. - /// - public class SftpListDirectoryAsyncResult : AsyncResult> - { - /// - /// Gets the number of files read so far. - /// - public int FilesRead { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpListDirectoryAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - - } - - /// - /// Updates asynchronous operation status information. - /// - /// The files read. - internal void Update(int filesRead) - { - this.FilesRead = filesRead; - } - } -} diff --git a/Renci.SshNet/Sftp/SftpMessage.cs b/Renci.SshNet/Sftp/SftpMessage.cs deleted file mode 100644 index c187a41..0000000 --- a/Renci.SshNet/Sftp/SftpMessage.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Renci.SshNet.Common; -using System.Globalization; -using Renci.SshNet.Sftp.Responses; -using System.Text; - -namespace Renci.SshNet.Sftp -{ - internal abstract class SftpMessage : SshData - { - public static SftpMessage Load(uint protocolVersion, byte[] data, Encoding encoding) - { - var messageType = (SftpMessageTypes)data.FirstOrDefault(); - - return Load(protocolVersion, data, messageType, encoding); - } - - protected override int ZeroReaderIndex - { - get - { - return 1; - } - } - - public abstract SftpMessageTypes SftpMessageType { get; } - - protected override void LoadData() - { - } - - protected override void SaveData() - { - this.Write((byte)this.SftpMessageType); - } - - protected SftpFileAttributes ReadAttributes() - { - - var flag = this.ReadUInt32(); - - long size = -1; - int userId = -1; - int groupId = -1; - uint permissions = 0; - var accessTime = DateTime.MinValue; - var modifyTime = DateTime.MinValue; - IDictionary extensions = null; - - if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE - { - size = (long)this.ReadUInt64(); - } - - if ((flag & 0x00000002) == 0x00000002) // SSH_FILEXFER_ATTR_UIDGID - { - userId = (int)this.ReadUInt32(); - - groupId = (int)this.ReadUInt32(); - } - - if ((flag & 0x00000004) == 0x00000004) // SSH_FILEXFER_ATTR_PERMISSIONS - { - permissions = this.ReadUInt32(); - } - - if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME - { - var time = this.ReadUInt32(); - accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000); - time = this.ReadUInt32(); - modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000); - } - - if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_ACMODTIME - { - var extendedCount = this.ReadUInt32(); - extensions = this.ReadExtensionPair(); - } - var attributes = new SftpFileAttributes(accessTime, modifyTime, size, userId, groupId, permissions, extensions); - - return attributes; - } - - protected void Write(SftpFileAttributes attributes) - { - if (attributes == null) - { - this.Write((uint)0); - return; - } - - UInt32 flag = 0; - - if (attributes.IsSizeChanged && attributes.IsRegularFile) - { - flag |= 0x00000001; - } - - if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) - { - flag |= 0x00000002; - } - - if (attributes.IsPermissionsChanged) - { - flag |= 0x00000004; - } - - if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) - { - flag |= 0x00000008; - } - - if (attributes.IsExtensionsChanged) - { - flag |= 0x80000000; - } - - this.Write(flag); - - if (attributes.IsSizeChanged && attributes.IsRegularFile) - { - this.Write((UInt64)attributes.Size); - } - - if (attributes.IsUserIdChanged|| attributes.IsGroupIdChanged) - { - this.Write((UInt32)attributes.UserId); - this.Write((UInt32)attributes.GroupId); - } - - if (attributes.IsPermissionsChanged) - { - this.Write(attributes.Permissions); - } - - if (attributes.IsLastAccessTimeChanged || attributes.IsLastWriteTimeChanged) - { - var time = (uint)(attributes.LastAccessTime.ToFileTime() / 10000000 - 11644473600); - this.Write(time); - time = (uint)(attributes.LastWriteTime.ToFileTime() / 10000000 - 11644473600); - this.Write(time); - } - - if (attributes.IsExtensionsChanged) - { - this.Write(attributes.Extensions); - } - } - - private static SftpMessage Load(uint protocolVersion, byte[] data, SftpMessageTypes messageType, Encoding encoding) - { - SftpMessage message; - - switch (messageType) - { - case SftpMessageTypes.Version: - message = new SftpVersionResponse(); - break; - case SftpMessageTypes.Status: - message = new SftpStatusResponse(protocolVersion); - break; - case SftpMessageTypes.Data: - message = new SftpDataResponse(protocolVersion); - break; - case SftpMessageTypes.Handle: - message = new SftpHandleResponse(protocolVersion); - break; - case SftpMessageTypes.Name: - message = new SftpNameResponse(protocolVersion, encoding); - break; - case SftpMessageTypes.Attrs: - message = new SftpAttrsResponse(protocolVersion); - break; - case SftpMessageTypes.ExtendedReply: - message = new SftpExtendedReplyResponse(protocolVersion); - break; - default: - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", messageType)); - } - - message.LoadBytes(data); - - message.ResetReader(); - - message.LoadData(); - - return message; - } - - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "SFTP Message : {0}", this.SftpMessageType); - } - } -} diff --git a/Renci.SshNet/Sftp/SftpMessageTypes.cs b/Renci.SshNet/Sftp/SftpMessageTypes.cs deleted file mode 100644 index b87bb8d..0000000 --- a/Renci.SshNet/Sftp/SftpMessageTypes.cs +++ /dev/null @@ -1,130 +0,0 @@ - -namespace Renci.SshNet.Sftp -{ - internal enum SftpMessageTypes : byte - { - /// - /// SSH_FXP_INIT - /// - Init = 1, - /// - /// SSH_FXP_VERSION - /// - Version = 2, - /// - /// SSH_FXP_OPEN - /// - Open = 3, - /// - /// SSH_FXP_CLOSE - /// - Close = 4, - /// - /// SSH_FXP_READ - /// - Read = 5, - /// - /// SSH_FXP_WRITE - /// - Write = 6, - /// - /// SSH_FXP_LSTAT - /// - LStat = 7, - /// - /// SSH_FXP_FSTAT - /// - FStat = 8, - /// - /// SSH_FXP_SETSTAT - /// - SetStat = 9, - /// - /// SSH_FXP_FSETSTAT - /// - FSetStat = 10, - /// - /// SSH_FXP_OPENDIR - /// - OpenDir = 11, - /// - /// SSH_FXP_READDIR - /// - ReadDir = 12, - /// - /// SSH_FXP_REMOVE - /// - Remove = 13, - /// - /// SSH_FXP_MKDIR - /// - MkDir = 14, - /// - /// SSH_FXP_RMDIR - /// - RmDir = 15, - /// - /// SSH_FXP_REALPATH - /// - RealPath = 16, - /// - /// SSH_FXP_STAT - /// - Stat = 17, - /// - /// SSH_FXP_RENAME - /// - Rename = 18, - /// - /// SSH_FXP_READLINK - /// - ReadLink = 19, - /// - /// SSH_FXP_SYMLINK - /// - SymLink = 20, - /// - /// SSH_FXP_LINK - /// - Link = 21, - /// - /// SSH_FXP_BLOCK - /// - Block = 22, - /// - /// SSH_FXP_UNBLOCK - /// - Unblock = 23, - - /// - /// SSH_FXP_STATUS - /// - Status = 101, - /// - /// SSH_FXP_HANDLE - /// - Handle = 102, - /// - /// SSH_FXP_DATA - /// - Data = 103, - /// - /// SSH_FXP_NAME - /// - Name = 104, - /// - /// SSH_FXP_ATTRS - /// - Attrs = 105, - - /// - /// SSH_FXP_EXTENDED - /// - Extended = 200, - /// - /// SSH_FXP_EXTENDED_REPLY - /// - ExtendedReply = 201 - - } -} diff --git a/Renci.SshNet/Sftp/SftpSession.cs b/Renci.SshNet/Sftp/SftpSession.cs deleted file mode 100644 index dc1a735..0000000 --- a/Renci.SshNet/Sftp/SftpSession.cs +++ /dev/null @@ -1,1188 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using System.Collections.Generic; -using System.Globalization; -using Renci.SshNet.Sftp.Responses; -using Renci.SshNet.Sftp.Requests; -using System.Runtime.CompilerServices; - -namespace Renci.SshNet.Sftp -{ - internal class SftpSession : SubsystemSession - { - private const int MAXIMUM_SUPPORTED_VERSION = 3; - - private const int MINIMUM_SUPPORTED_VERSION = 0; - - private readonly Dictionary _requests = new Dictionary(); - - private readonly List _data = new List(16 * 1024); - - private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); - - internal IDictionary _supportedExtensions; - - /// - /// Gets remote working directory. - /// - public string WorkingDirectory { get; private set; } - - /// - /// Gets SFTP protocol version. - /// - public uint ProtocolVersion { get; private set; } - - private long _requestId; - /// - /// Gets the next request id for sftp session. - /// - public uint NextRequestId - { - get - { -#if WINDOWS_PHONE - lock (this) - { - this._requestId++; - } - - return (uint)this._requestId; -#else - return ((uint)Interlocked.Increment(ref this._requestId)); -#endif - } - } - - public SftpSession(Session session, TimeSpan operationTimeout, Encoding encoding) - : base(session, "sftp", operationTimeout, encoding) - { - } - - public void ChangeDirectory(string path) - { - var fullPath = this.GetCanonicalPath(path); - - var handle = this.RequestOpenDir(fullPath); - - this.RequestClose(handle); - - this.WorkingDirectory = fullPath; - } - - internal void SendMessage(SftpMessage sftpMessage) - { - var messageData = sftpMessage.GetBytes(); - - var data = new byte[4 + messageData.Length]; - - ((uint)messageData.Length).GetBytes().CopyTo(data, 0); - messageData.CopyTo(data, 4); - - this.SendData(data); - } - - /// - /// Resolves path into absolute path on the server. - /// - /// Path to resolve. - /// Absolute path - internal string GetCanonicalPath(string path) - { - var fullPath = GetFullRemotePath(path); - - var canonizedPath = string.Empty; - - var realPathFiles = this.RequestRealPath(fullPath, true); - - if (realPathFiles != null) - { - canonizedPath = realPathFiles.First().Key; - } - - if (!string.IsNullOrEmpty(canonizedPath)) - return canonizedPath; - - // Check for special cases - if (fullPath.EndsWith("/.", StringComparison.InvariantCultureIgnoreCase) || - fullPath.EndsWith("/..", StringComparison.InvariantCultureIgnoreCase) || - fullPath.Equals("/", StringComparison.InvariantCultureIgnoreCase) || - fullPath.IndexOf('/') < 0) - return fullPath; - - var pathParts = fullPath.Split(new char[] { '/' }); - - var partialFullPath = string.Join("/", pathParts, 0, pathParts.Length - 1); - - if (string.IsNullOrEmpty(partialFullPath)) - partialFullPath = "/"; - - realPathFiles = this.RequestRealPath(partialFullPath, true); - - if (realPathFiles != null) - { - canonizedPath = realPathFiles.First().Key; - } - - if (string.IsNullOrEmpty(canonizedPath)) - { - return fullPath; - } - - var slash = string.Empty; - if (canonizedPath[canonizedPath.Length - 1] != '/') - slash = "/"; - return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", canonizedPath, slash, pathParts[pathParts.Length - 1]); - } - - internal string GetFullRemotePath(string path) - { - var fullPath = path; - - if (!string.IsNullOrEmpty(path) && path[0] != '/' && this.WorkingDirectory != null) - { - if (this.WorkingDirectory[this.WorkingDirectory.Length - 1] == '/') - { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}{1}", this.WorkingDirectory, path); - } - else - { - fullPath = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", this.WorkingDirectory, path); - } - } - return fullPath; - } - - protected override void OnChannelOpen() - { - this.SendMessage(new SftpInitRequest(MAXIMUM_SUPPORTED_VERSION)); - - this.WaitOnHandle(this._sftpVersionConfirmed, this._operationTimeout); - - if (this.ProtocolVersion > MAXIMUM_SUPPORTED_VERSION || this.ProtocolVersion < MINIMUM_SUPPORTED_VERSION) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Server SFTP version {0} is not supported.", this.ProtocolVersion)); - } - - // Resolve current directory - this.WorkingDirectory = this.RequestRealPath(".").First().Key; - } - - protected override void OnDataReceived(uint dataTypeCode, byte[] data) - { - // Add channel data to internal data holder - this._data.AddRange(data); - - while (this._data.Count > 4 + 1) - { - // Extract packet length - var packetLength = (this._data[0] << 24 | this._data[1] << 16 | this._data[2] << 8 | this._data[3]); - - // Check if complete packet data is available - if (this._data.Count < packetLength + 4) - { - // Wait for complete message to arrive first - break; - } - this._data.RemoveRange(0, 4); - - // Create buffer to hold packet data - var packetData = new byte[packetLength]; - - // Cope packet data to array - this._data.CopyTo(0, packetData, 0, packetLength); - - // Remove loaded data from _data holder - this._data.RemoveRange(0, packetLength); - - // Load SFTP Message and handle it - var response = SftpMessage.Load(this.ProtocolVersion, packetData, this.Encoding); - - try - { - var versionResponse = response as SftpVersionResponse; - if (versionResponse != null) - { - this.ProtocolVersion = versionResponse.Version; - this._supportedExtensions = versionResponse.Extentions; - - this._sftpVersionConfirmed.Set(); - } - else - { - this.HandleResponse(response as SftpResponse); - } - } - catch (Exception exp) - { - this.RaiseError(exp); - break; - } - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - { - if (this._sftpVersionConfirmed != null) - { - this._sftpVersionConfirmed.Dispose(); - this._sftpVersionConfirmed = null; - } - } - } - - private void SendRequest(SftpRequest request) - { - lock (this._requests) - { - this._requests.Add(request.RequestId, request); - } - - this.SendMessage(request); - } - - #region SFTP API functions - - /// - /// Performs SSH_FXP_OPEN request - /// - /// The path. - /// The flags. - /// if set to true returns null instead of throwing an exception. - /// File handle. - internal byte[] RequestOpen(string path, Flags flags, bool nullOnError = false) - { - byte[] handle = null; - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpOpenRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, flags, - response => - { - handle = response.Handle; - wait.Set(); - }, - response => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return handle; - } - - /// - /// Performs SSH_FXP_CLOSE request. - /// - /// The handle. - internal void RequestClose(byte[] handle) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpCloseRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_READ request. - /// - /// The handle. - /// The offset. - /// The length. - /// data array; null if EOF - internal byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length) - { - SshException exception = null; - - var data = new byte[0]; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, length, - (response) => - { - data = response.Data; - wait.Set(); - }, - (response) => - { - if (response.StatusCode != StatusCodes.Eof) - { - exception = this.GetSftpException(response); - } - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - - return data; - } - - /// - /// Performs SSH_FXP_READ request. - /// - /// The handle. - /// The offset. - /// The length. - /// data array; null if EOF - internal bool RequestReadAsync(byte[] handle, UInt64 offset, UInt32 length, Action readCompleted) - { - SshException exception = null; - - var request = new SftpReadRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, length, - (response) =>//data - { - readCompleted(response); - }, - (response) =>//status, eof, no data in area - { - SftpDataResponse dataResponse = new SftpDataResponse(response.ProtocolVersion); - dataResponse.Data = null; - readCompleted(dataResponse); - }); - - this.SendRequest(request); - - return true; - } - - /// - /// Performs SSH_FXP_WRITE request. - /// - /// The handle. - /// The offset. - /// The data to send. - /// The wait event handle if needed. - /// The callback to invoke when the write has completed. - internal void RequestWrite(byte[] handle, UInt64 offset, byte[] data, EventWaitHandle wait, Action writeCompleted = null) - { - SshException exception = null; - - var request = new SftpWriteRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, data, - (response) => - { - if (writeCompleted != null) - { - writeCompleted(response); - } - - exception = this.GetSftpException(response); - if (wait != null) - wait.Set(); - }); - - this.SendRequest(request); - - if (wait != null) - this.WaitOnHandle(wait, this._operationTimeout); - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_LSTAT request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - internal SftpFileAttributes RequestLStat(string path, bool nullOnError = false) - { - SshException exception = null; - - SftpFileAttributes attributes = null; - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpLStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return attributes; - } - - /// - /// Performs SSH_FXP_FSTAT request. - /// - /// The handle. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - internal SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false) - { - SshException exception = null; - SftpFileAttributes attributes = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpFStatRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return attributes; - } - - /// - /// Performs SSH_FXP_SETSTAT request. - /// - /// The path. - /// The attributes. - internal void RequestSetStat(string path, SftpFileAttributes attributes) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpSetStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, attributes, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_FSETSTAT request. - /// - /// The handle. - /// The attributes. - internal void RequestFSetStat(byte[] handle, SftpFileAttributes attributes) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpFSetStatRequest(this.ProtocolVersion, this.NextRequestId, handle, attributes, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_OPENDIR request - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// File handle. - internal byte[] RequestOpenDir(string path, bool nullOnError = false) - { - SshException exception = null; - - byte[] handle = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpOpenDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - handle = response.Handle; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return handle; - } - - /// - /// Performs SSH_FXP_READDIR request - /// - /// The handle. - /// - internal KeyValuePair[] RequestReadDir(byte[] handle) - { - SshException exception = null; - - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadDirRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - result = response.Files; - wait.Set(); - }, - (response) => - { - if (response.StatusCode != StatusCodes.Eof) - { - exception = this.GetSftpException(response); - } - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - - return result; - } - - /// - /// Performs SSH_FXP_REMOVE request. - /// - /// The path. - internal void RequestRemove(string path) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRemoveRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_MKDIR request. - /// - /// The path. - internal void RequestMkDir(string path) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpMkDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_RMDIR request. - /// - /// The path. - internal void RequestRmDir(string path) - { - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRmDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_REALPATH request - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - internal KeyValuePair[] RequestRealPath(string path, bool nullOnError = false) - { - SshException exception = null; - - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRealPathRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - result = response.Files; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return result; - } - - /// - /// Performs SSH_FXP_STAT request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - /// File attributes - /// - internal SftpFileAttributes RequestStat(string path, bool nullOnError = false) - { - SshException exception = null; - - SftpFileAttributes attributes = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - attributes = response.Attributes; - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return attributes; - } - - /// - /// Performs SSH_FXP_RENAME request. - /// - /// The old path. - /// The new path. - internal void RequestRename(string oldPath, string newPath) - { - if (this.ProtocolVersion < 2) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs SSH_FXP_READLINK request. - /// - /// The path. - /// if set to true returns null instead of throwing an exception. - /// - internal KeyValuePair[] RequestReadLink(string path, bool nullOnError = false) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - KeyValuePair[] result = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpReadLinkRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - result = response.Files; - - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return result; - } - - /// - /// Performs SSH_FXP_SYMLINK request. - /// - /// The linkpath. - /// The targetpath. - internal void RequestSymLink(string linkpath, string targetpath) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new SftpSymLinkRequest(this.ProtocolVersion, this.NextRequestId, linkpath, targetpath, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - #endregion - - #region SFTP Extended API functions - - /// - /// Performs posix-rename@openssh.com extended request. - /// - /// The old path. - /// The new path. - internal void RequestPosixRename(string oldPath, string newPath) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new PosixRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - /// - /// Performs statvfs@openssh.com extended request. - /// - /// The path. - /// if set to true [null on error]. - /// - internal SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - SftpFileSytemInformation information = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new StatVfsRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, - (response) => - { - information = response.GetReply().Information; - - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return information; - } - - /// - /// Performs fstatvfs@openssh.com extended request. - /// - /// The file handle. - /// if set to true [null on error]. - /// - /// - internal SftpFileSytemInformation RequestFStatVfs(byte[] handle, bool nullOnError = false) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - SftpFileSytemInformation information = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new FStatVfsRequest(this.ProtocolVersion, this.NextRequestId, handle, - (response) => - { - information = response.GetReply().Information; - - wait.Set(); - }, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (!nullOnError && exception != null) - { - throw exception; - } - - return information; - } - - /// - /// Performs hardlink@openssh.com extended request. - /// - /// The old path. - /// The new path. - internal void HardLink(string oldPath, string newPath) - { - if (this.ProtocolVersion < 3) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion)); - } - - SshException exception = null; - - using (var wait = new AutoResetEvent(false)) - { - var request = new HardLinkRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, - (response) => - { - exception = this.GetSftpException(response); - wait.Set(); - }); - - if (!this._supportedExtensions.ContainsKey(request.Name)) - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Extension method {0} currently not supported by the server.", request.Name)); - - this.SendRequest(request); - - this.WaitOnHandle(wait, this._operationTimeout); - } - - if (exception != null) - { - throw exception; - } - } - - #endregion - - /// - /// Calculates the optimal size of the buffer to read data from the channel. - /// - /// The buffer size configured on the client. - /// - /// The optimal size of the buffer to read data from the channel. - /// - internal uint CalculateOptimalReadLength(uint bufferSize) - { - // a SSH_FXP_DATA message has 13 bytes of protocol fields: - // bytes 1 to 4: packet length - // byte 5: message type - // bytes 6 to 9: response id - // bytes 10 to 13: length of payload‏ - // - // most ssh servers limit the size of the payload of a SSH_MSG_CHANNEL_DATA - // response to 16 KB; if we requested 16 KB of data, then the SSH_FXP_DATA - // payload of the SSH_MSG_CHANNEL_DATA message would be too big (16 KB + 13 bytes), and - // as a result, the ssh server would split this into two responses: - // one containing 16384 bytes (13 bytes header, and 16371 bytes file data) - // and one with the remaining 13 bytes of file data - const uint lengthOfNonDataProtocolFields = 13u; - var maximumPacketSize = Channel.LocalPacketSize; - return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields; - } - - /// - /// Calculates the optimal size of the buffer to write data on the channel. - /// - /// The buffer size configured on the client. - /// The file handle. - /// - /// The optimal size of the buffer to write data on the channel. - /// - /// - /// Currently, we do not take the remote window size into account. - /// - internal uint CalculateOptimalWriteLength(uint bufferSize, byte[] handle) - { - // 1-4: package length of SSH_FXP_WRITE message - // 5: message type - // 6-9: request id - // 10-13: handle length - // - // 14-21: offset - // 22-25: data length - var lengthOfNonDataProtocolFields = 25u + (uint)handle.Length; - var maximumPacketSize = Channel.RemotePacketSize; - return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields; - } - - private SshException GetSftpException(SftpStatusResponse response) - { - if (response.StatusCode == StatusCodes.Ok) - { - return null; - } - if (response.StatusCode == StatusCodes.PermissionDenied) - { - return new SftpPermissionDeniedException(response.ErrorMessage); - } - else if (response.StatusCode == StatusCodes.NoSuchFile) - { - return new SftpPathNotFoundException(response.ErrorMessage); - } - else - { - return new SshException(response.ErrorMessage); - } - } - - private void HandleResponse(SftpResponse response) - { - SftpRequest request; - lock (this._requests) - { - this._requests.TryGetValue(response.ResponseId, out request); - if (request != null) - { - this._requests.Remove(response.ResponseId); - } - } - - if (request == null) - throw new InvalidOperationException("Invalid response."); - - request.Complete(response); - } - } -} diff --git a/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs b/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs deleted file mode 100644 index 38b824f..0000000 --- a/Renci.SshNet/Sftp/SftpSynchronizeDirectoriesAsyncResult.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using Renci.SshNet.Common; -using System.IO; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous directory synchronization operation. - /// - public class SftpSynchronizeDirectoriesAsyncResult : AsyncResult> - { - /// - /// Gets the number of files read so far. - /// - public int FilesRead { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpSynchronizeDirectoriesAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - } - - /// - /// Updates asynchronous operation status information. - /// - /// The files read. - internal void Update(int filesRead) - { - this.FilesRead = filesRead; - } - } -} diff --git a/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs b/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs deleted file mode 100644 index 521481d..0000000 --- a/Renci.SshNet/Sftp/SftpUploadAsyncResult.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Renci.SshNet.Common; - -namespace Renci.SshNet.Sftp -{ - /// - /// Encapsulates the results of an asynchronous upload operation. - /// - public class SftpUploadAsyncResult : AsyncResult - { - /// - /// Gets or sets a value indicating whether to cancel asynchronous upload operation - /// - /// - /// true if upload operation to be canceled; otherwise, false. - /// - /// - /// Upload operation will be canceled after finishing uploading current buffer. - /// - public bool IsUploadCanceled { get; set; } - - /// - /// Gets the number of uploaded bytes. - /// - public ulong UploadedBytes { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The async callback. - /// The state. - public SftpUploadAsyncResult(AsyncCallback asyncCallback, Object state) - : base(asyncCallback, state) - { - } - - /// - /// Updates asynchronous operation status information. - /// - /// Number of uploaded bytes. - internal void Update(ulong uploadedBytes) - { - this.UploadedBytes = uploadedBytes; - } - } -} diff --git a/Renci.SshNet/Sftp/StatusCodes.cs b/Renci.SshNet/Sftp/StatusCodes.cs deleted file mode 100644 index f13700b..0000000 --- a/Renci.SshNet/Sftp/StatusCodes.cs +++ /dev/null @@ -1,135 +0,0 @@ - -namespace Renci.SshNet.Sftp -{ - internal enum StatusCodes : uint - { - /// - /// SSH_FX_OK - /// - Ok = 0, - /// - /// SSH_FX_EOF - /// - Eof = 1, - /// - /// SSH_FX_NO_SUCH_FILE - /// - NoSuchFile = 2, - /// - /// SSH_FX_PERMISSION_DENIED - /// - PermissionDenied = 3, - /// - /// SSH_FX_FAILURE - /// - Failure = 4, - /// - /// SSH_FX_BAD_MESSAGE - /// - BadMessage = 5, - /// - /// SSH_FX_NO_CONNECTION - /// - NoConnection = 6, - /// - /// SSH_FX_CONNECTION_LOST - /// - ConnectionLost = 7, - /// - /// SSH_FX_OP_UNSUPPORTED - /// - OperationUnsupported = 8, - /// - /// SSH_FX_INVALID_HANDLE - /// - InvalidHandle = 9, - /// - /// SSH_FX_NO_SUCH_PATH - /// - NoSuchPath = 10, - /// - /// SSH_FX_FILE_ALREADY_EXISTS - /// - FileAlreadyExists = 11, - /// - /// SSH_FX_WRITE_PROTECT - /// - WriteProtect = 12, - /// - /// SSH_FX_NO_MEDIA - /// - NoMedia = 13, - /// - /// SSH_FX_NO_SPACE_ON_FILESYSTEM - /// - NoSpaceOnFilesystem = 14, - /// - /// SSH_FX_QUOTA_EXCEEDED - /// - QuotaExceeded = 15, - /// - /// SSH_FX_UNKNOWN_PRINCIPAL - /// - UnknownPrincipal = 16, - /// - /// SSH_FX_LOCK_CONFLICT - /// - LockConflict = 17, - /// - /// SSH_FX_DIR_NOT_EMPTY - /// - DirNotEmpty = 18, - /// - /// SSH_FX_NOT_A_DIRECTORY - /// - NotDirectory = 19, - /// - /// SSH_FX_INVALID_FILENAME - /// - InvalidFilename = 20, - /// - /// SSH_FX_LINK_LOOP - /// - LinkLoop = 21, - /// - /// SSH_FX_CANNOT_DELETE - /// - CannotDelete = 22, - /// - /// SSH_FX_INVALID_PARAMETER - /// - InvalidParameter = 23, - /// - /// SSH_FX_FILE_IS_A_DIRECTORY - /// - FileIsADirectory = 24, - /// - /// SSH_FX_BYTE_RANGE_LOCK_CONFLICT - /// - ByteRangeLockConflict = 25, - /// - /// SSH_FX_BYTE_RANGE_LOCK_REFUSED - /// - ByteRangeLockRefused = 26, - /// - /// SSH_FX_DELETE_PENDING - /// - DeletePending = 27, - /// - /// SSH_FX_FILE_CORRUPT - /// - FileCorrupt = 28, - /// - /// SSH_FX_OWNER_INVALID - /// - OwnerInvalid = 29, - /// - /// SSH_FX_GROUP_INVALID - /// - GroupInvalid = 30, - /// - /// SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK - /// - NoMatchingByteRangeLock = 31, - } -} diff --git a/Renci.SshNet/SftpClient.NET.cs b/Renci.SshNet/SftpClient.NET.cs deleted file mode 100644 index 392229e..0000000 --- a/Renci.SshNet/SftpClient.NET.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.IO; -using Renci.SshNet.Sftp; -using System.Globalization; - -namespace Renci.SshNet -{ - /// - /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. - /// - public partial class SftpClient : BaseClient - { - #region SynchronizeDirectories - - /// - /// Synchronizes the directories. - /// - /// The source path. - /// The destination path. - /// The search pattern. - /// List of uploaded files. - public IEnumerable SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern) - { - return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, null); - } - - /// - /// Begins the synchronize directories. - /// - /// The source path. - /// The destination path. - /// The search pattern. - /// The async callback. - /// The state. - /// - /// An that represents the asynchronous directory synchronization. - /// - /// is null. - /// is null or contains only whitespace. - public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state) - { - if (sourcePath == null) - throw new ArgumentNullException("sourcePath"); - if (destinationPath.IsNullOrWhiteSpace()) - throw new ArgumentException("destDir"); - - var asyncResult = new SftpSynchronizeDirectoriesAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - var result = this.InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asyncResult); - - asyncResult.SetAsCompleted(result, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends the synchronize directories. - /// - /// The async result. - /// List of uploaded files. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - public IEnumerable EndSynchronizeDirectories(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpSynchronizeDirectoriesAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - return ar.EndInvoke(); - } - - private IEnumerable InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult asynchResult) - { - if (destinationPath.IsNullOrWhiteSpace()) - throw new ArgumentException("destinationPath"); - - if (!Directory.Exists(sourcePath)) - throw new FileNotFoundException(string.Format("Source directory not found: {0}", sourcePath)); - - IList uploadedFiles = new List(); - - DirectoryInfo sourceDirectory = new DirectoryInfo(sourcePath); - -#if SILVERLIGHT - var sourceFiles = sourceDirectory.EnumerateFiles(searchPattern); -#else - var sourceFiles = sourceDirectory.GetFiles(searchPattern); -#endif - - if (sourceFiles == null || !sourceFiles.Any()) - return uploadedFiles; - - #region Existing Files at The Destination - - var destFiles = InternalListDirectory(destinationPath, null); - Dictionary destDict = new Dictionary(); - foreach (var destFile in destFiles) - { - if (destFile.IsDirectory) - continue; - destDict.Add(destFile.Name, destFile); - } - - #endregion - - #region Upload the difference - - const Flags uploadFlag = Flags.Write | Flags.Truncate | Flags.CreateNewOrOpen; - foreach (var localFile in sourceFiles) - { - bool isDifferent = !destDict.ContainsKey(localFile.Name); - - if (!isDifferent) - { - SftpFile temp = destDict[localFile.Name]; - // TODO: Use md5 to detect a difference - //ltang: File exists at the destination => Using filesize to detect the difference - isDifferent = localFile.Length != temp.Length; - } - - if (isDifferent) - { - var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name); - try - { - using (var file = File.OpenRead(localFile.FullName)) - { - this.InternalUploadFile(file, remoteFileName, uploadFlag, null, null); - } - - uploadedFiles.Add(localFile); - - if (asynchResult != null) - { - asynchResult.Update(uploadedFiles.Count); - } - } - catch (Exception ex) - { - throw new Exception(string.Format("Failed to upload {0} to {1}", localFile.FullName, remoteFileName), ex); - } - } - } - - #endregion - - return uploadedFiles; - } - - #endregion - } -} diff --git a/Renci.SshNet/SftpClient.NET40.cs b/Renci.SshNet/SftpClient.NET40.cs deleted file mode 100644 index f373c70..0000000 --- a/Renci.SshNet/SftpClient.NET40.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// - /// - public partial class SftpClient - { - /// - /// - /// - /// - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} \ No newline at end of file diff --git a/Renci.SshNet/SftpClient.cs b/Renci.SshNet/SftpClient.cs deleted file mode 100644 index ccb4889..0000000 --- a/Renci.SshNet/SftpClient.cs +++ /dev/null @@ -1,1808 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.IO; -using Renci.SshNet.Sftp; -using System.Text; -using Renci.SshNet.Common; -using System.Globalization; -using System.Threading; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - - -[assembly: InternalsVisibleTo("WinSshFS, PublicKey=00240000048000009400000006020000002400005253413100040000010001005337866700b92e3a2a5d5be0292cdb0f2f6daa283526126b30169255b3c522f51593d15b5db31da4ddbe3e6ef5d9b80a05ddf4d1b1bca1c67ca62bf0b0c4d1b2ea3d4242027a2052b3c3cb17b98077a5c9f08143617ec3a1143c97c48bf27a378a9ec250220fb899f25c084599f477e36f699ec74aa452a3fd9e90007648a397")] - -namespace Renci.SshNet -{ - /// - /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. - /// - public partial class SftpClient : BaseClient - { - /// - /// Holds the instance that used to communicate to the - /// SFTP server. - /// - private SftpSession _sftpSession; - - /// - /// test - /// - /// SftpSession - internal SftpSession getSftpSession() - { - return this._sftpSession; - } - - /// - /// Holds the operation timeout. - /// - private TimeSpan _operationTimeout; - - /// - /// Holds the size of the buffer. - /// - private uint _bufferSize; - - /// - /// Gets or sets the operation timeout. - /// - /// - /// The timeout to wait until an operation completes. The default value is negative - /// one (-1) milliseconds, which indicates an infinite time-out period. - /// - /// The method was called after the client was disposed. - public TimeSpan OperationTimeout - { - get - { - CheckDisposed(); - return _operationTimeout; - } - set - { - CheckDisposed(); - _operationTimeout = value; - } - } - - /// - /// Gets or sets the maximum size of the buffer in bytes. - /// - /// - /// The size of the buffer. The default buffer size is 65536 bytes (64 KB). - /// - /// - /// - /// For write operations, this limits the size of the payload for - /// individual SSH_FXP_WRITE messages. The actual size is always - /// capped at the maximum packet size supported by the peer - /// (minus the size of protocol fields). - /// - /// - /// For read operations, this controls the size of the payload which - /// is requested from the peer in each SSH_FXP_READ message. The peer - /// will send the requested number of bytes in one or more SSH_FXP_DATA - /// messages. To optimize the size of the SSH packets sent by the peer, - /// the actual requested size will take into account the size of the - /// SSH_FXP_DATA protocol fields. - /// - /// - /// The size of the each indivual SSH_FXP_DATA message is limited to the - /// local maximum packet size of the channel, which is set to 64 KB - /// for SSH.NET. However, the peer can limit this even further. - /// - /// - /// The method was called after the client was disposed. - public uint BufferSize - { - get - { - CheckDisposed(); - return _bufferSize; - } - set - { - CheckDisposed(); - _bufferSize = value; - } - } - - /// - /// Gets remote working directory. - /// - /// Client is not connected. - /// The method was called after the client was disposed. - public string WorkingDirectory - { - get - { - CheckDisposed(); - if (_sftpSession == null) - throw new SshConnectionException("Client not connected."); - return _sftpSession.WorkingDirectory; - } - } - - /// - /// Gets sftp protocol version. - /// - /// Client is not connected. - /// The method was called after the client was disposed. - public int ProtocolVersion - { - get - { - CheckDisposed(); - if (_sftpSession == null) - throw new SshConnectionException("Client not connected."); - return (int) _sftpSession.ProtocolVersion; - } - } - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// is null. - public SftpClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid. -or- is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SftpClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid. -or- is null contains whitespace characters. - public SftpClient(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid. -or- is nunullll or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication private key file(s) . - /// is null. - /// is invalid. -or- is null or contains whitespace characters. - public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// Specified whether this instance owns the connection info. - /// is null. - /// - /// If is true, then the - /// connection info will be disposed when this instance is disposed. - /// - private SftpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo) - : base(connectionInfo, ownsConnectionInfo) - { - this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); - this.BufferSize = 1024 * 64; - } - - #endregion - - /// - /// Changes remote directory to path. - /// - /// New directory path. - /// is null. - /// Client is not connected. - /// Permission to change directory denied by remote host. -or- A SSH command was denied by the server. - /// The path in was not found on the remote host. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void ChangeDirectory(string path) - { - CheckDisposed(); - - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - this._sftpSession.ChangeDirectory(path); - } - - /// - /// Changes permissions of file(s) to specified mode. - /// - /// File(s) path, may match multiple files. - /// The mode. - /// is null. - /// Client is not connected. - /// Permission to change permission on the path(s) was denied by the remote host. -or- A SSH command was denied by the server. - /// The path in was not found on the remote host. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void ChangePermissions(string path, short mode) - { - var file = this.Get(path); - file.SetPermissions(mode); - } - - /// - /// Creates remote directory specified by path. - /// - /// Directory path to create. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to create the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void CreateDirectory(string path) - { - CheckDisposed(); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException(path); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestMkDir(fullPath); - } - - /// - /// Deletes remote directory specified by path. - /// - /// Directory to be deleted path. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to delete the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void DeleteDirectory(string path) - { - CheckDisposed(); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestRmDir(fullPath); - } - - /// - /// Deletes remote file specified by path. - /// - /// File to be deleted path. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to delete the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void DeleteFile(string path) - { - CheckDisposed(); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestRemove(fullPath); - } - - /// - /// Renames remote file from old path to new path. - /// - /// Path to the old file location. - /// Path to the new file location. - /// is null. -or- or is null. - /// Client is not connected. - /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void RenameFile(string oldPath, string newPath) - { - this.RenameFile(oldPath, newPath, false); - } - - /// - /// Renames remote file from old path to new path. - /// - /// Path to the old file location. - /// Path to the new file location. - /// if set to true then perform a posix rename. - /// is null. -or- or is null. - /// Client is not connected. - /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void RenameFile(string oldPath, string newPath, bool isPosix) - { - CheckDisposed(); - - if (oldPath == null) - throw new ArgumentNullException("oldPath"); - - if (newPath == null) - throw new ArgumentNullException("newPath"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var oldFullPath = this._sftpSession.GetCanonicalPath(oldPath); - - var newFullPath = this._sftpSession.GetCanonicalPath(newPath); - - if (isPosix) - { - this._sftpSession.RequestPosixRename(oldFullPath, newFullPath); - } - else - { - this._sftpSession.RequestRename(oldFullPath, newFullPath); - } - } - - /// - /// Creates a symbolic link from old path to new path. - /// - /// The old path. - /// The new path. - /// is null. -or- is null or contains whitespace characters. - /// Client is not connected. - /// Permission to create the symbolic link was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public void SymbolicLink(string path, string linkPath) - { - CheckDisposed(); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (linkPath.IsNullOrWhiteSpace()) - throw new ArgumentException("linkPath"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var linkFullPath = this._sftpSession.GetCanonicalPath(linkPath); - - this._sftpSession.RequestSymLink(fullPath, linkFullPath); - } - - /// - /// Creates a symbolic link from old path to new path. - /// - /// The old path. - /// Client is not connected. - /// is null. - /// There was a problem retrieving the symlink. - /// Client is not connected. - /// is null. - /// The method was called after the client was disposed. - public SftpFile GetSymbolicLinkTarget(string path) - { - CheckDisposed(); - - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var symTargetPaths = this._sftpSession.RequestReadLink(path, true); - - if (symTargetPaths == null) - { - throw new SftpPathNotFoundException("No valid symlink target"); - } - - SftpFileAttributes symTargetAttributes = GetAttributes(symTargetPaths[0].Key); - - return new SftpFile(this._sftpSession, symTargetPaths[0].Key, symTargetAttributes); - } - - /// - /// Retrieves list of files in remote directory. - /// - /// The path. - /// The list callback. - /// - /// List of directory entries - /// - /// is null. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public IEnumerable ListDirectory(string path, Action listCallback = null) - { - CheckDisposed(); - - return InternalListDirectory(path, listCallback); - } - - /// - /// Begins an asynchronous operation of retrieving list of files in remote directory. - /// - /// The path. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The list callback. - /// - /// An that references the asynchronous operation. - /// - /// The method was called after the client was disposed. - public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state, Action listCallback = null) - { - CheckDisposed(); - - var asyncResult = new SftpListDirectoryAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - var result = this.InternalListDirectory(path, count => - { - asyncResult.Update(count); - - if (listCallback != null) - { - listCallback(count); - } - }); - - asyncResult.SetAsCompleted(result, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends an asynchronous operation of retrieving list of files in remote directory. - /// - /// The pending asynchronous SFTP request. - /// - /// List of files - /// - /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. - public IEnumerable EndListDirectory(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpListDirectoryAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - return ar.EndInvoke(); - } - - /// - /// Gets reference to remote file or directory. - /// - /// The path. - /// Reference to file object. - /// Client is not connected. - /// is null. - /// The method was called after the client was disposed. - public SftpFile Get(string path) - { - CheckDisposed(); - - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var attributes = this._sftpSession.RequestLStat(fullPath); - - return new SftpFile(this._sftpSession, fullPath, attributes); - } - - /// - /// Checks whether file pr directory exists; - /// - /// The path. - /// true if directory or file exists; otherwise false. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - public bool Exists(string path) - { - CheckDisposed(); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetFullRemotePath(path); - - if (this._sftpSession.RequestRealPath(fullPath, true) == null) - { - return false; - } - return true; - } - - /// - /// Downloads remote file specified by the path into the stream. - /// - /// File to download. - /// Stream to write the file into. - /// The download callback. - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public void DownloadFile(string path, Stream output, Action downloadCallback = null) - { - CheckDisposed(); - - this.InternalDownloadFile(path, output, null, downloadCallback); - } - - /// - /// Begins an asynchronous file downloading into the stream. - /// - /// The path. - /// The output. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginDownloadFile(string path, Stream output) - { - return this.BeginDownloadFile(path, output, null, null, null); - } - - /// - /// Begins an asynchronous file downloading into the stream. - /// - /// The path. - /// The output. - /// The method to be called when the asynchronous write operation is completed. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback) - { - return this.BeginDownloadFile(path, output, asyncCallback, null, null); - } - - /// - /// Begins an asynchronous file downloading into the stream. - /// - /// The path. - /// The output. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The download callback. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state, Action downloadCallback = null) - { - CheckDisposed(); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (output == null) - throw new ArgumentNullException("output"); - - var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - this.InternalDownloadFile(path, output, asyncResult, offset => - { - asyncResult.Update(offset); - - if (downloadCallback != null) - { - downloadCallback(offset); - } - }); - - asyncResult.SetAsCompleted(null, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends an asynchronous file downloading into the stream. - /// - /// The pending asynchronous SFTP request. - /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. - public void EndDownloadFile(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpDownloadAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - ar.EndInvoke(); - } - - /// - /// Uploads stream into remote file.. - /// - /// Data input stream. - /// Remote file path. - /// The upload callback. - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public void UploadFile(Stream input, string path, Action uploadCallback = null) - { - this.UploadFile(input, path, true, uploadCallback); - } - - /// - /// Uploads stream into remote file.. - /// - /// Data input stream. - /// Remote file path. - /// if set to true then existing file will be overwritten. - /// The upload callback. - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public void UploadFile(Stream input, string path, bool canOverride, Action uploadCallback = null) - { - CheckDisposed(); - - var flags = Flags.Write | Flags.Truncate; - - if (canOverride) - flags |= Flags.CreateNewOrOpen; - else - flags |= Flags.CreateNew; - - this.InternalUploadFile(input, path, flags, null, uploadCallback); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path) - { - return this.BeginUploadFile(input, path, true, null, null, null); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// The method to be called when the asynchronous write operation is completed. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback) - { - return this.BeginUploadFile(input, path, true, asyncCallback, null, null); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The upload callback. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state, Action uploadCallback = null) - { - return this.BeginUploadFile(input, path, true, asyncCallback, state, uploadCallback); - } - - /// - /// Begins an asynchronous uploading the steam into remote file. - /// - /// Data input stream. - /// Remote file path. - /// if set to true then existing file will be overwritten. - /// The method to be called when the asynchronous write operation is completed. - /// A user-provided object that distinguishes this particular asynchronous write request from other requests. - /// The upload callback. - /// - /// An that references the asynchronous operation. - /// - /// is null. - /// is null or contains whitespace characters. - /// Client is not connected. - /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. - /// A SSH error where is the message from the remote host. - /// The method was called after the client was disposed. - /// - /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. - /// - public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback asyncCallback, object state, Action uploadCallback = null) - { - CheckDisposed(); - - if (input == null) - throw new ArgumentNullException("input"); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - var flags = Flags.Write | Flags.Truncate; - - if (canOverride) - flags |= Flags.CreateNewOrOpen; - else - flags |= Flags.CreateNew; - - var asyncResult = new SftpUploadAsyncResult(asyncCallback, state); - - this.ExecuteThread(() => - { - try - { - this.InternalUploadFile(input, path, flags, asyncResult, offset => - { - asyncResult.Update(offset); - - if (uploadCallback != null) - { - uploadCallback(offset); - } - - }); - - asyncResult.SetAsCompleted(null, false); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, false); - } - }); - - return asyncResult; - } - - /// - /// Ends an asynchronous uploading the steam into remote file. - /// - /// The pending asynchronous SFTP request. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. - public void EndUploadFile(IAsyncResult asyncResult) - { - var ar = asyncResult as SftpUploadAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - ar.EndInvoke(); - } - - /// - /// Gets status using statvfs@openssh.com request. - /// - /// The path. - /// Reference to object that contains file status information. - /// Client is not connected. - /// is null. - /// The method was called after the client was disposed. - public SftpFileSytemInformation GetStatus(string path) - { - CheckDisposed(); - - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - return this._sftpSession.RequestStatVfs(fullPath); - } - - #region File Methods - - /// - /// Appends lines to a file, and then closes the file. - /// - /// The file to append the lines to. The file is created if it does not already exist. - /// The lines to append to the file. - /// isnull -or- is null. - /// The method was called after the client was disposed. - public void AppendAllLines(string path, IEnumerable contents) - { - CheckDisposed(); - - if (contents == null) - throw new ArgumentNullException("contents"); - - using (var stream = this.AppendText(path)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Appends lines to a file by using a specified encoding, and then closes the file. - /// - /// The file to append the lines to. The file is created if it does not already exist. - /// The lines to append to the file. - /// The character encoding to use. - /// is null. -or- is null. -or- is null. - /// The method was called after the client was disposed. - public void AppendAllLines(string path, IEnumerable contents, Encoding encoding) - { - CheckDisposed(); - - if (contents == null) - throw new ArgumentNullException("contents"); - - using (var stream = this.AppendText(path, encoding)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Opens a file, appends the specified string to the file, and then closes the file. - /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. - /// - /// The file to append the specified string to. - /// The string to append to the file. - /// is null. -or- is null. - /// The method was called after the client was disposed. - public void AppendAllText(string path, string contents) - { - using (var stream = this.AppendText(path)) - { - stream.Write(contents); - } - } - - /// - /// Opens a file, appends the specified string to the file, and then closes the file. - /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. - /// - /// The file to append the specified string to. - /// The string to append to the file. - /// The character encoding to use. - /// is null. -or- is null. -or- is null. - /// The method was called after the client was disposed. - public void AppendAllText(string path, string contents, Encoding encoding) - { - using (var stream = this.AppendText(path, encoding)) - { - stream.Write(contents); - } - } - - /// - /// Creates a that appends UTF-8 encoded text to an existing file. - /// - /// The path to the file to append to. - /// A StreamWriter that appends UTF-8 encoded text to an existing file. - /// is null. - /// The method was called after the client was disposed. - public StreamWriter AppendText(string path) - { - return this.AppendText(path, Encoding.UTF8); - } - - /// - /// Creates a that appends UTF-8 encoded text to an existing file. - /// - /// The path to the file to append to. - /// The character encoding to use. - /// - /// A StreamWriter that appends UTF-8 encoded text to an existing file. - /// - /// is null. -or- is null. - /// The method was called after the client was disposed. - public StreamWriter AppendText(string path, Encoding encoding) - { - CheckDisposed(); - - if (encoding == null) - throw new ArgumentNullException("encoding"); - - return new StreamWriter(new SftpFileStream(this._sftpSession, path, FileMode.Append, FileAccess.Write), encoding); - } - - /// - /// Creates or overwrites a file in the specified path. - /// - /// The path and name of the file to create. - /// A that provides read/write access to the file specified in path - /// is null. - /// The method was called after the client was disposed. - public SftpFileStream Create(string path) - { - CheckDisposed(); - - return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite); - } - - /// - /// Creates or overwrites the specified file. - /// - /// The path and name of the file to create. - /// The number of bytes buffered for reads and writes to the file. - /// A that provides read/write access to the file specified in path - /// is null. - /// The method was called after the client was disposed. - public SftpFileStream Create(string path, int bufferSize) - { - CheckDisposed(); - - return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite, bufferSize); - } - - /// - /// Creates or opens a file for writing UTF-8 encoded text. - /// - /// The file to be opened for writing. - /// A that writes to the specified file using UTF-8 encoding. - /// is null. - /// The method was called after the client was disposed. - public StreamWriter CreateText(string path) - { - return CreateText(path, Encoding.UTF8); - } - - /// - /// Creates or opens a file for writing UTF-8 encoded text. - /// - /// The file to be opened for writing. - /// The character encoding to use. - /// A that writes to the specified file using UTF-8 encoding. - /// is null. - /// The method was called after the client was disposed. - public StreamWriter CreateText(string path, Encoding encoding) - { - CheckDisposed(); - - return new StreamWriter(this.OpenWrite(path), encoding); - } - - /// - /// Deletes the specified file or directory. An exception is not thrown if the specified file does not exist. - /// - /// The name of the file or directory to be deleted. Wildcard characters are not supported. - /// is null. - /// Client is not connected. - /// The method was called after the client was disposed. - public void Delete(string path) - { - var file = this.Get(path); - file.Delete(); - } - - /// - /// Returns the date and time the specified file or directory was last accessed. - /// - /// The file or directory for which to obtain access date and time information. - /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in local time. - /// is null. - /// Client is not connected. - /// The method was called after the client was disposed. - public DateTime GetLastAccessTime(string path) - { - var file = this.Get(path); - return file.LastAccessTime; - } - - /// - /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last accessed. - /// - /// The file or directory for which to obtain access date and time information. - /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in UTC time. - /// is null. - /// Client is not connected. - /// The method was called after the client was disposed. - public DateTime GetLastAccessTimeUtc(string path) - { - var lastAccessTime = GetLastAccessTime(path); - return lastAccessTime.ToUniversalTime(); - } - - /// - /// Returns the date and time the specified file or directory was last written to. - /// - /// The file or directory for which to obtain write date and time information. - /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in local time. - /// is null. - /// Client is not connected. - /// The method was called after the client was disposed. - public DateTime GetLastWriteTime(string path) - { - var file = this.Get(path); - return file.LastWriteTime; - } - - /// - /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to. - /// - /// The file or directory for which to obtain write date and time information. - /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in UTC time. - /// is null. - /// Client is not connected. - /// The method was called after the client was disposed. - public DateTime GetLastWriteTimeUtc(string path) - { - var lastWriteTime = GetLastWriteTime(path); - return lastWriteTime.ToUniversalTime(); - } - - /// - /// Opens a on the specified path with read/write access. - /// - /// The file to open. - /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. - /// An unshared that provides access to the specified file, with the specified mode and access. - /// is null. - /// The method was called after the client was disposed. - public SftpFileStream Open(string path, FileMode mode) - { - return Open(path, mode, FileAccess.ReadWrite); - } - - /// - /// Opens a on the specified path, with the specified mode and access. - /// - /// The file to open. - /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. - /// A value that specifies the operations that can be performed on the file. - /// An unshared that provides access to the specified file, with the specified mode and access. - /// is null. - /// The method was called after the client was disposed. - public SftpFileStream Open(string path, FileMode mode, FileAccess access) - { - CheckDisposed(); - - return new SftpFileStream(this._sftpSession, path, mode, access, (int) _bufferSize); - } - - /// - /// Opens an existing file for reading. - /// - /// The file to be opened for reading. - /// A read-only System.IO.FileStream on the specified path. - /// is null. - /// The method was called after the client was disposed. - public SftpFileStream OpenRead(string path) - { - return Open(path, FileMode.Open, FileAccess.Read); - } - - /// - /// Opens an existing UTF-8 encoded text file for reading. - /// - /// The file to be opened for reading. - /// A on the specified path. - /// is null. - /// The method was called after the client was disposed. - public StreamReader OpenText(string path) - { - return new StreamReader(this.OpenRead(path), Encoding.UTF8); - } - - /// - /// Opens an existing file for writing. - /// - /// The file to be opened for writing. - /// An unshared object on the specified path with access. - /// is null. - /// The method was called after the client was disposed. - public SftpFileStream OpenWrite(string path) - { - CheckDisposed(); - - return new SftpFileStream(this._sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write, - (int) _bufferSize); - } - - /// - /// Opens a binary file, reads the contents of the file into a byte array, and then closes the file. - /// - /// The file to open for reading. - /// A byte array containing the contents of the file. - /// is null. - /// The method was called after the client was disposed. - public byte[] ReadAllBytes(string path) - { - using (var stream = this.OpenRead(path)) - { - var buffer = new byte[stream.Length]; - stream.Read(buffer, 0, buffer.Length); - return buffer; - } - } - - /// - /// Opens a text file, reads all lines of the file, and then closes the file. - /// - /// The file to open for reading. - /// A string array containing all lines of the file. - /// is null. - /// The method was called after the client was disposed. - public string[] ReadAllLines(string path) - { - return this.ReadAllLines(path, Encoding.UTF8); - } - - /// - /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. - /// - /// The file to open for reading. - /// The encoding applied to the contents of the file. - /// A string array containing all lines of the file. - /// is null. - /// The method was called after the client was disposed. - public string[] ReadAllLines(string path, Encoding encoding) - { - var lines = new List(); - using (var stream = new StreamReader(this.OpenRead(path), encoding)) - { - while (!stream.EndOfStream) - { - lines.Add(stream.ReadLine()); - } - } - return lines.ToArray(); - } - - /// - /// Opens a text file, reads all lines of the file, and then closes the file. - /// - /// The file to open for reading. - /// A string containing all lines of the file. - /// is null. - /// The method was called after the client was disposed. - public string ReadAllText(string path) - { - return this.ReadAllText(path, Encoding.UTF8); - } - - /// - /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. - /// - /// The file to open for reading. - /// The encoding applied to the contents of the file. - /// A string containing all lines of the file. - /// is null. - /// The method was called after the client was disposed. - public string ReadAllText(string path, Encoding encoding) - { - using (var stream = new StreamReader(this.OpenRead(path), encoding)) - { - return stream.ReadToEnd(); - } - } - - /// - /// Reads the lines of a file. - /// - /// The file to read. - /// The lines of the file. - /// is null. - /// The method was called after the client was disposed. - public IEnumerable ReadLines(string path) - { - return this.ReadAllLines(path); - } - - /// - /// Read the lines of a file that has a specified encoding. - /// - /// The file to read. - /// The encoding that is applied to the contents of the file. - /// The lines of the file. - /// is null. - /// The method was called after the client was disposed. - public IEnumerable ReadLines(string path, Encoding encoding) - { - return this.ReadAllLines(path, encoding); - } - - /// - /// Sets the date and time the specified file was last accessed. - /// - /// The file for which to set the access date and time information. - /// A containing the value to set for the last access date and time of path. This value is expressed in local time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastAccessTime(string path, DateTime lastAccessTime) - { - throw new NotImplementedException(); - } - - /// - /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last accessed. - /// - /// The file for which to set the access date and time information. - /// A containing the value to set for the last access date and time of path. This value is expressed in UTC time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) - { - throw new NotImplementedException(); - } - - /// - /// Sets the date and time that the specified file was last written to. - /// - /// The file for which to set the date and time information. - /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in local time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastWriteTime(string path, DateTime lastWriteTime) - { - throw new NotImplementedException(); - } - - /// - /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last written to. - /// - /// The file for which to set the date and time information. - /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in UTC time. - [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] - public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) - { - throw new NotImplementedException(); - } - - /// - /// Creates a new file, writes the specified byte array to the file, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The bytes to write to the file. - /// is null. - /// The method was called after the client was disposed. - public void WriteAllBytes(string path, byte[] bytes) - { - using (var stream = this.OpenWrite(path)) - { - stream.Write(bytes, 0, bytes.Length); - } - } - - /// - /// Creates a new file, writes a collection of strings to the file, and then closes the file. - /// - /// The file to write to. - /// The lines to write to the file. - /// is null. - /// The method was called after the client was disposed. - public void WriteAllLines(string path, IEnumerable contents) - { - this.WriteAllLines(path, contents, Encoding.UTF8); - } - - /// - /// Creates a new file, write the specified string array to the file, and then closes the file. - /// - /// The file to write to. - /// The string array to write to the file. - /// is null. - /// The method was called after the client was disposed. - public void WriteAllLines(string path, string[] contents) - { - this.WriteAllLines(path, contents, Encoding.UTF8); - } - - /// - /// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file. - /// - /// The file to write to. - /// The lines to write to the file. - /// The character encoding to use. - /// is null. - /// The method was called after the client was disposed. - public void WriteAllLines(string path, IEnumerable contents, Encoding encoding) - { - using (var stream = this.CreateText(path, encoding)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file. - /// - /// The file to write to. - /// The string array to write to the file. - /// An object that represents the character encoding applied to the string array. - /// is null. - /// The method was called after the client was disposed. - public void WriteAllLines(string path, string[] contents, Encoding encoding) - { - using (var stream = this.CreateText(path, encoding)) - { - foreach (var line in contents) - { - stream.WriteLine(line); - } - } - } - - /// - /// Creates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The string to write to the file. - /// is null. - /// The method was called after the client was disposed. - public void WriteAllText(string path, string contents) - { - using (var stream = this.CreateText(path)) - { - stream.Write(contents); - } - } - - /// - /// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten. - /// - /// The file to write to. - /// The string to write to the file. - /// The encoding to apply to the string. - /// is null. - /// The method was called after the client was disposed. - public void WriteAllText(string path, string contents, Encoding encoding) - { - using (var stream = this.CreateText(path, encoding)) - { - stream.Write(contents); - } - } - - /// - /// Gets the of the file on the path. - /// - /// The path to the file. - /// The of the file on the path. - /// is null. - /// The method was called after the client was disposed. - public SftpFileAttributes GetAttributes(string path) - { - CheckDisposed(); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - return this._sftpSession.RequestLStat(fullPath); - } - - /// - /// Sets the specified of the file on the specified path. - /// - /// The path to the file. - /// The desired . - /// is null. - /// The method was called after the client was disposed. - public void SetAttributes(string path, SftpFileAttributes fileAttributes) - { - CheckDisposed(); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - this._sftpSession.RequestSetStat(fullPath, fileAttributes); - } - - // Please don't forget this when you implement these methods: is null. - //public FileSecurity GetAccessControl(string path); - //public FileSecurity GetAccessControl(string path, AccessControlSections includeSections); - //public DateTime GetCreationTime(string path); - //public DateTime GetCreationTimeUtc(string path); - //public void SetAccessControl(string path, FileSecurity fileSecurity); - //public void SetCreationTime(string path, DateTime creationTime); - //public void SetCreationTimeUtc(string path, DateTime creationTimeUtc); - - #endregion - - /// - /// Internals the list directory. - /// - /// The path. - /// The list callback. - /// - /// path - /// is null. - /// Client not connected. - private IEnumerable InternalListDirectory(string path, Action listCallback) - { - if (path == null) - throw new ArgumentNullException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var handle = this._sftpSession.RequestOpenDir(fullPath); - - var basePath = fullPath; - - if (!basePath.EndsWith("/")) - basePath = string.Format("{0}/", fullPath); - - var result = new List(); - - var files = this._sftpSession.RequestReadDir(handle); - - while (files != null) - { - result.AddRange(from f in files - select new SftpFile(this._sftpSession, string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), f.Value)); - - // Call callback to report number of files read - if (listCallback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => listCallback(result.Count)); - } - - files = this._sftpSession.RequestReadDir(handle); - } - - this._sftpSession.RequestClose(handle); - - return result; - } - - /// - /// Internals the download file. - /// - /// The path. - /// The output. - /// An that references the asynchronous request. - /// The download callback. - /// is null. - /// is null or contains whitespace. - /// Client not connected. - private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult asyncResult, Action downloadCallback) - { - if (output == null) - throw new ArgumentNullException("output"); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var handle = this._sftpSession.RequestOpen(fullPath, Flags.Read); - - ulong offset = 0; - - var optimalReadLength = _sftpSession.CalculateOptimalReadLength(_bufferSize); - - var data = this._sftpSession.RequestRead(handle, offset, optimalReadLength); - - // Read data while available - while (data.Length > 0) - { - // Cancel download - if (asyncResult != null && asyncResult.IsDownloadCanceled) - break; - - output.Write(data, 0, data.Length); - - output.Flush(); - - offset += (ulong)data.Length; - - // Call callback to report number of bytes read - if (downloadCallback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => { downloadCallback(offset); }); - } - - data = this._sftpSession.RequestRead(handle, offset, optimalReadLength); - } - - this._sftpSession.RequestClose(handle); - } - - /// - /// Internals the upload file. - /// - /// The input. - /// The path. - /// The flags. - /// An that references the asynchronous request. - /// The upload callback. - /// is null. - /// is null or contains whitespace. - /// Client not connected. - private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult asyncResult, Action uploadCallback) - { - if (input == null) - throw new ArgumentNullException("input"); - - if (path.IsNullOrWhiteSpace()) - throw new ArgumentException("path"); - - if (this._sftpSession == null) - throw new SshConnectionException("Client not connected."); - - var fullPath = this._sftpSession.GetCanonicalPath(path); - - var handle = this._sftpSession.RequestOpen(fullPath, flags); - - ulong offset = 0; - - // create buffer of optimal length - var buffer = new byte[_sftpSession.CalculateOptimalWriteLength(_bufferSize, handle)]; - - var bytesRead = input.Read(buffer, 0, buffer.Length); - var expectedResponses = 0; - var responseReceivedWaitHandle = new AutoResetEvent(false); - - do - { - // Cancel upload - if (asyncResult != null && asyncResult.IsUploadCanceled) - break; - - if (bytesRead > 0) - { - if (bytesRead < buffer.Length) - { - // Replace buffer for last chunk of data - var data = new byte[bytesRead]; - Buffer.BlockCopy(buffer, 0, data, 0, bytesRead); - buffer = data; - } - - var writtenBytes = offset + (ulong)buffer.Length; - this._sftpSession.RequestWrite(handle, offset, buffer, null, s => - { - if (s.StatusCode == StatusCodes.Ok) - { - Interlocked.Decrement(ref expectedResponses); - responseReceivedWaitHandle.Set(); - - // Call callback to report number of bytes written - if (uploadCallback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => uploadCallback(writtenBytes)); - } - } - }); - Interlocked.Increment(ref expectedResponses); - - offset += (uint)bytesRead; - - bytesRead = input.Read(buffer, 0, buffer.Length); - } - else if (expectedResponses > 0) - { - // Wait for expectedResponses to change - this._sftpSession.WaitOnHandle(responseReceivedWaitHandle, this.OperationTimeout); - } - } while (expectedResponses > 0 || bytesRead > 0); - - this._sftpSession.RequestClose(handle); - } - - partial void ExecuteThread(Action action); - - /// - /// Called when client is connected to the server. - /// - protected override void OnConnected() - { - base.OnConnected(); - - this._sftpSession = new SftpSession(this.Session, this.OperationTimeout, this.ConnectionInfo.Encoding); - this._sftpSession.Connect(); - } - - /// - /// Called when client is disconnecting from the server. - /// - protected override void OnDisconnecting() - { - base.OnDisconnecting(); - - // disconnect and dispose the SFTP session - // the dispose is necessary since we create a new SFTP session - // on each connect - if (_sftpSession != null) - { - this._sftpSession.Disconnect(); - this._sftpSession.Dispose(); - this._sftpSession = null; - } - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - if (this._sftpSession != null) - { - this._sftpSession.Dispose(); - this._sftpSession = null; - } - - base.Dispose(disposing); - } - } -} diff --git a/Renci.SshNet/Shell.NET40.cs b/Renci.SshNet/Shell.NET40.cs deleted file mode 100644 index e3dfcf8..0000000 --- a/Renci.SshNet/Shell.NET40.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Represents instance of the SSH shell object - /// - public partial class Shell - { - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/Shell.cs b/Renci.SshNet/Shell.cs deleted file mode 100644 index 4ae1f2b..0000000 --- a/Renci.SshNet/Shell.cs +++ /dev/null @@ -1,346 +0,0 @@ -using System; -using System.Linq; -using System.IO; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using System.Collections.Generic; - -namespace Renci.SshNet -{ - /// - /// Represents instance of the SSH shell object - /// - public partial class Shell : IDisposable - { - private readonly Session _session; - - private ChannelSession _channel; - - private EventWaitHandle _channelClosedWaitHandle; - - private Stream _input; - - private readonly string _terminalName; - - private readonly uint _columns; - - private readonly uint _rows; - - private readonly uint _width; - - private readonly uint _height; - - private readonly IDictionary _terminalModes; - - private EventWaitHandle _dataReaderTaskCompleted; - - private readonly Stream _outputStream; - - private readonly Stream _extendedOutputStream; - - private readonly int _bufferSize; - - /// - /// Gets a value indicating whether this shell is started. - /// - /// - /// true if started is started; otherwise, false. - /// - public bool IsStarted { get; private set; } - - /// - /// Occurs when shell is starting. - /// - public event EventHandler Starting; - - /// - /// Occurs when shell is started. - /// - public event EventHandler Started; - - /// - /// Occurs when shell is stopping. - /// - public event EventHandler Stopping; - - /// - /// Occurs when shell is stopped. - /// - public event EventHandler Stopped; - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccurred; - - /// - /// Initializes a new instance of the class. - /// - /// The session. - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal modes. - /// Size of the buffer for output stream. - internal Shell(Session session, Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) - { - this._session = session; - this._input = input; - this._outputStream = output; - this._extendedOutputStream = extendedOutput; - this._terminalName = terminalName; - this._columns = columns; - this._rows = rows; - this._width = width; - this._height = height; - this._terminalModes = terminalModes; - this._bufferSize = bufferSize; - } - - /// - /// Starts this shell. - /// - /// Shell is started. - public void Start() - { - if (this.IsStarted) - { - throw new SshException("Shell is started."); - } - - if (this.Starting != null) - { - this.Starting(this, new EventArgs()); - } - - this._channel = this._session.CreateClientChannel(); - this._channel.DataReceived += Channel_DataReceived; - this._channel.ExtendedDataReceived += Channel_ExtendedDataReceived; - this._channel.Closed += Channel_Closed; - this._session.Disconnected += Session_Disconnected; - this._session.ErrorOccured += Session_ErrorOccured; - - this._channel.Open(); - this._channel.SendPseudoTerminalRequest(this._terminalName, this._columns, this._rows, this._width, this._height, this._terminalModes); - this._channel.SendShellRequest(); - - this._channelClosedWaitHandle = new AutoResetEvent(false); - - // Start input stream listener - this._dataReaderTaskCompleted = new ManualResetEvent(false); - this.ExecuteThread(() => - { - try - { - var buffer = new byte[this._bufferSize]; - - while (this._channel.IsOpen) - { - var asyncResult = this._input.BeginRead(buffer, 0, buffer.Length, delegate(IAsyncResult result) - { - // If input stream is closed and disposed already dont finish reading the stream - if (this._input == null) - return; - - var read = this._input.EndRead(result); - if (read > 0) - { - this._channel.SendData(buffer.Take(read).ToArray()); - } - - }, null); - - EventWaitHandle.WaitAny(new WaitHandle[] { asyncResult.AsyncWaitHandle, this._channelClosedWaitHandle }); - - if (asyncResult.IsCompleted) - continue; - break; - } - } - catch (Exception exp) - { - this.RaiseError(new ExceptionEventArgs(exp)); - } - finally - { - this._dataReaderTaskCompleted.Set(); - } - }); - - this.IsStarted = true; - - if (this.Started != null) - { - this.Started(this, new EventArgs()); - } - } - - /// - /// Stops this shell. - /// - /// Shell is not started. - public void Stop() - { - if (!this.IsStarted) - { - throw new SshException("Shell is not started."); - } - - // If channel is open then close it to cause Channel_Closed method to be called - if (this._channel != null && this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - } - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - this.RaiseError(e); - } - - private void RaiseError(ExceptionEventArgs e) - { - var handler = this.ErrorOccurred; - if (handler != null) - { - handler(this, e); - } - } - - private void Session_Disconnected(object sender, EventArgs e) - { - this.Stop(); - } - - private void Channel_ExtendedDataReceived(object sender, ChannelDataEventArgs e) - { - if (this._extendedOutputStream != null) - { - this._extendedOutputStream.Write(e.Data, 0, e.Data.Length); - } - } - - private void Channel_DataReceived(object sender, ChannelDataEventArgs e) - { - if (this._outputStream != null) - { - this._outputStream.Write(e.Data, 0, e.Data.Length); - } - } - - private void Channel_Closed(object sender, ChannelEventArgs e) - { - if (this.Stopping != null) - { - // Handle event on different thread - this.ExecuteThread(() => this.Stopping(this, new EventArgs())); - } - - if (this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - - this._channelClosedWaitHandle.Set(); - - this._input.Dispose(); - this._input = null; - - this._dataReaderTaskCompleted.WaitOne(this._session.ConnectionInfo.Timeout); - this._dataReaderTaskCompleted.Dispose(); - this._dataReaderTaskCompleted = null; - - this._channel.DataReceived -= Channel_DataReceived; - this._channel.ExtendedDataReceived -= Channel_ExtendedDataReceived; - this._channel.Closed -= Channel_Closed; - this._session.Disconnected -= Session_Disconnected; - this._session.ErrorOccured -= Session_ErrorOccured; - - if (this.Stopped != null) - { - // Handle event on different thread - this.ExecuteThread(() => this.Stopped(this, new EventArgs())); - } - - this._channel = null; - } - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _disposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._disposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - if (this._channelClosedWaitHandle != null) - { - this._channelClosedWaitHandle.Dispose(); - this._channelClosedWaitHandle = null; - } - - if (this._channel != null) - { - this._channel.Dispose(); - this._channel = null; - } - - if (this._dataReaderTaskCompleted != null) - { - this._dataReaderTaskCompleted.Dispose(); - this._dataReaderTaskCompleted = null; - } - } - - // Note disposing has been done. - this._disposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~Shell() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - - } -} diff --git a/Renci.SshNet/ShellStream.NET40.cs b/Renci.SshNet/ShellStream.NET40.cs deleted file mode 100644 index 9ade4f0..0000000 --- a/Renci.SshNet/ShellStream.NET40.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Represents instance of the SSH shell object - /// - public partial class ShellStream - { - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/ShellStream.cs b/Renci.SshNet/ShellStream.cs deleted file mode 100644 index 0b723df..0000000 --- a/Renci.SshNet/ShellStream.cs +++ /dev/null @@ -1,758 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using System.Threading; -using System.Text.RegularExpressions; - -namespace Renci.SshNet -{ - /// - /// Contains operation for working with SSH Shell. - /// - public partial class ShellStream : Stream - { - private readonly Session _session; - - private const int _bufferSize = 1024; - - private ChannelSession _channel; - - private readonly Encoding _encoding; - - private readonly Queue _incoming; - - private readonly Queue _outgoing; - - private AutoResetEvent _dataReceived = new AutoResetEvent(false); - - /// - /// Occurs when data was received. - /// - public event EventHandler DataReceived; - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccurred; - - /// - /// Gets a value that indicates whether data is available on the to be read. - /// - /// - /// true if data is available to be read; otherwise, false. - /// - public bool DataAvailable - { - get - { - lock (this._incoming) - { - return this._incoming.Count > 0; - } - } - } - - internal ShellStream(Session session, string terminalName, uint columns, uint rows, uint width, uint height, int maxLines, IDictionary terminalModeValues) - { - this._encoding = session.ConnectionInfo.Encoding; - this._session = session; - this._incoming = new Queue(); - this._outgoing = new Queue(); - - this._channel = this._session.CreateClientChannel(); - this._channel.DataReceived += Channel_DataReceived; - this._channel.Closed += Channel_Closed; - this._session.Disconnected += Session_Disconnected; - this._session.ErrorOccured += Session_ErrorOccured; - - this._channel.Open(); - this._channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues); - this._channel.SendShellRequest(); - } - - #region Stream overide methods - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get { return true; } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get { return false; } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get { return true; } - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// An I/O error occurs. - public override void Flush() - { - if (this._channel == null) - { - throw new ObjectDisposedException("ShellStream"); - } - this._channel.SendData(this._outgoing.ToArray()); - this._outgoing.Clear(); - } - - /// - /// Gets the length in bytes of the stream. - /// - /// A long value representing the length of the stream in bytes. - /// - /// A class derived from Stream does not support seeking. - /// - /// Methods were called after the stream was closed. - public override long Length - { - get - { - lock (this._incoming) - { - return this._incoming.Count; - } - } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// The current position within the stream. - /// - /// An I/O error occurs. - /// - /// The stream does not support seeking. - /// - /// Methods were called after the stream was closed. - public override long Position - { - get { return 0; } - set { throw new NotSupportedException(); } - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// - /// The sum of and is larger than the buffer length. - /// - /// - /// is null. - /// - /// - /// or is negative. - /// - /// An I/O error occurs. - /// - /// The stream does not support reading. - /// - /// Methods were called after the stream was closed. - public override int Read(byte[] buffer, int offset, int count) - { - var i = 0; - - lock (this._incoming) - { - for (; i < count && this._incoming.Count > 0; i++) - { - buffer[offset + i] = this._incoming.Dequeue(); - } - } - - return i; - } - - /// - /// This method is not supported. - /// - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// - /// The new position within the current stream. - /// - /// An I/O error occurs. - /// - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// - /// Methods were called after the stream was closed. - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// This method is not supported. - /// - /// The desired length of the current stream in bytes. - /// An I/O error occurs. - /// - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// - /// Methods were called after the stream was closed. - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies bytes from to the current stream. - /// The zero-based byte offset in at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// The sum of and is greater than the buffer length. - /// - /// - /// is null. - /// - /// - /// or is negative. - /// - /// An I/O error occurs. - /// - /// The stream does not support writing. - /// - /// Methods were called after the stream was closed. - public override void Write(byte[] buffer, int offset, int count) - { - foreach (var b in buffer.Skip(offset).Take(count).ToArray()) - { - if (this._outgoing.Count < _bufferSize) - { - this._outgoing.Enqueue(b); - continue; - } - - this.Flush(); - } - } - - #endregion - - /// - /// Expects the specified expression and performs action when one is found. - /// - /// The expected expressions and actions to perform. - public void Expect(params ExpectAction[] expectActions) - { - this.Expect(TimeSpan.Zero, expectActions); - } - - /// - /// Expects the specified expression and performs action when one is found. - /// - /// Time to wait for input. - /// The expected expressions and actions to perform, if the specified time elapsed and expected condition have not met, that method will exit without executing any action. - public void Expect(TimeSpan timeout, params ExpectAction[] expectActions) - { - var expectedFound = false; - var text = string.Empty; - - do - { - lock (this._incoming) - { - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - if (text.Length > 0) - { - foreach (var expectAction in expectActions) - { - var match = expectAction.Expect.Match(text); - - if (match.Success) - { - var result = text.Substring(0, match.Index + match.Length); - - for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) - { - // Remove processed items from the queue - this._incoming.Dequeue(); - } - - expectAction.Action(result); - expectedFound = true; - } - } - } - } - - if (!expectedFound) - { - if (timeout.Ticks > 0) - { - if (!this._dataReceived.WaitOne(timeout)) - { - return; - } - } - else - { - this._dataReceived.WaitOne(); - } - } - } - while (!expectedFound); - } - - /// - /// Begins the expect. - /// - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(params ExpectAction[] expectActions) - { - return this.BeginExpect(TimeSpan.Zero, null, null, expectActions); - } - - /// - /// Begins the expect. - /// - /// The callback. - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(AsyncCallback callback, params ExpectAction[] expectActions) - { - return this.BeginExpect(TimeSpan.Zero, callback, null, expectActions); - } - - /// - /// Begins the expect. - /// - /// The callback. - /// The state. - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(AsyncCallback callback, object state, params ExpectAction[] expectActions) - { - return this.BeginExpect(TimeSpan.Zero, callback, state, expectActions); - } - - /// - /// Begins the expect. - /// - /// The timeout. - /// The callback. - /// The state. - /// The expect actions. - /// - /// An that references the asynchronous operation. - /// - public IAsyncResult BeginExpect(TimeSpan timeout, AsyncCallback callback, object state, params ExpectAction[] expectActions) - { - var text = string.Empty; - - // Create new AsyncResult object - var asyncResult = new ExpectAsyncResult(callback, state); - - // Execute callback on different thread - this.ExecuteThread(() => - { - string expectActionResult = null; - try - { - - do - { - lock (this._incoming) - { - - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - if (text.Length > 0) - { - foreach (var expectAction in expectActions) - { - var match = expectAction.Expect.Match(text); - - if (match.Success) - { - var result = text.Substring(0, match.Index + match.Length); - - for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) - { - // Remove processed items from the queue - this._incoming.Dequeue(); - } - - expectAction.Action(result); - - if (callback != null) - { - callback(asyncResult); - } - expectActionResult = result; - } - } - } - } - - if (expectActionResult != null) - break; - - if (timeout.Ticks > 0) - { - if (!this._dataReceived.WaitOne(timeout)) - { - if (callback != null) - { - callback(asyncResult); - } - break; - } - } - else - { - this._dataReceived.WaitOne(); - } - } while (true); - - asyncResult.SetAsCompleted(expectActionResult, true); - } - catch (Exception exp) - { - asyncResult.SetAsCompleted(exp, true); - } - }); - - return asyncResult; - } - - /// - /// Ends the execute. - /// - /// The async result. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - public string EndExpect(IAsyncResult asyncResult) - { - var ar = asyncResult as ExpectAsyncResult; - - if (ar == null || ar.EndInvokeCalled) - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - - // Wait for operation to complete, then return result or throw exception - return ar.EndInvoke(); - } - - /// - /// Expects the expression specified by text. - /// - /// The text to expect. - /// - /// Text available in the shell that ends with expected text. - /// - public string Expect(string text) - { - return this.Expect(new Regex(Regex.Escape(text)), TimeSpan.FromMilliseconds(-1)); - } - - /// - /// Expects the expression specified by text. - /// - /// The text to expect. - /// Time to wait for input. - /// - /// Text available in the shell that ends with expected text, if the specified time elapsed returns null. - /// - public string Expect(string text, TimeSpan timeout) - { - return this.Expect(new Regex(Regex.Escape(text)), timeout); - } - - /// - /// Expects the expression specified by regular expression. - /// - /// The regular expression to expect. - /// Text available in the shell that contains all the text that ends with expected expression. - public string Expect(Regex regex) - { - return this.Expect(regex, TimeSpan.Zero); - } - - /// - /// Expects the expression specified by regular expression. - /// - /// The regular expression to expect. - /// Time to wait for input. - /// - /// Text available in the shell that contains all the text that ends with expected expression, if the specified time elapsed returns null. - /// - public string Expect(Regex regex, TimeSpan timeout) - { - // TODO: Refactor this method, will deda lock - var text = string.Empty; - - while (true) - { - lock (this._incoming) - { - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - var match = regex.Match(text); - - if (match.Success) - { - // Remove processed items from the queue - for (int i = 0; i < match.Index + match.Length && this._incoming.Count > 0; i++) - { - this._incoming.Dequeue(); - } - break; - } - } - - if (timeout.Ticks > 0) - { - if (!this._dataReceived.WaitOne(timeout)) - { - return null; - } - } - else - { - this._dataReceived.WaitOne(); - } - - } - - return text; - } - - /// - /// Reads the line from the shell. If line is not available it will block the execution and will wait for new line. - /// - /// The line read from the shell. - public string ReadLine() - { - return this.ReadLine(TimeSpan.Zero); - } - - /// - /// Reads the line from the shell. If line is not available it will block the execution and will wait for new line. - /// - /// Time to wait for input. - /// - /// The line read from the shell, if the specified time elapsed returns null. - /// - public string ReadLine(TimeSpan timeout) - { - var text = string.Empty; - - while (true) - { - lock (this._incoming) - { - if (this._incoming.Count > 0) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - } - - var index = text.IndexOf("\r\n"); - - if (index >= 0) - { - text = text.Substring(0, index); - - // Remove processed items from the queue - for (int i = 0; i < index + 2 && this._incoming.Count > 0; i++) - { - this._incoming.Dequeue(); - } - break; - } - } - - if (timeout.Ticks > 0) - { - if (!this._dataReceived.WaitOne(timeout)) - { - return null; - } - } - else - { - this._dataReceived.WaitOne(); - } - - } - - return text; - } - - /// - /// Reads text available in the shell. - /// - /// The text available in the shell. - public string Read() - { - string text; - - lock (this._incoming) - { - text = this._encoding.GetString(this._incoming.ToArray(), 0, this._incoming.Count); - this._incoming.Clear(); - } - - return text; - } - - /// - /// Writes the specified text to the shell. - /// - /// The text to be written to the shell. - public void Write(string text) - { - if (this._channel == null) - { - throw new ObjectDisposedException("ShellStream"); - } - - var data = this._encoding.GetBytes(text); - this._channel.SendData(data); - } - - /// - /// Writes the line to the shell. - /// - /// The line to be written to the shell. - public void WriteLine(string line) - { - var commandText = string.Format("{0}{1}", line, "\r"); - this.Write(commandText); - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._channel != null) - { - this._channel.Dispose(); - this._channel = null; - } - - if (this._dataReceived != null) - { - this._dataReceived.Dispose(); - this._dataReceived = null; - } - - if (this._session != null) - { - this._session.Disconnected -= Session_Disconnected; - this._session.ErrorOccured -= Session_ErrorOccured; - } - } - - /// - /// Waits for the handle to be signaled or for an error to occurs. - /// - /// The wait handle. - protected void WaitOnHandle(WaitHandle waitHandle) - { - this._session.WaitOnHandle(waitHandle); - } - - partial void ExecuteThread(Action action); - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - this.OnRaiseError(e); - } - - private void Session_Disconnected(object sender, EventArgs e) - { - // If channel is open then close it to cause Channel_Closed method to be called - if (this._channel != null && this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - } - - private void Channel_Closed(object sender, ChannelEventArgs e) - { - // TODO: Do we need to call dispose here ?? - this.Dispose(); - } - - private void Channel_DataReceived(object sender, ChannelDataEventArgs e) - { - lock (this._incoming) - { - foreach (var b in e.Data) - this._incoming.Enqueue(b); - } - - if (_dataReceived != null) - _dataReceived.Set(); - - this.OnDataReceived(e.Data); - } - - private void OnRaiseError(ExceptionEventArgs e) - { - var handler = this.ErrorOccurred; - if (handler != null) - { - handler(this, e); - } - } - - private void OnDataReceived(byte[] data) - { - var handler = this.DataReceived; - if (handler != null) - { - handler(this, new ShellDataEventArgs(data)); - } - } - } -} diff --git a/Renci.SshNet/SshClient.cs b/Renci.SshNet/SshClient.cs deleted file mode 100644 index 23c8039..0000000 --- a/Renci.SshNet/SshClient.cs +++ /dev/null @@ -1,424 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Diagnostics.CodeAnalysis; -using Renci.SshNet.Common; - -namespace Renci.SshNet -{ - /// - /// Provides client connection to SSH server. - /// - public class SshClient : BaseClient - { - /// - /// Holds the list of forwarded ports - /// - private readonly List _forwardedPorts; - - private Stream _inputStream; - - /// - /// Gets the list of forwarded ports. - /// - public IEnumerable ForwardedPorts - { - get - { - return this._forwardedPorts.AsReadOnly(); - } - } - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// - /// - /// - /// - /// - /// - /// is null. - public SshClient(ConnectionInfo connectionInfo) - : this(connectionInfo, false) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication password. - /// is null. - /// is invalid, or is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "C2A000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SshClient(string host, int port, string username, string password) - : this(new PasswordConnectionInfo(host, port, username, password), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication password. - /// - /// - /// - /// is null. - /// is invalid, or is null or contains whitespace characters. - public SshClient(string host, string username, string password) - : this(host, ConnectionInfo.DEFAULT_PORT, username, password) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Connection port. - /// Authentication username. - /// Authentication private key file(s) . - /// - /// - /// - /// - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - /// is not within and . - [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] - public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) - : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Connection host. - /// Authentication username. - /// Authentication private key file(s) . - /// - /// - /// - /// - /// is null. - /// is invalid, -or- is null or contains whitespace characters. - public SshClient(string host, string username, params PrivateKeyFile[] keyFiles) - : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The connection info. - /// Specified whether this instance owns the connection info. - /// is null. - /// - /// If is true, then the - /// connection info will be disposed when this instance is disposed. - /// - private SshClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo) - : base(connectionInfo, ownsConnectionInfo) - { - _forwardedPorts = new List(); - } - - #endregion - - /// - /// Called when client is disconnecting from the server. - /// - protected override void OnDisconnecting() - { - base.OnDisconnecting(); - - foreach (var port in this._forwardedPorts) - { - port.Stop(); - } - } - - /// - /// Adds the forwarded port. - /// - /// The port. - /// - /// - /// - /// - /// Forwarded port is already added to a different client. - /// is null. - /// Client is not connected. - public void AddForwardedPort(ForwardedPort port) - { - if (port == null) - throw new ArgumentNullException("port"); - - if (port.Session != null && port.Session != this.Session) - throw new InvalidOperationException("Forwarded port is already added to a different client."); - - port.Session = this.Session; - - this._forwardedPorts.Add(port); - } - - /// - /// Stops and removes the forwarded port from the list. - /// - /// Forwarded port. - /// is null. - public void RemoveForwardedPort(ForwardedPort port) - { - if (port == null) - throw new ArgumentNullException("port"); - - // Stop port forwarding before removing it - port.Stop(); - - port.Session = null; - - this._forwardedPorts.Remove(port); - } - - /// - /// Creates the command to be executed. - /// - /// The command text. - /// object. - /// Client is not connected. - public SshCommand CreateCommand(string commandText) - { - return this.CreateCommand(commandText, this.ConnectionInfo.Encoding); - } - - /// - /// Creates the command to be executed with specified encoding. - /// - /// The command text. - /// The encoding to use for results. - /// object which uses specified encoding. - /// TThis method will change current default encoding. - /// Client is not connected. - /// or is null. - public SshCommand CreateCommand(string commandText, Encoding encoding) - { - this.ConnectionInfo.Encoding = encoding; - return new SshCommand(this.Session, commandText); - } - - /// - /// Creates and executes the command. - /// - /// The command text. - /// Returns an instance of with execution results. - /// This method internally uses asynchronous calls. - /// - /// - /// - /// - /// CommandText property is empty. - /// Invalid Operation - An existing channel was used to execute this command. - /// Asynchronous operation is already in progress. - /// Client is not connected. - /// is null. - public SshCommand RunCommand(string commandText) - { - var cmd = this.CreateCommand(commandText); - cmd.Execute(); - return cmd; - } - - /// - /// Creates the shell. - /// - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode. - /// Size of the internal read buffer. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) - { - return new Shell(this.Session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize); - } - - /// - /// Creates the shell. - /// - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes) - { - return this.CreateShell(input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024); - } - - /// - /// Creates the shell. - /// - /// The input. - /// The output. - /// The extended output. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Stream input, Stream output, Stream extendedOutput) - { - return this.CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); - } - - /// - /// Creates the shell. - /// - /// The encoding to use to send the input. - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal mode. - /// Size of the internal read buffer. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize) - { - this._inputStream = new MemoryStream(); - var writer = new StreamWriter(this._inputStream, encoding); - writer.Write(input); - writer.Flush(); - this._inputStream.Seek(0, SeekOrigin.Begin); - - return this.CreateShell(this._inputStream, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize); - } - - /// - /// Creates the shell. - /// - /// The encoding. - /// The input. - /// The output. - /// The extended output. - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// The terminal modes. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes) - { - return this.CreateShell(encoding, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024); - } - - /// - /// Creates the shell. - /// - /// The encoding. - /// The input. - /// The output. - /// The extended output. - /// - /// Returns a representation of a object. - /// - public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput) - { - return this.CreateShell(encoding, input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024); - } - - /// - /// Creates the shell stream. - /// - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// Size of the buffer. - /// - /// Reference to Created ShellStream object. - /// - public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize) - { - return this.CreateShellStream(terminalName, columns, rows, width, height, bufferSize, null); - } - - /// - /// Creates the shell stream. - /// - /// Name of the terminal. - /// The columns. - /// The rows. - /// The width. - /// The height. - /// Size of the buffer. - /// The terminal mode values. - /// - /// Reference to Created ShellStream object. - /// - public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary terminalModeValues) - { - return new ShellStream(this.Session, terminalName, columns, rows, width, height, bufferSize, terminalModeValues); - } - - /// - /// Stops forwarded ports. - /// - protected override void OnDisconnected() - { - base.OnDisconnected(); - - foreach (var forwardedPort in this._forwardedPorts.ToArray()) - { - this.RemoveForwardedPort(forwardedPort); - } - - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (this._inputStream != null) - { - this._inputStream.Dispose(); - this._inputStream = null; - } - } - } -} diff --git a/Renci.SshNet/SshCommand.NET40.cs b/Renci.SshNet/SshCommand.NET40.cs deleted file mode 100644 index 9677e14..0000000 --- a/Renci.SshNet/SshCommand.NET40.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Threading; - -namespace Renci.SshNet -{ - /// - /// Represents SSH command that can be executed. - /// - public partial class SshCommand - { - /// is null. - partial void ExecuteThread(Action action) - { - ThreadPool.QueueUserWorkItem(o => action()); - } - } -} diff --git a/Renci.SshNet/SshCommand.cs b/Renci.SshNet/SshCommand.cs deleted file mode 100644 index c12d715..0000000 --- a/Renci.SshNet/SshCommand.cs +++ /dev/null @@ -1,575 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using Renci.SshNet.Messages; -using Renci.SshNet.Messages.Connection; -using Renci.SshNet.Messages.Transport; -using System.Globalization; - -namespace Renci.SshNet -{ - /// - /// Represents SSH command that can be executed. - /// - public partial class SshCommand : IDisposable - { - private readonly Session _session; - - private ChannelSession _channel; - - private CommandAsyncResult _asyncResult; - - private AsyncCallback _callback; - - private EventWaitHandle _sessionErrorOccuredWaitHandle = new AutoResetEvent(false); - - private Exception _exception; - - private bool _hasError; - - private readonly object _endExecuteLock = new object(); - - /// - /// Gets the command text. - /// - public string CommandText { get; private set; } - - /// - /// Gets or sets the command timeout. - /// - /// - /// The command timeout. - /// - /// - /// - /// - public TimeSpan CommandTimeout { get; set; } - - /// - /// Gets the command exit status. - /// - /// - /// - /// - public int ExitStatus { get; private set; } - - /// - /// Gets the output stream. - /// - /// - /// - /// - public Stream OutputStream { get; private set; } - - /// - /// Gets the extended output stream. - /// - /// - /// - /// - public Stream ExtendedOutputStream { get; private set; } - - private StringBuilder _result; - /// - /// Gets the command execution result. - /// - /// - /// - /// - public string Result - { - get - { - if (this._result == null) - { - this._result = new StringBuilder(); - } - - if (this.OutputStream != null && this.OutputStream.Length > 0) - { - using (var sr = new StreamReader(this.OutputStream, this._session.ConnectionInfo.Encoding)) - { - this._result.Append(sr.ReadToEnd()); - } - } - - return this._result.ToString(); - } - } - - private StringBuilder _error; - /// - /// Gets the command execution error. - /// - /// - /// - /// - public string Error - { - get - { - if (this._hasError) - { - if (this._error == null) - { - this._error = new StringBuilder(); - } - - if (this.ExtendedOutputStream != null && this.ExtendedOutputStream.Length > 0) - { - using (var sr = new StreamReader(this.ExtendedOutputStream, this._session.ConnectionInfo.Encoding)) - { - this._error.Append(sr.ReadToEnd()); - } - } - - return this._error.ToString(); - } - return string.Empty; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The session. - /// The command text. - /// Either , is null. - internal SshCommand(Session session, string commandText) - { - if (session == null) - throw new ArgumentNullException("session"); - if (commandText == null) - throw new ArgumentNullException("commandText"); - - this._session = session; - this.CommandText = commandText; - this.CommandTimeout = new TimeSpan(0, 0, 0, 0, -1); - - this._session.Disconnected += Session_Disconnected; - this._session.ErrorOccured += Session_ErrorOccured; - } - - /// - /// Begins an asynchronous command execution. - /// - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// - /// - /// - /// Asynchronous operation is already in progress. - /// Invalid operation. - /// CommandText property is empty. - /// Client is not connected. - /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. - public IAsyncResult BeginExecute() - { - return this.BeginExecute(null, null); - } - - /// - /// Begins an asynchronous command execution. - /// - /// An optional asynchronous callback, to be called when the command execution is complete. - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// Asynchronous operation is already in progress. - /// Invalid operation. - /// CommandText property is empty. - /// Client is not connected. - /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. - public IAsyncResult BeginExecute(AsyncCallback callback) - { - return this.BeginExecute(callback, null); - } - - /// - /// Begins an asynchronous command execution. - /// - /// An optional asynchronous callback, to be called when the command execution is complete. - /// A user-provided object that distinguishes this particular asynchronous read request from other requests. - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// Asynchronous operation is already in progress. - /// Invalid operation. - /// CommandText property is empty. - /// Client is not connected. - /// Operation has timed out. - /// Asynchronous operation is already in progress. - /// CommandText property is empty. - public IAsyncResult BeginExecute(AsyncCallback callback, object state) - { - // Prevent from executing BeginExecute before calling EndExecute - if (this._asyncResult != null) - { - throw new InvalidOperationException("Asynchronous operation is already in progress."); - } - - // Create new AsyncResult object - this._asyncResult = new CommandAsyncResult(this) - { - AsyncWaitHandle = new ManualResetEvent(false), - IsCompleted = false, - AsyncState = state, - }; - - // When command re-executed again, create a new channel - if (this._channel != null) - { - throw new SshException("Invalid operation."); - } - - this.CreateChannel(); - - if (string.IsNullOrEmpty(this.CommandText)) - throw new ArgumentException("CommandText property is empty."); - - this._callback = callback; - - this._channel.Open(); - - // Send channel command request - this._channel.SendExecRequest(this.CommandText); - - return _asyncResult; - } - - /// - /// Begins an asynchronous command execution. 22 - /// - /// The command text. - /// An optional asynchronous callback, to be called when the command execution is complete. - /// A user-provided object that distinguishes this particular asynchronous read request from other requests. - /// - /// An that represents the asynchronous command execution, which could still be pending. - /// - /// Client is not connected. - /// Operation has timed out. - public IAsyncResult BeginExecute(string commandText, AsyncCallback callback, object state) - { - this.CommandText = commandText; - - return BeginExecute(callback, state); - } - - /// - /// Waits for the pending asynchronous command execution to complete. - /// - /// The reference to the pending asynchronous request to finish. - /// Command execution result. - /// - /// - /// - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. - public string EndExecute(IAsyncResult asyncResult) - { - if (this._asyncResult == asyncResult && this._asyncResult != null) - { - lock (this._endExecuteLock) - { - if (this._asyncResult != null) - { - // Make sure that operation completed if not wait for it to finish - this.WaitOnHandle(this._asyncResult.AsyncWaitHandle); - - if (this._channel.IsOpen) - { - this._channel.SendEof(); - - this._channel.Close(); - } - - this._channel = null; - - this._asyncResult = null; - - return this.Result; - } - } - } - - throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); - } - - /// - /// Executes command specified by property. - /// - /// Command execution result - /// - /// - /// - /// - /// - /// Client is not connected. - /// Operation has timed out. - public string Execute() - { - return this.EndExecute(this.BeginExecute(null, null)); - } - - /// - /// Cancels command execution in asynchronous scenarios. - /// - public void CancelAsync() - { - if (this._channel != null && this._channel.IsOpen && this._asyncResult != null) - { - this._channel.Close(); - } - } - - /// - /// Executes the specified command text. - /// - /// The command text. - /// Command execution result - /// Client is not connected. - /// Operation has timed out. - public string Execute(string commandText) - { - this.CommandText = commandText; - - return this.Execute(); - } - - private void CreateChannel() - { - this._channel = this._session.CreateClientChannel(); - this._channel.DataReceived += Channel_DataReceived; - this._channel.ExtendedDataReceived += Channel_ExtendedDataReceived; - this._channel.RequestReceived += Channel_RequestReceived; - this._channel.Closed += Channel_Closed; - - // Dispose of streams if already exists - if (this.OutputStream != null) - { - this.OutputStream.Dispose(); - this.OutputStream = null; - } - - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Dispose(); - this.ExtendedOutputStream = null; - } - - // Initialize output streams and StringBuilders - this.OutputStream = new PipeStream(); - this.ExtendedOutputStream = new PipeStream(); - - this._result = null; - this._error = null; - } - - private void Session_Disconnected(object sender, EventArgs e) - { - // If objected is disposed or being disposed don't handle this event - if (this._isDisposed) - return; - - this._exception = new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost); - - this._sessionErrorOccuredWaitHandle.Set(); - } - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - // If objected is disposed or being disposed don't handle this event - if (this._isDisposed) - return; - - this._exception = e.Exception; - - this._sessionErrorOccuredWaitHandle.Set(); - } - - private void Channel_Closed(object sender, ChannelEventArgs e) - { - if (this.OutputStream != null) - { - this.OutputStream.Flush(); - } - - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Flush(); - } - - this._asyncResult.IsCompleted = true; - - if (this._callback != null) - { - // Execute callback on different thread - this.ExecuteThread(() => this._callback(this._asyncResult)); - } - ((EventWaitHandle)this._asyncResult.AsyncWaitHandle).Set(); - } - - private void Channel_RequestReceived(object sender, ChannelRequestEventArgs e) - { - Message replyMessage = new ChannelFailureMessage(this._channel.LocalChannelNumber); - - if (e.Info is ExitStatusRequestInfo) - { - ExitStatusRequestInfo exitStatusInfo = e.Info as ExitStatusRequestInfo; - - this.ExitStatus = (int)exitStatusInfo.ExitStatus; - - replyMessage = new ChannelSuccessMessage(this._channel.LocalChannelNumber); - } - - if (e.Info.WantReply) - { - this._session.SendMessage(replyMessage); - } - } - - private void Channel_ExtendedDataReceived(object sender, ChannelDataEventArgs e) - { - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Write(e.Data, 0, e.Data.Length); - this.ExtendedOutputStream.Flush(); - } - - if (e.DataTypeCode == 1) - { - this._hasError = true; - } - } - - private void Channel_DataReceived(object sender, ChannelDataEventArgs e) - { - if (this.OutputStream != null) - { - this.OutputStream.Write(e.Data, 0, e.Data.Length); - this.OutputStream.Flush(); - } - - if (this._asyncResult != null) - { - lock (this._asyncResult) - { - this._asyncResult.BytesReceived += e.Data.Length; - } - } - } - - /// Command '{0}' has timed out. - /// The actual command will be included in the exception message. - private void WaitOnHandle(WaitHandle waitHandle) - { - var waitHandles = new[] - { - this._sessionErrorOccuredWaitHandle, - waitHandle - }; - - switch (WaitHandle.WaitAny(waitHandles, this.CommandTimeout)) - { - case 0: - throw this._exception; - case WaitHandle.WaitTimeout: - throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", this.CommandText)); - } - } - - partial void ExecuteThread(Action action); - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged ResourceMessages. - /// - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - // If disposing equals true, dispose all managed - // and unmanaged ResourceMessages. - if (disposing) - { - - this._session.Disconnected -= Session_Disconnected; - this._session.ErrorOccured -= Session_ErrorOccured; - - // Dispose managed ResourceMessages. - if (this.OutputStream != null) - { - this.OutputStream.Dispose(); - this.OutputStream = null; - } - - // Dispose managed ResourceMessages. - if (this.ExtendedOutputStream != null) - { - this.ExtendedOutputStream.Dispose(); - this.ExtendedOutputStream = null; - } - - // Dispose managed ResourceMessages. - if (this._sessionErrorOccuredWaitHandle != null) - { - this._sessionErrorOccuredWaitHandle.Dispose(); - this._sessionErrorOccuredWaitHandle = null; - } - - // Dispose managed ResourceMessages. - if (this._channel != null) - { - this._channel.DataReceived -= Channel_DataReceived; - this._channel.ExtendedDataReceived -= Channel_ExtendedDataReceived; - this._channel.RequestReceived -= Channel_RequestReceived; - this._channel.Closed -= Channel_Closed; - - this._channel.Dispose(); - this._channel = null; - } - } - - // Note disposing has been done. - this._isDisposed = true; - } - } - - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~SshCommand() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/SubsystemSession.cs b/Renci.SshNet/SubsystemSession.cs deleted file mode 100644 index 97ed6dd..0000000 --- a/Renci.SshNet/SubsystemSession.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using System.Globalization; -using System.Text; -using System.Threading; -using Renci.SshNet.Channels; -using Renci.SshNet.Common; -using Renci.SshNet.Messages.Connection; - -namespace Renci.SshNet.Sftp -{ - /// - /// Base class for SSH subsystem implementations - /// - public abstract class SubsystemSession : IDisposable - { - private readonly Session _session; - private readonly string _subsystemName; - private ChannelSession _channel; - private Exception _exception; - private EventWaitHandle _errorOccuredWaitHandle = new ManualResetEvent(false); - private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(false); - - /// - /// Specifies a timeout to wait for operation to complete - /// - protected TimeSpan _operationTimeout; - - /// - /// Occurs when an error occurred. - /// - public event EventHandler ErrorOccurred; - - /// - /// Occurs when session has been disconnected form the server. - /// - public event EventHandler Disconnected; - - /// - /// Gets the channel associated with this session. - /// - /// - /// The channel associated with this session. - /// - internal ChannelSession Channel - { - get { return _channel; } - } - - /// - /// Gets the character encoding to use. - /// - protected Encoding Encoding { get; private set; } - - /// - /// Initializes a new instance of the SubsystemSession class. - /// - /// The session. - /// Name of the subsystem. - /// The operation timeout. - /// The character encoding to use. - /// or or is null. - protected SubsystemSession(Session session, string subsystemName, TimeSpan operationTimeout, Encoding encoding) - { - if (session == null) - throw new ArgumentNullException("session"); - if (subsystemName == null) - throw new ArgumentNullException("subsystemName"); - if (encoding == null) - throw new ArgumentNullException("encoding"); - - this._session = session; - this._subsystemName = subsystemName; - this._operationTimeout = operationTimeout; - this.Encoding = encoding; - } - - /// - /// Connects subsystem on SSH channel. - /// - public void Connect() - { - this._channel = this._session.CreateClientChannel(); - - this._session.ErrorOccured += Session_ErrorOccured; - this._session.Disconnected += Session_Disconnected; - this._channel.DataReceived += Channel_DataReceived; - this._channel.Closed += Channel_Closed; - this._channel.Open(); - this._channel.SendSubsystemRequest(_subsystemName); - this.OnChannelOpen(); - } - - /// - /// Disconnects subsystem channel. - /// - public void Disconnect() - { - this._channel.SendEof(); - this._channel.Close(); - } - - /// - /// Sends data to the subsystem. - /// - /// The data to be sent. - public void SendData(byte[] data) - { - this._channel.SendData(data); - } - - /// - /// Called when channel is open. - /// - protected abstract void OnChannelOpen(); - - /// - /// Called when data is received. - /// - /// The data type code. - /// The data. - protected abstract void OnDataReceived(uint dataTypeCode, byte[] data); - - /// - /// Raises the error. - /// - /// The error. - protected void RaiseError(Exception error) - { - this._exception = error; - - var errorOccuredWaitHandle = _errorOccuredWaitHandle; - if (errorOccuredWaitHandle != null) - errorOccuredWaitHandle.Set(); - SignalErrorOccurred(error); - } - - private void Channel_DataReceived(object sender, ChannelDataEventArgs e) - { - this.OnDataReceived(e.DataTypeCode, e.Data); - } - - private void Channel_Closed(object sender, ChannelEventArgs e) - { - var channelClosedWaitHandle = _channelClosedWaitHandle; - if (channelClosedWaitHandle != null) - channelClosedWaitHandle.Set(); - } - - internal void WaitOnHandle(WaitHandle waitHandle, TimeSpan operationTimeout) - { - var waitHandles = new[] - { - this._errorOccuredWaitHandle, - this._channelClosedWaitHandle, - waitHandle - }; - - switch (WaitHandle.WaitAny(waitHandles, operationTimeout)) - { - case 0: - throw this._exception; - case 1: - throw new SshException("Channel was closed."); - case WaitHandle.WaitTimeout: - throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Operation has timed out.")); - } - } - - private void Session_Disconnected(object sender, EventArgs e) - { - SignalDisconnected(); - this.RaiseError(new SshException("Connection was lost")); - } - - private void Session_ErrorOccured(object sender, ExceptionEventArgs e) - { - this.RaiseError(e.Exception); - } - - private void SignalErrorOccurred(Exception error) - { - var errorOccurred = ErrorOccurred; - if (errorOccurred != null) - { - errorOccurred(this, new ExceptionEventArgs(error)); - } - } - - private void SignalDisconnected() - { - var disconnected = Disconnected; - if (disconnected != null) - { - disconnected(this, new EventArgs()); - } - } - - #region IDisposable Members - - private bool _isDisposed; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._isDisposed) - { - if (this._channel != null) - { - this._channel.DataReceived -= Channel_DataReceived; - this._channel.Closed -= Channel_Closed; - this._channel.Dispose(); - this._channel = null; - } - - this._session.ErrorOccured -= Session_ErrorOccured; - this._session.Disconnected -= Session_Disconnected; - - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - if (this._errorOccuredWaitHandle != null) - { - this._errorOccuredWaitHandle.Dispose(); - this._errorOccuredWaitHandle = null; - } - - if (this._channelClosedWaitHandle != null) - { - this._channelClosedWaitHandle.Dispose(); - this._channelClosedWaitHandle = null; - } - } - - // Note disposing has been done. - _isDisposed = true; - } - } - - /// - /// Finalizes an instance of the class. - /// - ~SubsystemSession() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(false) is optimal in terms of - // readability and maintainability. - Dispose(false); - } - - #endregion - } -} diff --git a/Renci.SshNet/WinSSH4e1-public.snk b/Renci.SshNet/WinSSH4e1-public.snk deleted file mode 100644 index d64facbaf6f8f1f9124741c0367df314668125a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096~H-={bxh^^?U0dKOEZYw+ZK^0WCK78f z7Lrx7#Uk|;lhIpUvmK<}zCLdC*|-V?-SpA1yrIT?rYrEU#L=?qJwiePdLUA>!^;=B zfOn1-4tXfdY3vNh`dPAu;xwL^~P?JIdU4St=Tj*(CA!$qnew}95%+cH!0c3Bg3`d*cC-w>a3fT0F1ahZx9KZ#eoLwGke21WKd4*D>I$(09HLhOz0@fP1KOnwFxl^Z)16f z?rVd*?%Y48*ZKWd?9+TH@dl^q8;3J=ZrYa{2ux$JDzS1+L5Mv7g7J|Wvtf+(98@1@ z?rG@%95`MPbizi^%dnm1&zu~RF%BG&G`n6O)h-NOD&dcb@w!fA zIAayDAeiTBmR2ucNg(^qk92(4@^j5Ggi| iY+5nt3VuPH#1G#Kje%Vk>Z4#+xAJ6rcY0XH3vUg9v>|{1 diff --git a/Sshfs/Sshfs.sln b/Sshfs/Sshfs.sln index dacacc3..c05007d 100644 --- a/Sshfs/Sshfs.sln +++ b/Sshfs/Sshfs.sln @@ -5,10 +5,10 @@ VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sshfs", "Sshfs\Sshfs.csproj", "{FF4FC8BB-91A3-45FF-8449-650647610394}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet", "..\Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSTester", "FSTester\FSTester.csproj", "{1B5533B8-DFCD-4F19-9EAB-438633E7C00B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet", "..\SSH.NET\src\Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -58,38 +58,6 @@ Global {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x64.Build.0 = Release|x64 {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.ActiveCfg = Release|x86 {FF4FC8BB-91A3-45FF-8449-650647610394}.ReleaseFW2|x86.Build.0 = Release|x86 - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.ActiveCfg = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.Build.0 = Debug|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.Build.0 = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU - {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.Build.0 = Release|Any CPU {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|Any CPU.Build.0 = Debug|Any CPU {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -122,6 +90,38 @@ Global {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|x64.Build.0 = Release|Any CPU {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU {1B5533B8-DFCD-4F19-9EAB-438633E7C00B}.ReleaseFW2|x86.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x64.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Debug|x86.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Any CPU.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x64.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.ActiveCfg = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.DebugNoTimeout|x86.Build.0 = Debug|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Any CPU.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x64.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.Release|x86.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Any CPU.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|Mixed Platforms.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.Build.0 = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU + {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Sshfs/Sshfs/AboutForm.cs b/Sshfs/Sshfs/AboutForm.cs index 1c043a6..81ac86f 100644 --- a/Sshfs/Sshfs/AboutForm.cs +++ b/Sshfs/Sshfs/AboutForm.cs @@ -1,45 +1,45 @@ -using System; -using System.Diagnostics; -using System.Drawing; -using System.Reflection; -using System.Windows.Forms; -using Renci.SshNet; - -namespace Sshfs -{ - public partial class AboutForm : Form - { - private static readonly string _sshfsText = String.Format("Sshfs {0}", Assembly.GetEntryAssembly().GetName().Version); +using System; +using System.Diagnostics; +using System.Drawing; +using System.Reflection; +using System.Windows.Forms; +using Renci.SshNet; + +namespace Sshfs +{ + public partial class AboutForm : Form + { + private static readonly string _sshfsText = String.Format("Sshfs {0}", Assembly.GetEntryAssembly().GetName().Version); private static readonly string _dokanText = - (DokanNet.Dokan.Version < 600) - ? String.Format("Dokan {0}.{1}.{2}", DokanNet.Dokan.Version / 100, (DokanNet.Dokan.Version % 100) / 10, DokanNet.Dokan.Version % 10) - : String.Format("Dokan {0}.{1}.{2}.{3}", DokanNet.Dokan.Version / 1000, (DokanNet.Dokan.Version % 1000) / 100, (DokanNet.Dokan.Version % 100) / 10, DokanNet.Dokan.Version % 10); - private static readonly string _sshnetText = String.Format("SSH.NET {0}", Assembly.GetAssembly(typeof (SshClient)).GetName().Version); - - public AboutForm() - { - InitializeComponent(); - label1.Text = _sshfsText; - label2.Text = _dokanText; - label3.Text = _sshnetText; - } - - private void ok_Click(object sender, EventArgs e) - { - this.Close(); - } - - private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - Process.Start(String.Format("http:\\{0}", (sender as LinkLabel).Text)); - } - - private void panel1_Paint(object sender, PaintEventArgs e) - { - // ControlPaint.DrawBorder3D(e.Graphics,0,0,panel1.Width,panel1.Height,Border3DStyle.); - e.Graphics.DrawLine(new Pen(SystemColors.ActiveBorder,3), 0, panel1.Height, panel1.Width, panel1.Height); - - // ControlPaint.DrawBorder(e.Graphics, new Rectangle(0, panel1.Height-1, panel1.Width, panel1.Height-1), SystemColors.ActiveBorder, ButtonBorderStyle.Dashed); - } - } -} + (DokanNet.Dokan.Version < 600) + ? String.Format("Dokan {0}.{1}.{2}", DokanNet.Dokan.Version / 100, (DokanNet.Dokan.Version % 100) / 10, DokanNet.Dokan.Version % 10) + : String.Format("Dokan {0}.{1}.{2}.{3}", DokanNet.Dokan.Version / 1000, (DokanNet.Dokan.Version % 1000) / 100, (DokanNet.Dokan.Version % 100) / 10, DokanNet.Dokan.Version % 10); + private static readonly string _sshnetText = String.Format("SSH.NET {0}", Assembly.GetAssembly(typeof (SshClient)).GetName().Version); + + public AboutForm() + { + InitializeComponent(); + label1.Text = _sshfsText; + label2.Text = _dokanText; + label3.Text = _sshnetText; + } + + private void ok_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start(String.Format("http:\\{0}", (sender as LinkLabel).Text)); + } + + private void panel1_Paint(object sender, PaintEventArgs e) + { + // ControlPaint.DrawBorder3D(e.Graphics,0,0,panel1.Width,panel1.Height,Border3DStyle.); + e.Graphics.DrawLine(new Pen(SystemColors.ActiveBorder,3), 0, panel1.Height, panel1.Width, panel1.Height); + + // ControlPaint.DrawBorder(e.Graphics, new Rectangle(0, panel1.Height-1, panel1.Width, panel1.Height-1), SystemColors.ActiveBorder, ButtonBorderStyle.Dashed); + } + } +} diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs index 299d59f..e9f0d56 100644 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -47,7 +47,7 @@ internal sealed class SftpContextStream : Stream private readonly byte[] _writeBuffer; - private readonly SftpSession _session; + private readonly ISftpSession _session; private SftpFileAttributes _attributes; private byte[] _handle; @@ -55,7 +55,7 @@ internal sealed class SftpContextStream : Stream private int _writeBufferPosition; private long _position; - internal SftpContextStream(SftpSession session, string path, FileMode mode, FileAccess access, + internal SftpContextStream(ISftpSession session, string path, FileMode mode, FileAccess access, SftpFileAttributes attributes) { Flags flags = Flags.None; @@ -456,7 +456,7 @@ public override void Write(byte[] buffer, int offset, int count) { int chunkcount = remainingcount <= WRITE_BUFFER_SIZE ? remainingcount : WRITE_BUFFER_SIZE; Buffer.BlockCopy(buffer, offset+suboffset, _writeBuffer, _writeBufferPosition/*always zero*/, chunkcount); - _session.RequestWrite(_handle, (ulong)(_position+suboffset), _writeBuffer, null, null); + _session.RequestWrite(_handle, (ulong)(_position+suboffset), _writeBuffer, chunkcount, null, null); remainingcount -= chunkcount; suboffset += chunkcount; } @@ -483,7 +483,7 @@ public override void Write(byte[] buffer, int offset, int count) if (_writeBufferPosition == WRITE_BUFFER_SIZE) { - _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, null,null); + _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, _writeBufferPosition, null,null); _writeBufferPosition = 0; @@ -521,7 +521,7 @@ private void FlushWriteBuffer() - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, null,null); + _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, _writeBufferPosition, null, null); _writeBufferPosition = 0; diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index b150991..424bffa 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -128,7 +128,7 @@ private void SetupFilesystem() KeepAliveInterval = 1; } - ConnectionInfo info; + ConnectionInfo info = null; switch (ConnectionType) { case ConnectionType.Pageant: diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index d67daf9..68b76dd 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -231,7 +231,7 @@
- + {2f5f8c90-0bd1-424f-997c-7bc6280919d1} Renci.SshNet From 0845d711495c7250c3ae01fe0cc96c8cb8b45822 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 3 Aug 2016 01:05:41 +0200 Subject: [PATCH 110/134] Renci 2016 as submodule --- .gitmodules | 3 +++ SSH.NET | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 SSH.NET diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8ece131 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "SSH.NET"] + path = SSH.NET + url = git@github.com:Foreveryone-cz/SSH.NET-for-winsshfs.git diff --git a/SSH.NET b/SSH.NET new file mode 160000 index 0000000..79d6637 --- /dev/null +++ b/SSH.NET @@ -0,0 +1 @@ +Subproject commit 79d66375fa86a2d78864b8a7f5604dcc3fa76083 From f05f1f2f1c186e5c5bba28dc73e303d95a015683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Linhart?= Date: Wed, 17 Aug 2016 15:52:10 +0200 Subject: [PATCH 111/134] Add support keybord control of contextMenu It's easier to use for blind people. --- Sshfs/Sshfs/MainForm.Designer.cs | 3 ++ Sshfs/Sshfs/MainForm.cs | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/Sshfs/Sshfs/MainForm.Designer.cs b/Sshfs/Sshfs/MainForm.Designer.cs index 314e2e5..0d24785 100644 --- a/Sshfs/Sshfs/MainForm.Designer.cs +++ b/Sshfs/Sshfs/MainForm.Designer.cs @@ -745,6 +745,7 @@ private void InitializeComponent() this.contextMenu.ShowImageMargin = false; this.contextMenu.Size = new System.Drawing.Size(135, 148); this.contextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenu_Opening); + this.contextMenu.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.contextMenu_PreviewKeyDown); // // showMenuItem // @@ -768,6 +769,7 @@ private void InitializeComponent() this.mountMenuItem.Text = "Mount"; this.mountMenuItem.DropDownClosed += new System.EventHandler(this.mountMenuItem_DropDownClosed); this.mountMenuItem.DropDownOpening += new System.EventHandler(this.mountMenuItem_DropDownOpening); + this.mountMenuItem.DropDown.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.mountMenuItem_PreviewKeyDown); // // unmountMenuItem // @@ -777,6 +779,7 @@ private void InitializeComponent() this.unmountMenuItem.Text = "Unmount"; this.unmountMenuItem.DropDownClosed += new System.EventHandler(this.unmountMenuItem_DropDownClosed); this.unmountMenuItem.DropDownOpening += new System.EventHandler(this.unmountMenuItem_DropDownOpening); + this.unmountMenuItem.DropDown.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.unmountMenuItem_PreviewKeyDown); // // toolStripSeparator2 // diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index 3bb2fba..df8890e 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -829,5 +829,57 @@ private void drive_VFSStatusChanged(object sender, EventArgs e) buttonVFSupdate(); } + private void contextMenu_PreviewKeyDown(object sender, System.Windows.Forms.PreviewKeyDownEventArgs e) + { + if (mountMenuItem.Selected && (e.KeyCode == Keys.Right || e.KeyCode == Keys.Enter)) + { + mountMenuItem.ShowDropDown(); + } + else + { + mountMenuItem.HideDropDown(); + } + if (unmountMenuItem.Selected && (e.KeyCode == Keys.Right || e.KeyCode == Keys.Enter)) + { + unmountMenuItem.ShowDropDown(); + } + else + { + unmountMenuItem.HideDropDown(); + } + } + + private void mountMenuItem_PreviewKeyDown(object sender, System.Windows.Forms.PreviewKeyDownEventArgs e) + { + switch (e.KeyCode) + { + case Keys.Right: + contextMenu.AutoClose = false; + mountMenuItem.DropDown.AutoClose = false; + break; + default: + contextMenu.AutoClose = true; + mountMenuItem.DropDown.AutoClose = true; + break; + + } + } + + private void unmountMenuItem_PreviewKeyDown(object sender, System.Windows.Forms.PreviewKeyDownEventArgs e) + { + switch (e.KeyCode) + { + case Keys.Right: + contextMenu.AutoClose = false; + unmountMenuItem.DropDown.AutoClose = false; + break; + default: + contextMenu.AutoClose = true; + unmountMenuItem.DropDown.AutoClose = true; + break; + + } + } + } } \ No newline at end of file From 915204c8d361bf2ca6b4589f13bb377f141fa4c4 Mon Sep 17 00:00:00 2001 From: Aslak Grinsted Date: Mon, 5 Sep 2016 12:35:28 +0200 Subject: [PATCH 112/134] Add link to releases to readme.md This is an attempt to address issue #89 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a544f68..a291f90 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ WinSshFS 4every1 edition I decided to share my clone of win-sshfs based on I did some improvments for my needs. Current devel branch version seems to be very stable. +Download [latest release here](https://github.com/Foreveryone-cz/win-sshfs/releases). + ![img](https://cloud.githubusercontent.com/assets/1085397/10747956/3f684d3a-7c18-11e5-8ca6-0f37a60426e4.jpg "UI") There are several changes, main differences: From e2952cbcaf2af9fc17b5756725170c9250315601 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 00:26:51 +0200 Subject: [PATCH 113/134] SSH.NET 09/2016 update --- SSH.NET | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SSH.NET b/SSH.NET index 79d6637..3471969 160000 --- a/SSH.NET +++ b/SSH.NET @@ -1 +1 @@ -Subproject commit 79d66375fa86a2d78864b8a7f5604dcc3fa76083 +Subproject commit 34719699c2d46d389df95080707c8622d9143e82 From 885a7aec5c01f7088aff4e8867e016c3fc9cfec7 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 13:04:52 +0200 Subject: [PATCH 114/134] SSH.net pageant compatibility fix --- SSH.NET | 2 +- Sshfs/Sshfs/SftpContextStream.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SSH.NET b/SSH.NET index 3471969..0d19314 160000 --- a/SSH.NET +++ b/SSH.NET @@ -1 +1 @@ -Subproject commit 34719699c2d46d389df95080707c8622d9143e82 +Subproject commit 0d19314e88cb6d49bf33d1f7f9dabee28b47a62f diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs index e9f0d56..bd762de 100644 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -456,7 +456,7 @@ public override void Write(byte[] buffer, int offset, int count) { int chunkcount = remainingcount <= WRITE_BUFFER_SIZE ? remainingcount : WRITE_BUFFER_SIZE; Buffer.BlockCopy(buffer, offset+suboffset, _writeBuffer, _writeBufferPosition/*always zero*/, chunkcount); - _session.RequestWrite(_handle, (ulong)(_position+suboffset), _writeBuffer, chunkcount, null, null); + _session.RequestWrite(_handle, (ulong)(_position+suboffset), _writeBuffer, chunkcount, 0, null, null); remainingcount -= chunkcount; suboffset += chunkcount; } @@ -483,7 +483,7 @@ public override void Write(byte[] buffer, int offset, int count) if (_writeBufferPosition == WRITE_BUFFER_SIZE) { - _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, _writeBufferPosition, null,null); + _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, _writeBufferPosition, 0, null,null); _writeBufferPosition = 0; @@ -521,7 +521,7 @@ private void FlushWriteBuffer() - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, _writeBufferPosition, null, null); + _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, _writeBufferPosition, 0, null, null); _writeBufferPosition = 0; From 9a0c1bc3556a5627e61dca15549dbcd19ef62b2f Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 13:06:18 +0200 Subject: [PATCH 115/134] turn off problemtic stripmenu renderer --- Sshfs/Sshfs/ContextMenuStripThemedRenderer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sshfs/Sshfs/ContextMenuStripThemedRenderer.cs b/Sshfs/Sshfs/ContextMenuStripThemedRenderer.cs index ca3ef69..b48d972 100644 --- a/Sshfs/Sshfs/ContextMenuStripThemedRenderer.cs +++ b/Sshfs/Sshfs/ContextMenuStripThemedRenderer.cs @@ -146,7 +146,7 @@ protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) } } - protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) + /* protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) { if (e.ToolStrip.IsDropDown && IsSupported) { @@ -177,7 +177,7 @@ protected override void OnRenderImageMargin(ToolStripRenderEventArgs e) { base.OnRenderImageMargin(e); } - } + }*/ } From eca4b27a7ffd733826ad7e61a7c30acc11c378fa Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 14:37:07 +0200 Subject: [PATCH 116/134] warnings fixies --- .../FSTester/Tests/Others/BackupAndShared.cs | 3 +- Sshfs/Sshfs/Program.cs | 58 +++++++++---------- Sshfs/Sshfs/SftpFilesystem.cs | 24 ++++---- 3 files changed, 42 insertions(+), 43 deletions(-) diff --git a/Sshfs/FSTester/Tests/Others/BackupAndShared.cs b/Sshfs/FSTester/Tests/Others/BackupAndShared.cs index a8ff71f..86377cc 100644 --- a/Sshfs/FSTester/Tests/Others/BackupAndShared.cs +++ b/Sshfs/FSTester/Tests/Others/BackupAndShared.cs @@ -55,10 +55,9 @@ override public bool Go() catch(Exception ex) { this.lastError = ex.Message; - return false; } - return true; + return false; } } } diff --git a/Sshfs/Sshfs/Program.cs b/Sshfs/Sshfs/Program.cs index 1717216..98e14cd 100644 --- a/Sshfs/Sshfs/Program.cs +++ b/Sshfs/Sshfs/Program.cs @@ -1,33 +1,33 @@ -#region - -using System; -using System.Diagnostics; -using System.IO; - -#endregion - -namespace Sshfs -{ - internal static class Program +#region + +using System; +using System.Diagnostics; +using System.IO; + +#endregion + +namespace Sshfs +{ + internal static class Program { - static SftpManagerApplication app; - - /// - /// The main entry point for the application. - /// - [STAThread] - private static void Main(params string[] args ) - { - -#if DEBUG + //static SftpManagerApplication app; + + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main(params string[] args ) + { + +#if DEBUG Debug.AutoFlush = true; - //Debug.Listeners.Clear(); + //Debug.Listeners.Clear(); //Debug.Listeners.Add(new DelimitedListTraceListener(String.Format("{0}\\log{1:yyyy-MM-dd-HH-mm-ss}.txt",Environment.CurrentDirectory,DateTime.Now), "debug")); - Debug.Listeners.Add(new DelimitedListTraceListener(Environment.CurrentDirectory+"\\last.log", "debug")); - //Debug.Listeners.Add(Console.Out); -#endif - SftpManagerApplication app = new SftpManagerApplication(); - app.Run(args); - } - } + Debug.Listeners.Add(new DelimitedListTraceListener(Environment.CurrentDirectory+"\\last.log", "debug")); + //Debug.Listeners.Add(Console.Out); +#endif + SftpManagerApplication app = new SftpManagerApplication(); + app.Run(args); + } + } } \ No newline at end of file diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index c057901..ceb62ec 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -335,7 +335,7 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha return (NtStatus)0xC0000103L; //STATUS_NOT_A_DIRECTORY } } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { } return status; @@ -366,7 +366,7 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha { sftpFileAttributes = GetAttributes(path); } - catch(SftpPathNotFoundException e) + catch(SftpPathNotFoundException) { Debug.WriteLine("File not found"); sftpFileAttributes = null; @@ -436,7 +436,7 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha SetLastError(183); //ERROR_ALREADY_EXISTS } } - catch (SshException ex) // Don't have access rights or try to read broken symlink + catch (SshException) // Don't have access rights or try to read broken symlink { var ownerpath = path.Substring(0, path.LastIndexOf('/')); var sftpPathAttributes = CacheGetAttr(ownerpath); @@ -448,7 +448,7 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha { sftpFileAttributes = GetAttributes(ownerpath); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); sftpFileAttributes = null; @@ -725,7 +725,7 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio { sftpFileAttributes = GetAttributes(path); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); sftpFileAttributes = null; @@ -743,7 +743,7 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio { sftpFileAttributes = GetAttributes(path); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); sftpFileAttributes = null; @@ -961,7 +961,7 @@ NtStatus IDokanOperations.SetFileAttributes(string fileName, FileAttributes attr { currentattr = GetAttributes(path); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); currentattr = null; @@ -997,7 +997,7 @@ NtStatus IDokanOperations.SetFileAttributes(string fileName, FileAttributes attr { SetAttributes(GetUnixPath(fileName), currentattr); } - catch(SftpPermissionDeniedException e) + catch(SftpPermissionDeniedException) { return NtStatus.AccessDenied; } @@ -1029,7 +1029,7 @@ NtStatus IDokanOperations.SetFileTime(string fileName, DateTime? creationTime, D { tempAttributes = GetAttributes(GetUnixPath(fileName)); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); tempAttributes = null; @@ -1060,7 +1060,7 @@ NtStatus IDokanOperations.DeleteFile(string fileName, DokanFileInfo info) { sftpFileAttributes = GetAttributes(parentPath); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); sftpFileAttributes = null; @@ -1100,7 +1100,7 @@ NtStatus IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) { sftpFileAttributes = GetAttributes(parentPath); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); sftpFileAttributes = null; @@ -1163,7 +1163,7 @@ NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, { sftpFileAttributes = GetAttributes(newpath); } - catch (SftpPathNotFoundException e) + catch (SftpPathNotFoundException) { Debug.WriteLine("File not found"); sftpFileAttributes = null; From b8b528108ff0f1f73e99fba3b6a38fe1e7e25c09 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 14:37:52 +0200 Subject: [PATCH 117/134] version hook script udpate path --- runversionhook.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runversionhook.bat b/runversionhook.bat index 20b6b6e..43af75e 100644 --- a/runversionhook.bat +++ b/runversionhook.bat @@ -1,2 +1,2 @@ -"%SYSTEMDRIVE%\Program Files (x86)\Git\bin\bash.exe" --login version.hook.sh +"%SYSTEMDRIVE%\Program Files\Git\bin\bash.exe" --login version.hook.sh pause \ No newline at end of file From 81204b5c623f47c5eb2eba0ddbd6dc799cd0c1f3 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 16:36:37 +0200 Subject: [PATCH 118/134] 1.6.1, add simple installer, readme update --- README.md | 32 +++++----- Sshfs/Sshfs.sln | 30 ++++++++++ Sshfs/Sshfs/Properties/AssemblyInfo.cs | 16 ++--- Sshfs/Sshfs/Sshfs.csproj | 2 +- Sshfs/Sshfs/app.manifest | 58 +++++++++--------- Sshfs/WinSSHFS-setup/Product.wxs | 59 +++++++++++++++++++ .../WinSSHFS-setup/WinSshFs Installer.wixproj | 46 +++++++++++++++ 7 files changed, 189 insertions(+), 54 deletions(-) create mode 100644 Sshfs/WinSSHFS-setup/Product.wxs create mode 100644 Sshfs/WinSSHFS-setup/WinSshFs Installer.wixproj diff --git a/README.md b/README.md index 0b30d82..d6c4e09 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,27 @@ -WinSshFS 4every1 edition +[![Build status](https://ci.appveyor.com/api/projects/status/bhqp9ib4bkv951w4?svg=true)](https://ci.appveyor.com/project/dimov-cz/win-sshfs) +| +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=winsshfs%40gmail%2ecom&lc=GB&item_name=WinSSHFS%20support%20donation&item_number=WinSSHFS¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted) +[![Donate](https://img.shields.io/badge/donate-bitcoin-green.svg)](bitcoin:129dWbvuc11dy4PRBM8e2aEcL2bAunYokG?&message=WinSSHFS donation&label=WinSSHFS) +| +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/Foreveryone-cz/win-sshfs.svg)](http://isitmaintained.com/project/Foreveryone-cz/win-sshfs "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/Foreveryone-cz/win-sshfs.svg)](http://isitmaintained.com/project/Foreveryone-cz/win-sshfs "Percentage of issues still open") +WinSshFS Foreveryone ======================== - -I decided to share my clone of win-sshfs based on -I did some improvments for my needs. Current devel branch version seems to be very stable. -Download [latest release here](https://github.com/Foreveryone-cz/win-sshfs/releases). - -![img](https://cloud.githubusercontent.com/assets/1085397/10747956/3f684d3a-7c18-11e5-8ca6-0f37a60426e4.jpg "UI") - -There are several changes, main differences: +Download [latest release here](https://github.com/Foreveryone-cz/win-sshfs/releases). +Main features: * Windows 10 Support -* Support for Android hosts (tested with CyanogenMod 11 [Android 4.4], requires busybox to be installed) -* current Renci SSH (2014.4.6beta) -* solved few bugs like payload, 2 hosts and others * Puttyant (Pageant) support -* settings files location is in fixed place (%localappdata%\WinSshFS) -* "spooldrive" - all remote hosts can by mount as mountpoint dir in one virtual drive +* Support for Android hosts (tested with CyanogenMod 11 [Android 4.4], requires busybox to be installed) +* Spooldrive - remote hosts can by mounted as directories on same virtual drive * archive flag of file in windows represents and controls permission for group: * ON => group have same rights as owner * OFF => same rights as others) * Ability to use Proxy for connections * Send Keepalive packets. (Not configurable, each 60sec hardcoded) -* I use different versioning: 1.5.12.5 = version.subversion.release.build -And probably others , see logs for details. +![img](https://cloud.githubusercontent.com/assets/1085397/10747956/3f684d3a-7c18-11e5-8ca6-0f37a60426e4.jpg "UI") + +Development version: + [![Build status](https://ci.appveyor.com/api/projects/status/bhqp9ib4bkv951w4/branch/devel?svg=true)](https://ci.appveyor.com/project/dimov-cz/win-sshfs/branch/devel) diff --git a/Sshfs/Sshfs.sln b/Sshfs/Sshfs.sln index c05007d..6ef8b40 100644 --- a/Sshfs/Sshfs.sln +++ b/Sshfs/Sshfs.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSTester", "FSTester\FSTest EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Renci.SshNet", "..\SSH.NET\src\Renci.SshNet\Renci.SshNet.csproj", "{2F5F8C90-0BD1-424F-997C-7BC6280919D1}" EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "WinSshFs Installer", "WinSSHFS-setup\WinSshFs Installer.wixproj", "{9552311D-F6B4-43F6-8859-7C3B41A4311D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -122,6 +124,34 @@ Global {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x64.Build.0 = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.ActiveCfg = Release|Any CPU {2F5F8C90-0BD1-424F-997C-7BC6280919D1}.ReleaseFW2|x86.Build.0 = Release|Any CPU + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Debug|Any CPU.ActiveCfg = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Debug|x64.ActiveCfg = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Debug|x86.ActiveCfg = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Debug|x86.Build.0 = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|Any CPU.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|Any CPU.Build.0 = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|Mixed Platforms.ActiveCfg = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|Mixed Platforms.Build.0 = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|x64.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|x64.Build.0 = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|x86.ActiveCfg = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.DebugNoTimeout|x86.Build.0 = Debug|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Release|Any CPU.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Release|Mixed Platforms.Build.0 = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Release|x64.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Release|x86.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.Release|x86.Build.0 = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|Any CPU.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|Any CPU.Build.0 = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|Mixed Platforms.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|Mixed Platforms.Build.0 = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|x64.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|x64.Build.0 = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|x86.ActiveCfg = Release|x86 + {9552311D-F6B4-43F6-8859-7C3B41A4311D}.ReleaseFW2|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 614d5f3..ecfac52 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("WinSshFS 4every1 edition")] -[assembly: AssemblyDescription("SFTP Filesystem for Windows ™")] +[assembly: AssemblyTitle("WinSshFS Foreveryone")] +[assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("4every1 s.r.o.")] -[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.0.9-devel")] -[assembly: AssemblyCopyright("Copyright © 4every1 s.r.o.")] +[assembly: AssemblyCompany("Foreveryone")] +[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.0-devel")] +[assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.0.9")] -[assembly: AssemblyVersion("1.6.0.9")] -[assembly: AssemblyFileVersion("1.6.0.9")] +// [assembly: AssemblyVersion("1.6.1.0")] +[assembly: AssemblyVersion("1.6.1.0")] +[assembly: AssemblyFileVersion("1.6.1.0")] diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 68b76dd..3fe8cb2 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -82,7 +82,7 @@ LocalIntranet - true + false true diff --git a/Sshfs/Sshfs/app.manifest b/Sshfs/Sshfs/app.manifest index cfe0b89..2c2409a 100644 --- a/Sshfs/Sshfs/app.manifest +++ b/Sshfs/Sshfs/app.manifest @@ -1,9 +1,9 @@ - - - - - - + + + + + + - - - - - - - - - - - + --> + + + + + + + + + + + - - - - - - - - - + Windows will automatically select the most compatible environment.--> + + + + + + + + + + --> \ No newline at end of file diff --git a/Sshfs/WinSSHFS-setup/Product.wxs b/Sshfs/WinSSHFS-setup/Product.wxs new file mode 100644 index 0000000..c7edb66 --- /dev/null +++ b/Sshfs/WinSSHFS-setup/Product.wxs @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sshfs/WinSSHFS-setup/WinSshFs Installer.wixproj b/Sshfs/WinSSHFS-setup/WinSshFs Installer.wixproj new file mode 100644 index 0000000..c32b40d --- /dev/null +++ b/Sshfs/WinSSHFS-setup/WinSshFs Installer.wixproj @@ -0,0 +1,46 @@ + + + + Debug + x86 + 3.10 + 9552311d-f6b4-43f6-8859-7c3b41a4311d + 2.0 + WinSSHFS-setup + Package + $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets + $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets + WinSshFs Installer + + + bin\$(Configuration)\ + obj\$(Configuration)\ + Debug + + + bin\$(Configuration)\ + obj\$(Configuration)\ + + + + + + + Sshfs + {ff4fc8bb-91a3-45ff-8449-650647610394} + True + True + Binaries;Content + INSTALLFOLDER + + + + + \ No newline at end of file From 60c510ca8c5fe28818620c90403f3bcbe3b5d610 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 16:56:17 +0200 Subject: [PATCH 119/134] submodule src to https Required for public access --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 8ece131..37f2d81 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "SSH.NET"] path = SSH.NET - url = git@github.com:Foreveryone-cz/SSH.NET-for-winsshfs.git + url = https://github.com/Foreveryone-cz/SSH.NET-for-winsshfs.git From 53fc8a3c1b307cb823ceba95c25a4c7a4a122035 Mon Sep 17 00:00:00 2001 From: dimov-cz Date: Sat, 8 Oct 2016 17:33:19 +0200 Subject: [PATCH 120/134] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6c4e09..1bea083 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Build status](https://ci.appveyor.com/api/projects/status/bhqp9ib4bkv951w4?svg=true)](https://ci.appveyor.com/project/dimov-cz/win-sshfs) | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=winsshfs%40gmail%2ecom&lc=GB&item_name=WinSSHFS%20support%20donation&item_number=WinSSHFS¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted) -[![Donate](https://img.shields.io/badge/donate-bitcoin-green.svg)](bitcoin:129dWbvuc11dy4PRBM8e2aEcL2bAunYokG?&message=WinSSHFS donation&label=WinSSHFS) +[![Donate](https://img.shields.io/badge/donate-bitcoin-green.svg)](https://foreveryone-cz.github.io/WinSshFS/donate-bitcoin/) | [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/Foreveryone-cz/win-sshfs.svg)](http://isitmaintained.com/project/Foreveryone-cz/win-sshfs "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/Foreveryone-cz/win-sshfs.svg)](http://isitmaintained.com/project/Foreveryone-cz/win-sshfs "Percentage of issues still open") From f2701c2ce415a9786ed21572a83b683fdf003dd7 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 22:27:19 +0200 Subject: [PATCH 121/134] Explorer diskspace on spooldrive fix, remove system flag from virtual folders --- Sshfs/Sshfs/VirtualFilesystem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 841db10..ec28ac6 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -401,7 +401,7 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio fileInfo = new FileInformation { Attributes = - FileAttributes.NotContentIndexed | FileAttributes.Directory | FileAttributes.Offline | FileAttributes.System, + FileAttributes.NotContentIndexed | FileAttributes.Directory, FileName = Path.GetFileName(fileName), //String.Empty, // GetInfo info doesn't use it maybe for sorting . CreationTime = DateTime.Now, @@ -495,7 +495,7 @@ NtStatus IDokanOperations.FindFiles(string fileName, out IList { FileInformation fi = new FileInformation(); fi.FileName = mp; - fi.Attributes = FileAttributes.NotContentIndexed | FileAttributes.Directory | FileAttributes.Offline | FileAttributes.System; + fi.Attributes = FileAttributes.NotContentIndexed | FileAttributes.Directory; fi.CreationTime = DateTime.Now; fi.LastWriteTime = DateTime.Now; fi.LastAccessTime = DateTime.Now; @@ -616,10 +616,10 @@ NtStatus IDokanOperations.GetDiskFreeSpace(out long free, out long total, } } + long terabyte = (long)1024 * 1024 * 1024 * 1024; - free = 0; - total = 1024; - used = 4; + total = 10*terabyte;//1TB for Explorer to see space + used = terabyte; free = total - used; return NtStatus.Success; From 592481bd5e1ade1d607eec522ca1ec196da06d6d Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sat, 8 Oct 2016 22:27:58 +0200 Subject: [PATCH 122/134] Hotfix writerequest arguments bug --- Sshfs/Sshfs/SftpContextStream.cs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/Sshfs/Sshfs/SftpContextStream.cs b/Sshfs/Sshfs/SftpContextStream.cs index bd762de..a4c379c 100644 --- a/Sshfs/Sshfs/SftpContextStream.cs +++ b/Sshfs/Sshfs/SftpContextStream.cs @@ -456,7 +456,15 @@ public override void Write(byte[] buffer, int offset, int count) { int chunkcount = remainingcount <= WRITE_BUFFER_SIZE ? remainingcount : WRITE_BUFFER_SIZE; Buffer.BlockCopy(buffer, offset+suboffset, _writeBuffer, _writeBufferPosition/*always zero*/, chunkcount); - _session.RequestWrite(_handle, (ulong)(_position+suboffset), _writeBuffer, chunkcount, 0, null, null); + _session.RequestWrite( + _handle, + (ulong)(_position+suboffset), + _writeBuffer, + 0, + chunkcount, + null, + null + ); remainingcount -= chunkcount; suboffset += chunkcount; } @@ -483,7 +491,15 @@ public override void Write(byte[] buffer, int offset, int count) if (_writeBufferPosition == WRITE_BUFFER_SIZE) { - _session.RequestWrite(_handle, (ulong) (_position - WRITE_BUFFER_SIZE), _writeBuffer, _writeBufferPosition, 0, null,null); + _session.RequestWrite( + _handle, + (ulong) (_position - WRITE_BUFFER_SIZE), + _writeBuffer, + 0, + _writeBufferPosition, + null, + null + ); _writeBufferPosition = 0; @@ -521,7 +537,15 @@ private void FlushWriteBuffer() - _session.RequestWrite(_handle, (ulong) (_position - _writeBufferPosition), data, _writeBufferPosition, 0, null, null); + _session.RequestWrite( + _handle, + (ulong) (_position - _writeBufferPosition), + data, + 0, + _writeBufferPosition, + null, + null + ); _writeBufferPosition = 0; From 4915d39e704895c386a79b4a0205f4caffc01dcb Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 9 Oct 2016 03:59:00 +0200 Subject: [PATCH 123/134] Improved symlink support --- SSH.NET | 2 +- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 +- Sshfs/Sshfs/SftpDrive.cs | 1 + Sshfs/Sshfs/SftpFilesystem.cs | 181 +++++++++++++++++-------- Sshfs/Sshfs/VirtualFilesystem.cs | 4 +- 5 files changed, 129 insertions(+), 67 deletions(-) diff --git a/SSH.NET b/SSH.NET index 0d19314..0480593 160000 --- a/SSH.NET +++ b/SSH.NET @@ -1 +1 @@ -Subproject commit 0d19314e88cb6d49bf33d1f7f9dabee28b47a62f +Subproject commit 048059381bd982f49832119a96f7a34a64dbb998 diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index ecfac52..1bc8685 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.0-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.6-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.0")] -[assembly: AssemblyVersion("1.6.1.0")] -[assembly: AssemblyFileVersion("1.6.1.0")] +// [assembly: AssemblyVersion("1.6.1.6")] +[assembly: AssemblyVersion("1.6.1.6")] +[assembly: AssemblyFileVersion("1.6.1.6")] diff --git a/Sshfs/Sshfs/SftpDrive.cs b/Sshfs/Sshfs/SftpDrive.cs index 424bffa..67651fa 100644 --- a/Sshfs/Sshfs/SftpDrive.cs +++ b/Sshfs/Sshfs/SftpDrive.cs @@ -171,6 +171,7 @@ private void SetupFilesystem() } _connection = Settings.Default.UseNetworkDrive ? String.Format("\\\\{0}\\{1}\\{2}", info.Host, Root, info.Username) : Name; + info.Timeout = new TimeSpan(0,0,5); _filesystem = new SftpFilesystem(info, Root,_connection,Settings.Default.UseOfflineAttribute,false, (int) Settings.Default.AttributeCacheTimeout, (int) Settings.Default.DirContentCacheTimeout); Debug.WriteLine("Connecting..."); diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index ceb62ec..d91f670 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -309,6 +309,28 @@ private bool GroupRightsSameAsOwner(SftpFileAttributes attributes) && (attributes.GroupCanExecute == attributes.OwnerCanExecute); } + override public SftpFileAttributes GetAttributes(string path) + { + SftpFileAttributes attributes = base.GetAttributes(path); + this.ExtendSFtpFileAttributes(path, attributes); + return attributes; + } + + private SftpFileAttributes ExtendSFtpFileAttributes(string path, SftpFileAttributes attributes) + { + if (attributes.IsSymbolicLink) + { + SftpFile symTarget = this.GetSymbolicLinkTarget(path); + attributes.IsSymbolicLinkToDirectory = symTarget.Attributes.IsDirectory; + attributes.SymbolicLinkTarget = symTarget.FullName; + if (!attributes.IsSymbolicLinkToDirectory) + { + attributes.Size = symTarget.Attributes.Size; + } + } + return attributes; + } + #endregion #region DokanOperations @@ -320,31 +342,49 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha //Split into four methods? LogFSActionInit("CreateFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1} IsDirectory:{2}", mode, options, info.IsDirectory); + if (fileName.Contains("symlinkfile")) + { + } + if (info.IsDirectory) { - if (mode == FileMode.Open) + SftpFileAttributes attributesDir = null; + try { + attributesDir = this.GetAttributes(this.GetUnixPath(fileName));//todo load from cache first + } + catch (SftpPathNotFoundException){} + if (attributesDir == null || attributesDir.IsDirectory || attributesDir.IsSymbolicLinkToDirectory) { - NtStatus status = OpenDirectory(fileName, info); - - try + + if (mode == FileMode.Open) { - if (status == NtStatus.ObjectNameNotFound) + NtStatus status = OpenDirectory(fileName, info); + + try { - GetAttributes(fileName); - //no expception -> its file - return (NtStatus)0xC0000103L; //STATUS_NOT_A_DIRECTORY + if (status == NtStatus.ObjectNameNotFound) + { + GetAttributes(fileName); + //no expception -> its file + return (NtStatus)0xC0000103L; //STATUS_NOT_A_DIRECTORY + } } + catch (SftpPathNotFoundException) + { + } + return status; + } - catch (SftpPathNotFoundException) - { - } - return status; + if (mode == FileMode.CreateNew) + return CreateDirectory(fileName, info); + return NtStatus.NotImplemented; + } + else + { + //its symbolic link behaving like directory? + return NtStatus.NotImplemented; } - if (mode == FileMode.CreateNew) - return CreateDirectory(fileName, info); - - return NtStatus.NotImplemented; } if (fileName.EndsWith("desktop.ini", StringComparison.OrdinalIgnoreCase) || @@ -388,7 +428,7 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha if (((uint)access & 0xe0000027) == 0 || sftpFileAttributes.IsDirectory) //check if only wants to read attributes,security info or open directory { - info.IsDirectory = sftpFileAttributes.IsDirectory; + info.IsDirectory = sftpFileAttributes.IsDirectory || sftpFileAttributes.IsSymbolicLinkToDirectory; if (options.HasFlag(FileOptions.DeleteOnClose)) { @@ -474,9 +514,6 @@ private NtStatus OpenDirectory(string fileName, DokanFileInfo info) { LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context,""); - - - string path = GetUnixPath(fileName); var sftpFileAttributes = CacheGetAttr(path); @@ -499,7 +536,7 @@ private NtStatus OpenDirectory(string fileName, DokanFileInfo info) if (sftpFileAttributes != null) { - if (!sftpFileAttributes.IsDirectory) + if (!sftpFileAttributes.IsDirectory && !sftpFileAttributes.IsSymbolicLinkToDirectory) { return (NtStatus)0xC0000103L; //STATUS_NOT_A_DIRECTORY } @@ -553,7 +590,7 @@ void IDokanOperations.Cleanup(string fileName, DokanFileInfo info) { LogFSActionInit("Cleanup", fileName, (SftpContext)info.Context, ""); - bool deleteOnCloseWorkAround = false; + bool deleteOnCloseWorkAround = false;//TODO not used probably, can be removed if (info.Context != null) { @@ -567,16 +604,33 @@ void IDokanOperations.Cleanup(string fileName, DokanFileInfo info) if (info.DeleteOnClose || deleteOnCloseWorkAround) { string path = GetUnixPath(fileName); - if (info.IsDirectory) + if (info.IsDirectory) //can be also symlink file! { try { - DeleteDirectory(path); + SftpFileAttributes attributes = this.CacheGetAttr(path); + if (attributes == null) + { + attributes = this.GetAttributes(path); + } + if (attributes == null) + { + //shoud never happen + throw new SftpPathNotFoundException(); + } + if (attributes.IsSymbolicLink) //symlink file or dir, can be both + { + DeleteFile(path); + } + else + { + DeleteDirectory(path); + } + } - catch (SftpPathNotFoundException) //in case we are dealing with simbolic link + catch (Exception) //in case we are dealing with simbolic link { //This may cause an error - DeleteFile(path); } } else @@ -770,7 +824,7 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio LastWriteTime = sftpFileAttributes.LastWriteTime, Length = sftpFileAttributes.Size }; - if (sftpFileAttributes.IsDirectory) + if (sftpFileAttributes.IsDirectory || sftpFileAttributes.IsSymbolicLinkToDirectory) { fileInfo.Attributes |= FileAttributes.Directory; fileInfo.Length = 0; // Windows directories use length of 0 @@ -830,7 +884,7 @@ NtStatus IDokanOperations.FindFiles(string fileName, out IList (files as List).AddRange(sftpFiles.Select( file => { - var sftpFileAttributes = file.Attributes; + var sftpFileAttributes = this.ExtendSFtpFileAttributes(file.FullName, file.Attributes); var fileInformation = new FileInformation { @@ -863,19 +917,8 @@ NtStatus IDokanOperations.FindFiles(string fileName, out IList }; if (sftpFileAttributes.IsSymbolicLink) { - try - { - SftpFile symTarget = GetSymbolicLinkTarget(file.FullName); - - if (symTarget.Attributes.IsDirectory) - { - fileInformation.Attributes |= FileAttributes.Directory; - } - } catch (Exception) - { - Debug.WriteLine("Error getting the symlink target."); - } - //link? + /* Also files must be marked as dir to reparse work on files */ + fileInformation.Attributes |= FileAttributes.ReparsePoint | FileAttributes.Directory; } if (sftpFileAttributes.IsSocket) @@ -883,7 +926,7 @@ NtStatus IDokanOperations.FindFiles(string fileName, out IList fileInformation.Attributes |= FileAttributes.NoScrubData | FileAttributes.System | FileAttributes.Device; - }else if (sftpFileAttributes.IsDirectory) + }else if (sftpFileAttributes.IsDirectory || sftpFileAttributes.IsSymbolicLinkToDirectory) { fileInformation.Attributes |= @@ -928,8 +971,8 @@ NtStatus IDokanOperations.FindFiles(string fileName, out IList foreach ( var file in - sftpFiles.Where( - pair => !pair.IsSymbolicLink)) + sftpFiles/*.Where( + pair => !pair.IsSymbolicLink)*/) { CacheAddAttr(GetUnixPath(String.Format("{0}\\{1}", fileName , file.Name)), file.Attributes, DateTimeOffset.UtcNow.AddSeconds(timeout)); @@ -1117,38 +1160,56 @@ NtStatus IDokanOperations.DeleteDirectory(string fileName, DokanFileInfo info) LogFSActionError("DeleteDir", fileName, (SftpContext)info.Context, "Access denied"); return NtStatus.AccessDenied; } + + var fileNameUnix = GetUnixPath(fileName); + sftpFileAttributes = this.CacheGetAttr(fileNameUnix); + if (sftpFileAttributes == null) + { + try + { + sftpFileAttributes = GetAttributes(fileNameUnix); + } + catch (SftpPathNotFoundException) + { + return NtStatus.NoSuchFile;//not sure if can happen and what to return + } + } + if (sftpFileAttributes.IsSymbolicLink) + { + return NtStatus.Success; + } + + //test content: var dircache = CacheGetDir(GetUnixPath(fileName)); if (dircache != null) { - //Log("DelateCacheHit:{0}", fileName); bool test = dircache.Item2.Count == 0 || dircache.Item2.All(i => i.FileName == "." || i.FileName == ".."); - - if (test) - LogFSActionSuccess("DeleteDir", fileName, (SftpContext)info.Context, ""); - else + if (!test) + { LogFSActionError("DeleteDir", fileName, (SftpContext)info.Context, "Dir not empty"); - - return test ? NtStatus.Success : NtStatus.DirectoryNotEmpty; + return NtStatus.DirectoryNotEmpty; + } + LogFSActionSuccess("DeleteDir", fileName, (SftpContext)info.Context, ""); + return NtStatus.Success; } + //no cache hit, test live, maybe we will get why: var dir = ListDirectory(GetUnixPath(fileName)).ToList(); - if (dir == null) { LogFSActionError("DeleteDir", fileName, (SftpContext)info.Context, "Open failed, access denied?"); return NtStatus.AccessDenied; } - - // usualy there are two entries . and .. bool test2 = dir.Count == 0 || dir.All(i => i.Name == "." || i.Name == ".."); - - if (test2) - LogFSActionSuccess("DeleteDir", fileName, (SftpContext)info.Context, ""); - else + if (!test2) + { LogFSActionError("DeleteDir", fileName, (SftpContext)info.Context, "Dir not empty"); + return NtStatus.DirectoryNotEmpty; + } - return test2 ? NtStatus.Success : NtStatus.DirectoryNotEmpty; + LogFSActionSuccess("DeleteDir", fileName, (SftpContext)info.Context, ""); + return NtStatus.Success; } NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, DokanFileInfo info) @@ -1194,7 +1255,7 @@ NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, info.Context = null; - if (sftpFileAttributes.IsDirectory) + if (sftpFileAttributes.IsDirectory || sftpFileAttributes.IsSymbolicLinkToDirectory) { return NtStatus.AccessDenied; } diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index ec28ac6..8948d26 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -401,7 +401,7 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio fileInfo = new FileInformation { Attributes = - FileAttributes.NotContentIndexed | FileAttributes.Directory, + FileAttributes.NotContentIndexed | FileAttributes.Directory | FileAttributes.ReparsePoint | FileAttributes.Offline, FileName = Path.GetFileName(fileName), //String.Empty, // GetInfo info doesn't use it maybe for sorting . CreationTime = DateTime.Now, @@ -495,7 +495,7 @@ NtStatus IDokanOperations.FindFiles(string fileName, out IList { FileInformation fi = new FileInformation(); fi.FileName = mp; - fi.Attributes = FileAttributes.NotContentIndexed | FileAttributes.Directory; + fi.Attributes = FileAttributes.NotContentIndexed | FileAttributes.Directory | FileAttributes.ReparsePoint | FileAttributes.Offline; fi.CreationTime = DateTime.Now; fi.LastWriteTime = DateTime.Now; fi.LastAccessTime = DateTime.Now; From 211f843c9982e23ebf07410017c49651b2274577 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 9 Oct 2016 14:11:20 +0200 Subject: [PATCH 124/134] Libraries update, better symlinks support --- SSH.NET | 2 +- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 +- Sshfs/Sshfs/Properties/Resources.Designer.cs | 226 +++++++++---------- Sshfs/Sshfs/Properties/Settings.Designer.cs | 4 +- Sshfs/Sshfs/SftpFilesystem.cs | 17 +- Sshfs/Sshfs/Sshfs.csproj | 13 +- Sshfs/Sshfs/VirtualFilesystem.cs | 7 + Sshfs/Sshfs/app.config | 7 +- Sshfs/Sshfs/packages.config | 3 +- Sshfs/WinSSHFS-setup/Product.wxs | 32 ++- create-mirror.bat | 5 - 11 files changed, 176 insertions(+), 148 deletions(-) delete mode 100644 create-mirror.bat diff --git a/SSH.NET b/SSH.NET index 0480593..efb0252 160000 --- a/SSH.NET +++ b/SSH.NET @@ -1 +1 @@ -Subproject commit 048059381bd982f49832119a96f7a34a64dbb998 +Subproject commit efb025251b60211339c87f146832a1c9ad4728ce diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 1bc8685..245a441 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.6-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.8-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.6")] -[assembly: AssemblyVersion("1.6.1.6")] -[assembly: AssemblyFileVersion("1.6.1.6")] +// [assembly: AssemblyVersion("1.6.1.8")] +[assembly: AssemblyVersion("1.6.1.8")] +[assembly: AssemblyFileVersion("1.6.1.8")] diff --git a/Sshfs/Sshfs/Properties/Resources.Designer.cs b/Sshfs/Sshfs/Properties/Resources.Designer.cs index 89bddd2..1733fdd 100644 --- a/Sshfs/Sshfs/Properties/Resources.Designer.cs +++ b/Sshfs/Sshfs/Properties/Resources.Designer.cs @@ -1,113 +1,113 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34014 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Sshfs.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Sshfs.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap add { - get { - object obj = ResourceManager.GetObject("add", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap mount { - get { - object obj = ResourceManager.GetObject("mount", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap remove { - get { - object obj = ResourceManager.GetObject("remove", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap save { - get { - object obj = ResourceManager.GetObject("save", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Drawing.Bitmap. - /// - internal static System.Drawing.Bitmap unmount { - get { - object obj = ResourceManager.GetObject("unmount", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Sshfs.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Sshfs.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap add { + get { + object obj = ResourceManager.GetObject("add", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap mount { + get { + object obj = ResourceManager.GetObject("mount", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap remove { + get { + object obj = ResourceManager.GetObject("remove", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap save { + get { + object obj = ResourceManager.GetObject("save", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap unmount { + get { + object obj = ResourceManager.GetObject("unmount", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Sshfs/Sshfs/Properties/Settings.Designer.cs b/Sshfs/Sshfs/Properties/Settings.Designer.cs index 3432372..9696e23 100644 --- a/Sshfs/Sshfs/Properties/Settings.Designer.cs +++ b/Sshfs/Sshfs/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34014 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ namespace Sshfs.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index d91f670..5aca6f1 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -320,7 +320,16 @@ private SftpFileAttributes ExtendSFtpFileAttributes(string path, SftpFileAttribu { if (attributes.IsSymbolicLink) { - SftpFile symTarget = this.GetSymbolicLinkTarget(path); + SftpFile symTarget; + try { + symTarget = this.GetSymbolicLinkTarget(path); + } + catch (SftpPathNotFoundException) + { + //invalid symlink + attributes.SymbolicLinkTarget = null; + return attributes; + } attributes.IsSymbolicLinkToDirectory = symTarget.Attributes.IsDirectory; attributes.SymbolicLinkTarget = symTarget.FullName; if (!attributes.IsSymbolicLinkToDirectory) @@ -859,6 +868,12 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio return NtStatus.Success; } + NtStatus IDokanOperations.FindFilesWithPattern(string fileName, string searchPattern, out IList files, DokanFileInfo info) + { + files = null; + return NtStatus.NotImplemented; + } + NtStatus IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { //Log("FindFiles:{0}", fileName); diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 3fe8cb2..ac92252 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -1,5 +1,5 @@  - + Debug x86 @@ -10,7 +10,7 @@ Properties Sshfs WinSshFS - v4.5 + v4.6 512 @@ -40,6 +40,8 @@ true true true + + x86 @@ -102,8 +104,8 @@ MinimumRecommendedRules.ruleset - - ..\packages\DokanNet.1.1.0-rc1\lib\net40\DokanNet.dll + + ..\packages\DokanNet.1.1.0\lib\net46\DokanNet.dll True @@ -242,9 +244,6 @@ --> - - - diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 8948d26..52f4c4b 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -450,6 +450,12 @@ NtStatus IDokanOperations.GetFileInformation(string fileName, out FileInformatio } + NtStatus IDokanOperations.FindFilesWithPattern(string fileName, string searchPattern, out IList files, DokanFileInfo info) + { + files = null; + return NtStatus.NotImplemented; + } + NtStatus IDokanOperations.FindFiles(string fileName, out IList files, DokanFileInfo info) { SftpDrive drive = this.GetDriveByMountPoint(fileName, out fileName); @@ -499,6 +505,7 @@ NtStatus IDokanOperations.FindFiles(string fileName, out IList fi.CreationTime = DateTime.Now; fi.LastWriteTime = DateTime.Now; fi.LastAccessTime = DateTime.Now; + files.Add(fi); } } diff --git a/Sshfs/Sshfs/app.config b/Sshfs/Sshfs/app.config index 8ddde00..40578d1 100644 --- a/Sshfs/Sshfs/app.config +++ b/Sshfs/Sshfs/app.config @@ -9,15 +9,12 @@ - + - + diff --git a/Sshfs/Sshfs/packages.config b/Sshfs/Sshfs/packages.config index 6e5fe3d..e88b271 100644 --- a/Sshfs/Sshfs/packages.config +++ b/Sshfs/Sshfs/packages.config @@ -1,5 +1,4 @@  - - + \ No newline at end of file diff --git a/Sshfs/WinSSHFS-setup/Product.wxs b/Sshfs/WinSSHFS-setup/Product.wxs index c7edb66..0c3ed92 100644 --- a/Sshfs/WinSSHFS-setup/Product.wxs +++ b/Sshfs/WinSSHFS-setup/Product.wxs @@ -6,6 +6,7 @@ @@ -17,7 +18,11 @@ - + + + + + @@ -40,19 +45,30 @@ - - + + - - - - + + - + + + + + + + + + + + + + + diff --git a/create-mirror.bat b/create-mirror.bat deleted file mode 100644 index be1983b..0000000 --- a/create-mirror.bat +++ /dev/null @@ -1,5 +0,0 @@ -mkdir mirrored -cd mirrored -mkdir testdir -cd .. -"%PROGRAMFILES(x86)%\Dokan\DokanLibrary\sample\mirror\mirror.exe" /r mirrored /l t /t 1 /d /s From 167c9b1e98599f8de4a9579fa210ec3ad09ae511 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 9 Oct 2016 16:44:03 +0200 Subject: [PATCH 125/134] Fix bug when spool drive folder is substring of another folder --- Sshfs/Sshfs/VirtualFilesystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sshfs/Sshfs/VirtualFilesystem.cs b/Sshfs/Sshfs/VirtualFilesystem.cs index 52f4c4b..02b0701 100644 --- a/Sshfs/Sshfs/VirtualFilesystem.cs +++ b/Sshfs/Sshfs/VirtualFilesystem.cs @@ -175,7 +175,8 @@ private SftpDrive GetDriveByMountPoint(string fileName, out string subfspath) { if (drive.MountPoint.Length > 0) { - if (path.IndexOf(drive.MountPoint)==0) + string mpWithPath = drive.MountPoint + "\\"; + if ( path == drive.MountPoint || path.IndexOf(mpWithPath) == 0 ) { subfspath = path.Substring(drive.MountPoint.Length); if (subfspath == "") From a730d03d97e7d09fedf6db69ad90aa3ad8037145 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Sun, 9 Oct 2016 22:39:14 +0200 Subject: [PATCH 126/134] add feature to debug read/write with shadow copy symbol DEBUGSHADOWCOPY --- Sshfs/Sshfs/Program.cs | 9 ++- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 +- Sshfs/Sshfs/SftpFilesystem.cs | 100 +++++++++++++++++++++++-- Sshfs/Sshfs/Sshfs.csproj | 6 +- Sshfs/WinSSHFS-setup/Product.wxs | 4 +- 5 files changed, 111 insertions(+), 16 deletions(-) diff --git a/Sshfs/Sshfs/Program.cs b/Sshfs/Sshfs/Program.cs index 98e14cd..3343593 100644 --- a/Sshfs/Sshfs/Program.cs +++ b/Sshfs/Sshfs/Program.cs @@ -26,7 +26,14 @@ private static void Main(params string[] args ) Debug.Listeners.Add(new DelimitedListTraceListener(Environment.CurrentDirectory+"\\last.log", "debug")); //Debug.Listeners.Add(Console.Out); #endif - SftpManagerApplication app = new SftpManagerApplication(); +#if DEBUG && DEBUGSHADOWCOPY + string shadowCopyDir = Environment.CurrentDirectory + "\\debug-shadow"; + if (Directory.Exists(shadowCopyDir)) + { + Directory.Delete(shadowCopyDir, true); + } +#endif + SftpManagerApplication app = new SftpManagerApplication(); app.Run(args); } } diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 245a441..06614ce 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.8-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.10-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.8")] -[assembly: AssemblyVersion("1.6.1.8")] -[assembly: AssemblyFileVersion("1.6.1.8")] +// [assembly: AssemblyVersion("1.6.1.10")] +[assembly: AssemblyVersion("1.6.1.10")] +[assembly: AssemblyFileVersion("1.6.1.10")] diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 5aca6f1..9d49397 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -716,15 +716,55 @@ NtStatus IDokanOperations.ReadFile(string fileName, byte[] buffer, out int bytes } } LogFSActionSuccess("ReadFile", fileName, (SftpContext)info.Context, ""); +#if DEBUG && DEBUGSHADOWCOPY + try { + string shadowCopyDir = Environment.CurrentDirectory + "\\debug-shadow"; + string tmpFilePath = shadowCopyDir + "\\" + fileName.Replace("/", "\\"); + FileStream fs = File.OpenRead(tmpFilePath); + byte[] localDataShadowBuffer = new byte[buffer.Length]; + fs.Seek(offset, SeekOrigin.Begin); + fs.Close(); + int readedShadow = fs.Read(localDataShadowBuffer, 0, localDataShadowBuffer.Length); + if (readedShadow != bytesRead) + { + throw new Exception("Length of readed data from "+fileName+" differs"); + } + if (!localDataShadowBuffer.SequenceEqual(buffer)) + { + throw new Exception("Data readed from " + fileName + " differs"); + } + } + catch (Exception) + { + + } +#endif return NtStatus.Success; } NtStatus IDokanOperations.WriteFile(string fileName, byte[] buffer, out int bytesWritten, long offset, DokanFileInfo info) { + bytesWritten = 0; LogFSActionInit("WriteFile", fileName, (SftpContext)info.Context, "Ofs:{0} Len:{1}", offset, buffer.Length); - - + try { +#if DEBUG && DEBUGSHADOWCOPY + string shadowCopyDir = Environment.CurrentDirectory + "\\debug-shadow"; + string tmpFilePath = shadowCopyDir + "\\" + fileName.Replace("/","\\"); + if (!Directory.Exists(tmpFilePath)) + { + Directory.CreateDirectory(Directory.GetParent(tmpFilePath).FullName); + } + FileStream tmpFile = File.OpenWrite(tmpFilePath); + if (tmpFile.Length < offset + buffer.Length) + { + tmpFile.SetLength(offset + buffer.Length); + } + tmpFile.Seek(offset, SeekOrigin.Begin); + tmpFile.Write(buffer, 0, buffer.Length); + tmpFile.Close(); +#endif + if (info.Context == null) // who would guess { SftpFileStream handle = Open(GetUnixPath(fileName), FileMode.Create); @@ -750,10 +790,15 @@ NtStatus IDokanOperations.WriteFile(string fileName, byte[] buffer, out int byte bytesWritten = buffer.Length; // TODO there are still some apps that don't check disk free space before write } - - LogFSActionSuccess("WriteFile", fileName, (SftpContext)info.Context, "Ofs:{1} Len:{0} Written:{2}", buffer.Length, offset, bytesWritten); - return NtStatus.Success; } + catch(Exception e) + { + return NtStatus.Error; + } + + LogFSActionSuccess("WriteFile", fileName, (SftpContext)info.Context, "Ofs:{1} Len:{0} Written:{2}", buffer.Length, offset, bytesWritten); + return NtStatus.Success; + } NtStatus IDokanOperations.FlushFileBuffers(string fileName, DokanFileInfo info) @@ -1232,6 +1277,7 @@ NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, LogFSActionInit("MoveFile", oldName, (SftpContext)info.Context, "To:{0} Replace:{1}",newName, replace); + string oldpath = GetUnixPath(oldName); string newpath = GetUnixPath(newName); SftpFileAttributes sftpFileAttributes; @@ -1255,6 +1301,27 @@ NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, CacheResetParent(oldpath); CacheResetParent(newpath); CacheReset(oldpath); +#if DEBUG && DEBUGSHADOWCOPY + try + { + string shadowCopyDir = Environment.CurrentDirectory + "\\debug-shadow"; + string tmpFilePath = shadowCopyDir + "\\" + oldName.Replace("/", "\\"); + string tmpFilePath2 = shadowCopyDir + "\\" + newName.Replace("/", "\\"); + Directory.CreateDirectory(Directory.GetParent(tmpFilePath2).FullName); + if (Directory.Exists(tmpFilePath)) + { + Directory.Move(tmpFilePath, tmpFilePath2); + } + else + { + File.Move(tmpFilePath, tmpFilePath2); + } + } + catch (Exception e) + { + + } +#endif } catch (SftpPermissionDeniedException) { @@ -1295,8 +1362,29 @@ NtStatus IDokanOperations.MoveFile(string oldName, string newName, bool replace, CacheReset(oldpath); CacheResetParent(oldpath); CacheResetParent(newpath); +#if DEBUG && DEBUGSHADOWCOPY + try + { + string shadowCopyDir = Environment.CurrentDirectory + "\\debug-shadow"; + string tmpFilePath = shadowCopyDir + "\\" + oldName.Replace("/", "\\"); + string tmpFilePath2 = shadowCopyDir + "\\" + newName.Replace("/", "\\"); + Directory.CreateDirectory(Directory.GetParent(tmpFilePath2).FullName); + if (Directory.Exists(tmpFilePath)) + { + Directory.Move(tmpFilePath, tmpFilePath2); + } + else + { + File.Move(tmpFilePath, tmpFilePath2); + } + } + catch (Exception e) + { + + } +#endif } - + catch (SftpPermissionDeniedException) { LogFSActionError("MoveFile", oldName, (SftpContext)info.Context, "To:{0} Access denied", newName); diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index ac92252..04ddcab 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -44,14 +44,14 @@ - x86 + AnyCPU true full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;DEBUGSHADOWCOPYOFF prompt - 4 + 3 false diff --git a/Sshfs/WinSSHFS-setup/Product.wxs b/Sshfs/WinSSHFS-setup/Product.wxs index 0c3ed92..a4a4190 100644 --- a/Sshfs/WinSSHFS-setup/Product.wxs +++ b/Sshfs/WinSSHFS-setup/Product.wxs @@ -1,12 +1,12 @@ - + From 5e9c2b149f0d72a0eb883b0b624983a83b84c1a4 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 10 Oct 2016 00:01:03 +0200 Subject: [PATCH 127/134] versioning, cleaning --- Sshfs/Sshfs/MainForm.cs | 2 +- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 ++++---- Sshfs/Sshfs/SftpFilesystem.cs | 2 +- Sshfs/Sshfs/Sshfs.csproj | 4 +--- post-commit | 7 +++++++ pre-commit | 3 +++ 6 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 post-commit create mode 100644 pre-commit diff --git a/Sshfs/Sshfs/MainForm.cs b/Sshfs/Sshfs/MainForm.cs index df8890e..61d97d7 100644 --- a/Sshfs/Sshfs/MainForm.cs +++ b/Sshfs/Sshfs/MainForm.cs @@ -99,7 +99,7 @@ protected override void OnLoad(EventArgs e) { - notifyIcon.Text = Text = String.Format("Sshfs Manager - 4every1 edition - v. {0}", Assembly.GetEntryAssembly().GetName().Version); + notifyIcon.Text = Text = String.Format("Sshfs Manager - WinSshFS Foreveryone - v. {0}", Assembly.GetEntryAssembly().GetName().Version); portBox.Minimum = IPEndPoint.MinPort; portBox.Maximum = IPEndPoint.MaxPort; diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 06614ce..b4e668d 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.10-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.12-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.10")] -[assembly: AssemblyVersion("1.6.1.10")] -[assembly: AssemblyFileVersion("1.6.1.10")] +// [assembly: AssemblyVersion("1.6.1.12")] +[assembly: AssemblyVersion("1.6.1.12")] +[assembly: AssemblyFileVersion("1.6.1.12")] diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index 9d49397..b5b53e8 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -791,7 +791,7 @@ NtStatus IDokanOperations.WriteFile(string fileName, byte[] buffer, out int byte // TODO there are still some apps that don't check disk free space before write } } - catch(Exception e) + catch(Exception) { return NtStatus.Error; } diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 04ddcab..8850adf 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -71,9 +71,7 @@ Sshfs.Program - - app.manifest - + true diff --git a/post-commit b/post-commit new file mode 100644 index 0000000..d409924 --- /dev/null +++ b/post-commit @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -f .gitcommit.user ]; then + ./version.hook.sh + git commit --ammend -C HEAD --no-verify + rm .gitcommit.user +fi \ No newline at end of file diff --git a/pre-commit b/pre-commit new file mode 100644 index 0000000..6f853c3 --- /dev/null +++ b/pre-commit @@ -0,0 +1,3 @@ +#!/bin/bash + +echo `date` > .gitcommit.user \ No newline at end of file From ab6360580d36a798067dfef12fbd7c0765cfc4f2 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 10 Oct 2016 00:24:22 +0200 Subject: [PATCH 128/134] cleaning and version testing --- Sshfs/Sshfs/SftpFilesystem.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index b5b53e8..d363ecc 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -775,10 +775,6 @@ NtStatus IDokanOperations.WriteFile(string fileName, byte[] buffer, out int byte } else { - if (fileName == "\\public\\1\\test\\.git\\config.lock") - { - Log("Data: {0}", Encoding.ASCII.GetString(buffer)); - } SftpContextStream stream = (info.Context as SftpContext).Stream; lock (stream) From 7c0debb2ff42000b3e863cc7dfa0b77ab097839b Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 10 Oct 2016 22:44:30 +0200 Subject: [PATCH 129/134] debug version - log process name --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 ++++---- Sshfs/Sshfs/SftpFilesystem.cs | 11 +++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index b4e668d..3802ffe 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.12-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.14-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.12")] -[assembly: AssemblyVersion("1.6.1.12")] -[assembly: AssemblyFileVersion("1.6.1.12")] +// [assembly: AssemblyVersion("1.6.1.14")] +[assembly: AssemblyVersion("1.6.1.14")] +[assembly: AssemblyFileVersion("1.6.1.14")] diff --git a/Sshfs/Sshfs/SftpFilesystem.cs b/Sshfs/Sshfs/SftpFilesystem.cs index d363ecc..9a519c2 100644 --- a/Sshfs/Sshfs/SftpFilesystem.cs +++ b/Sshfs/Sshfs/SftpFilesystem.cs @@ -349,7 +349,11 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha FileAttributes attributes, DokanFileInfo info) { //Split into four methods? - LogFSActionInit("CreateFile", fileName, (SftpContext)info.Context, "Mode:{0} Options:{1} IsDirectory:{2}", mode, options, info.IsDirectory); +#if DEBUG + //info.ProcessId + string processName = Process.GetProcessById(info.ProcessId).ProcessName; + LogFSActionInit("CreateFile", fileName, (SftpContext)info.Context, "ProcessName:{0} Mode:{1} Options:{2} IsDirectory:{3}", processName, mode, options, info.IsDirectory); +#endif if (fileName.Contains("symlinkfile")) { @@ -521,7 +525,10 @@ NtStatus IDokanOperations.CreateFile(string fileName, FileAccess access, FileSha private NtStatus OpenDirectory(string fileName, DokanFileInfo info) { - LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context,""); +#if DEBUG + string processName = Process.GetProcessById(info.ProcessId).ProcessName; + LogFSActionInit("OpenDir", fileName, (SftpContext)info.Context, "ProcessName:{0}", processName); +#endif string path = GetUnixPath(fileName); var sftpFileAttributes = CacheGetAttr(path); From 8328d3e71f6f37aef2c55aa752c17bbdd842c642 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Mon, 10 Oct 2016 23:13:26 +0200 Subject: [PATCH 130/134] autoversioning fix --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 ++++---- post-commit | 10 +++++++--- pre-commit | 3 --- version.hook.sh | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) delete mode 100644 pre-commit diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 3802ffe..4e358da 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone edition 1.6.1.14-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.15-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.14")] -[assembly: AssemblyVersion("1.6.1.14")] -[assembly: AssemblyFileVersion("1.6.1.14")] +// [assembly: AssemblyVersion("1.6.1.15")] +[assembly: AssemblyVersion("1.6.1.15")] +[assembly: AssemblyFileVersion("1.6.1.15")] diff --git a/post-commit b/post-commit index d409924..150e9f6 100644 --- a/post-commit +++ b/post-commit @@ -1,7 +1,11 @@ #!/bin/bash -if [ -f .gitcommit.user ]; then +if [ ! -f .gitcommit.amend.user ]; then + echo `date` > .gitcommit.amend.user + ./version.hook.sh - git commit --ammend -C HEAD --no-verify - rm .gitcommit.user + git add Sshfs/Sshfs/Properties/AssemblyInfo.cs + git commit --amend -C HEAD --no-verify + + rm .gitcommit.amend.user fi \ No newline at end of file diff --git a/pre-commit b/pre-commit deleted file mode 100644 index 6f853c3..0000000 --- a/pre-commit +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -echo `date` > .gitcommit.user \ No newline at end of file diff --git a/version.hook.sh b/version.hook.sh index 331cca7..b10e3b7 100644 --- a/version.hook.sh +++ b/version.hook.sh @@ -5,7 +5,7 @@ #This file needs to be place in the .git/hooks/ folder and only works when a git pull is #made which contains changes in the remote repo. -PRODUCT="WinSshFS Foreveryone edition" +PRODUCT="WinSshFS Foreveryone" #get the latest tag info. The 'always' flag will give you a shortened SHA1 if no tag exists. tag=$(git describe --tags --long) From 676a23852bd33b3bc45b2d2b5e235c8ad773229e Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 12 Oct 2016 00:14:55 +0200 Subject: [PATCH 131/134] Fix registry bug --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 ++++---- Sshfs/WinSSHFS-setup/Product.wxs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 4e358da..7709b47 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.15-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.16-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.15")] -[assembly: AssemblyVersion("1.6.1.15")] -[assembly: AssemblyFileVersion("1.6.1.15")] +// [assembly: AssemblyVersion("1.6.1.16")] +[assembly: AssemblyVersion("1.6.1.16")] +[assembly: AssemblyFileVersion("1.6.1.16")] diff --git a/Sshfs/WinSSHFS-setup/Product.wxs b/Sshfs/WinSSHFS-setup/Product.wxs index a4a4190..f936686 100644 --- a/Sshfs/WinSSHFS-setup/Product.wxs +++ b/Sshfs/WinSSHFS-setup/Product.wxs @@ -34,7 +34,7 @@ Date: Wed, 12 Oct 2016 00:15:02 +0200 Subject: [PATCH 132/134] prefer x32 on debug for pageant to work --- Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 ++++---- Sshfs/Sshfs/Sshfs.csproj | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 7709b47..4242a7a 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.16-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.17-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.16")] -[assembly: AssemblyVersion("1.6.1.16")] -[assembly: AssemblyFileVersion("1.6.1.16")] +// [assembly: AssemblyVersion("1.6.1.17")] +[assembly: AssemblyVersion("1.6.1.17")] +[assembly: AssemblyFileVersion("1.6.1.17")] diff --git a/Sshfs/Sshfs/Sshfs.csproj b/Sshfs/Sshfs/Sshfs.csproj index 8850adf..85383c5 100644 --- a/Sshfs/Sshfs/Sshfs.csproj +++ b/Sshfs/Sshfs/Sshfs.csproj @@ -44,7 +44,7 @@ - AnyCPU + x86 true full false @@ -52,7 +52,7 @@ TRACE;DEBUG;DEBUGSHADOWCOPYOFF prompt 3 - false + true x86 From f365a6a66bdce1fea4436f730c111221b81a9ce4 Mon Sep 17 00:00:00 2001 From: Martin Dimov Date: Wed, 19 Oct 2016 23:20:46 +0200 Subject: [PATCH 133/134] init issue tpl, add VS gitattr --- .gitattributes | 63 ++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE.md | 0 Sshfs/Sshfs/Properties/AssemblyInfo.cs | 8 ++-- 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5033154 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +#* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..e69de29 diff --git a/Sshfs/Sshfs/Properties/AssemblyInfo.cs b/Sshfs/Sshfs/Properties/AssemblyInfo.cs index 4242a7a..1e5cb9d 100644 --- a/Sshfs/Sshfs/Properties/AssemblyInfo.cs +++ b/Sshfs/Sshfs/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyDescription("SFTP Filesystem for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Foreveryone")] -[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.17-devel")] +[assembly: AssemblyProduct("WinSshFS Foreveryone 1.6.1.18-devel")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,6 +31,6 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.6.1.17")] -[assembly: AssemblyVersion("1.6.1.17")] -[assembly: AssemblyFileVersion("1.6.1.17")] +// [assembly: AssemblyVersion("1.6.1.18")] +[assembly: AssemblyVersion("1.6.1.18")] +[assembly: AssemblyFileVersion("1.6.1.18")] From dcb33f3ff1c01533144f44f0a39d1315d03b3668 Mon Sep 17 00:00:00 2001 From: dimov-cz Date: Thu, 20 Oct 2016 00:21:46 +0200 Subject: [PATCH 134/134] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index e69de29..94a28cf 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,37 @@ +For issues and bugs please complete maximum information you now in template below. + +Fot other types of requests you can remove any section. + +If you have tested more versions, fill them all. + +Fill instructions: move 'x' in checkboxes and fill parts marked <...>. + +NOW REMOVE THIS LINE OF TEXT AND THE PREVIOUS CONTENT. +### Technical data + +#### WinSshFS + - [x] stable + - [ ] prerelease + - [ ] custom build + - Version: <1.b.c.d> + +#### Dokan driver + - [x] stable + - [ ] prerelease + - [ ] custom build + - Version: Dokany <1.b.c.d> + +#### Enviroment + - Windows version: <10> + - Architecture: + - Related software: + +#### Support + - Usage: - + - Your personal priority to solve this issue: + - Support ability: + +------------------------------------------------ + + +