From 10028cd6e852fead8300a83dba38474fb85ed31a Mon Sep 17 00:00:00 2001 From: Timothy Prepscius Date: Sun, 14 Jul 2013 12:38:31 -0400 Subject: [PATCH] adds a set of files --- License.txt | 10 + java/core/src/core/crypt/Cryptor.java | 40 + java/core/src/core/crypt/CryptorAES.java | 105 +++ java/core/src/core/crypt/CryptorAESIV.java | 93 ++ java/core/src/core/crypt/CryptorAESJCE.java | 53 ++ java/core/src/core/crypt/CryptorNone.java | 25 + java/core/src/core/crypt/CryptorRSA.java | 27 + java/core/src/core/crypt/CryptorRSAAES.java | 216 +++++ java/core/src/core/crypt/CryptorRSABC.java | 172 ++++ .../src/core/crypt/CryptorRSAFactory.java | 78 ++ .../crypt/CryptorRSAFactoryEnvironment.java | 83 ++ java/core/src/core/crypt/CryptorRSAJCE.java | 91 ++ java/core/src/core/crypt/CryptorSeed.java | 16 + java/core/src/core/crypt/HashMd5.java | 26 + java/core/src/core/crypt/HashSha256.java | 26 + java/core/src/core/crypt/HmacSha1.java | 32 + .../src/core/crypt/KeyPairFromPassword.java | 75 ++ .../crypt/KeyPairFromPasswordCryptor.java | 41 + java/core/src/core/crypt/PBE.java | 105 +++ java/core/src/core/crypt/PBEPlatform.java | 16 + java/core/src/core/crypt/PBEPlatformBC.java | 29 + .../src/core/crypt/PBEPlatformNative.java | 13 + .../src/core/crypt/PasswordValidator.java | 30 + .../src/core/exceptions/CryptoException.java | 19 + .../core/exceptions/InternalException.java | 23 + .../exceptions/PublicMessageException.java | 32 + .../core/exceptions/UserExistsException.java | 16 + java/core/src/core/io/IoChain.java | 98 +++ java/core/src/core/io/IoChainAccumulator.java | 83 ++ java/core/src/core/io/IoChainBase64.java | 41 + .../src/core/io/IoChainFinishedException.java | 10 + .../src/core/io/IoChainNewLinePackets.java | 65 ++ java/core/src/core/io/IoChainSSLSocket.java | 32 + .../core/src/core/io/IoChainSizedPackets.java | 87 ++ java/core/src/core/io/IoChainSocket.java | 111 +++ java/core/src/core/io/IoChainThread.java | 29 + .../core/src/core/io/SSLContextGenerator.java | 47 + .../core/src/core/server/captcha/Captcha.java | 146 ++++ .../src/core/server/captcha/sql/Catalog.java | 22 + .../src/core/server/captcha/sql/add_token | 1 + .../src/core/server/captcha/sql/check_token | 1 + .../src/core/server/captcha/sql/create_tables | 5 + .../src/core/server/captcha/sql/prune_tokens | 1 + .../src/core/server/captcha/sql/use_token | 1 + java/core/src/core/server/db/UserDb.java | 479 +++++++++++ java/core/src/core/server/db/sql/Catalog.java | 51 ++ .../core/src/core/server/db/sql/create_tables | 55 ++ java/core/src/core/server/db/sql/create_user | 1 + .../src/core/server/db/sql/delete_user.sql | 24 + .../server/db/sql/expunge_deleted_user.sql | 9 + .../core/server/db/sql/get_deleted_user.sql | 7 + .../db/sql/get_deleted_user_mail_block.sql | 8 + .../src/core/server/db/sql/get_last_failure | 8 + java/core/src/core/server/db/sql/get_user | 6 + .../src/core/server/db/sql/get_user_key_block | 8 + .../core/server/db/sql/get_user_mail_block | 8 + java/core/src/core/server/db/sql/mark_failure | 8 + .../src/core/server/db/sql/room_for_new_user | 4 + .../src/core/server/db/sql/set_user_key_block | 1 + .../core/server/db/sql/set_user_mail_block | 1 + java/core/src/core/server/log/Tail.java | 51 ++ .../core/server/mailextra/MailExtraDb.java | 220 +++++ .../core/server/mailextra/sql/Catalog.java | 25 + .../src/core/server/mailextra/sql/add_days_to | 10 + .../core/server/mailextra/sql/create_tables | 15 + .../core/server/mailextra/sql/get_days_left | 6 + .../mailextra/sql/get_notifications_for.sql | 10 + .../mailextra/sql/prune_notifications.sql | 0 .../mailextra/sql/set_notifications_for.sql | 4 + .../src/core/srp/SRPPacketSerializerJSON.java | 301 +++++++ java/core/src/core/srp/SRPPackets.java | 182 ++++ java/core/src/core/srp/SRPSession.java | 43 + java/core/src/core/srp/client/SRPClient.java | 91 ++ .../src/core/srp/client/SRPClientAsync.java | 71 ++ .../core/srp/client/SRPClientListener.java | 10 + .../core/srp/client/SRPClientUserSession.java | 93 ++ java/core/src/core/srp/server/SRPServer.java | 77 ++ .../core/srp/server/SRPServerUserSession.java | 177 ++++ .../srp/server/SRPServerUserSessionDb.java | 23 + java/core/src/core/srp/test/PBETest.java | 57 ++ java/core/src/core/swing/CheckListener.java | 31 + java/core/src/core/swing/Checker.java | 11 + .../src/core/swing/DimensionCalculator.java | 16 + .../src/core/swing/FileFilterStandard.java | 38 + java/core/src/core/swing/RedirectStreams.java | 81 ++ .../core/src/core/swing/SpringLayoutSize.java | 72 ++ java/core/src/core/util/Arrays.java | 73 ++ java/core/src/core/util/Base16.java | 58 ++ java/core/src/core/util/Base64.java | 68 ++ java/core/src/core/util/Block.java | 20 + java/core/src/core/util/CallSingle.java | 15 + .../core/util/CallSingleWithVariables.java | 24 + java/core/src/core/util/Characters.java | 20 + .../src/core/util/CircularByteBuffer.java | 809 ++++++++++++++++++ java/core/src/core/util/Collectionz.java | 98 +++ java/core/src/core/util/Comparators.java | 96 +++ java/core/src/core/util/DateFormat.java | 38 + java/core/src/core/util/Environment.java | 111 +++ java/core/src/core/util/ExceptionHandler.java | 11 + java/core/src/core/util/ExternalResource.java | 39 + java/core/src/core/util/FastRandom.java | 12 + java/core/src/core/util/FileSystem.java | 50 ++ java/core/src/core/util/Http.java | 65 ++ java/core/src/core/util/HttpDelegate.java | 36 + java/core/src/core/util/HttpDelegateJava.java | 88 ++ java/core/src/core/util/InternalResource.java | 27 + java/core/src/core/util/JSONRegistry.java | 31 + java/core/src/core/util/JSONSerializer.java | 202 +++++ java/core/src/core/util/JSON_.java | 284 ++++++ java/core/src/core/util/JavaSerializer.java | 38 + java/core/src/core/util/LogNull.java | 71 ++ java/core/src/core/util/LogOut.java | 112 +++ java/core/src/core/util/LogPlatform.java | 20 + java/core/src/core/util/Maps.java | 87 ++ java/core/src/core/util/Pair.java | 47 + java/core/src/core/util/Passwords.java | 23 + .../src/core/util/ProxySelectorRegex.java | 101 +++ .../src/core/util/RunnableWithVariables.java | 22 + java/core/src/core/util/SecureRandom.java | 11 + java/core/src/core/util/SimpleSerializer.java | 21 + java/core/src/core/util/SqlCatalog.java | 29 + java/core/src/core/util/Streams.java | 140 +++ java/core/src/core/util/Strings.java | 118 +++ java/core/src/core/util/StringsPlatform.java | 36 + java/core/src/core/util/Triple.java | 31 + java/core/src/core/util/WorkerThread.java | 69 ++ java/core/src/core/util/XML.java | 52 ++ java/core/src/core/util/Zip.java | 71 ++ 128 files changed, 8039 insertions(+) create mode 100644 License.txt create mode 100644 java/core/src/core/crypt/Cryptor.java create mode 100644 java/core/src/core/crypt/CryptorAES.java create mode 100644 java/core/src/core/crypt/CryptorAESIV.java create mode 100644 java/core/src/core/crypt/CryptorAESJCE.java create mode 100644 java/core/src/core/crypt/CryptorNone.java create mode 100644 java/core/src/core/crypt/CryptorRSA.java create mode 100644 java/core/src/core/crypt/CryptorRSAAES.java create mode 100644 java/core/src/core/crypt/CryptorRSABC.java create mode 100644 java/core/src/core/crypt/CryptorRSAFactory.java create mode 100644 java/core/src/core/crypt/CryptorRSAFactoryEnvironment.java create mode 100644 java/core/src/core/crypt/CryptorRSAJCE.java create mode 100644 java/core/src/core/crypt/CryptorSeed.java create mode 100644 java/core/src/core/crypt/HashMd5.java create mode 100644 java/core/src/core/crypt/HashSha256.java create mode 100644 java/core/src/core/crypt/HmacSha1.java create mode 100644 java/core/src/core/crypt/KeyPairFromPassword.java create mode 100644 java/core/src/core/crypt/KeyPairFromPasswordCryptor.java create mode 100644 java/core/src/core/crypt/PBE.java create mode 100644 java/core/src/core/crypt/PBEPlatform.java create mode 100644 java/core/src/core/crypt/PBEPlatformBC.java create mode 100644 java/core/src/core/crypt/PBEPlatformNative.java create mode 100644 java/core/src/core/crypt/PasswordValidator.java create mode 100644 java/core/src/core/exceptions/CryptoException.java create mode 100644 java/core/src/core/exceptions/InternalException.java create mode 100644 java/core/src/core/exceptions/PublicMessageException.java create mode 100644 java/core/src/core/exceptions/UserExistsException.java create mode 100644 java/core/src/core/io/IoChain.java create mode 100644 java/core/src/core/io/IoChainAccumulator.java create mode 100644 java/core/src/core/io/IoChainBase64.java create mode 100644 java/core/src/core/io/IoChainFinishedException.java create mode 100644 java/core/src/core/io/IoChainNewLinePackets.java create mode 100644 java/core/src/core/io/IoChainSSLSocket.java create mode 100644 java/core/src/core/io/IoChainSizedPackets.java create mode 100644 java/core/src/core/io/IoChainSocket.java create mode 100644 java/core/src/core/io/IoChainThread.java create mode 100644 java/core/src/core/io/SSLContextGenerator.java create mode 100644 java/core/src/core/server/captcha/Captcha.java create mode 100644 java/core/src/core/server/captcha/sql/Catalog.java create mode 100644 java/core/src/core/server/captcha/sql/add_token create mode 100644 java/core/src/core/server/captcha/sql/check_token create mode 100644 java/core/src/core/server/captcha/sql/create_tables create mode 100644 java/core/src/core/server/captcha/sql/prune_tokens create mode 100644 java/core/src/core/server/captcha/sql/use_token create mode 100644 java/core/src/core/server/db/UserDb.java create mode 100644 java/core/src/core/server/db/sql/Catalog.java create mode 100644 java/core/src/core/server/db/sql/create_tables create mode 100644 java/core/src/core/server/db/sql/create_user create mode 100644 java/core/src/core/server/db/sql/delete_user.sql create mode 100644 java/core/src/core/server/db/sql/expunge_deleted_user.sql create mode 100644 java/core/src/core/server/db/sql/get_deleted_user.sql create mode 100644 java/core/src/core/server/db/sql/get_deleted_user_mail_block.sql create mode 100644 java/core/src/core/server/db/sql/get_last_failure create mode 100644 java/core/src/core/server/db/sql/get_user create mode 100644 java/core/src/core/server/db/sql/get_user_key_block create mode 100644 java/core/src/core/server/db/sql/get_user_mail_block create mode 100644 java/core/src/core/server/db/sql/mark_failure create mode 100644 java/core/src/core/server/db/sql/room_for_new_user create mode 100644 java/core/src/core/server/db/sql/set_user_key_block create mode 100644 java/core/src/core/server/db/sql/set_user_mail_block create mode 100644 java/core/src/core/server/log/Tail.java create mode 100644 java/core/src/core/server/mailextra/MailExtraDb.java create mode 100644 java/core/src/core/server/mailextra/sql/Catalog.java create mode 100644 java/core/src/core/server/mailextra/sql/add_days_to create mode 100644 java/core/src/core/server/mailextra/sql/create_tables create mode 100644 java/core/src/core/server/mailextra/sql/get_days_left create mode 100644 java/core/src/core/server/mailextra/sql/get_notifications_for.sql create mode 100644 java/core/src/core/server/mailextra/sql/prune_notifications.sql create mode 100644 java/core/src/core/server/mailextra/sql/set_notifications_for.sql create mode 100644 java/core/src/core/srp/SRPPacketSerializerJSON.java create mode 100644 java/core/src/core/srp/SRPPackets.java create mode 100644 java/core/src/core/srp/SRPSession.java create mode 100644 java/core/src/core/srp/client/SRPClient.java create mode 100644 java/core/src/core/srp/client/SRPClientAsync.java create mode 100644 java/core/src/core/srp/client/SRPClientListener.java create mode 100644 java/core/src/core/srp/client/SRPClientUserSession.java create mode 100644 java/core/src/core/srp/server/SRPServer.java create mode 100644 java/core/src/core/srp/server/SRPServerUserSession.java create mode 100644 java/core/src/core/srp/server/SRPServerUserSessionDb.java create mode 100644 java/core/src/core/srp/test/PBETest.java create mode 100644 java/core/src/core/swing/CheckListener.java create mode 100644 java/core/src/core/swing/Checker.java create mode 100644 java/core/src/core/swing/DimensionCalculator.java create mode 100644 java/core/src/core/swing/FileFilterStandard.java create mode 100644 java/core/src/core/swing/RedirectStreams.java create mode 100644 java/core/src/core/swing/SpringLayoutSize.java create mode 100644 java/core/src/core/util/Arrays.java create mode 100644 java/core/src/core/util/Base16.java create mode 100644 java/core/src/core/util/Base64.java create mode 100644 java/core/src/core/util/Block.java create mode 100644 java/core/src/core/util/CallSingle.java create mode 100644 java/core/src/core/util/CallSingleWithVariables.java create mode 100644 java/core/src/core/util/Characters.java create mode 100644 java/core/src/core/util/CircularByteBuffer.java create mode 100644 java/core/src/core/util/Collectionz.java create mode 100644 java/core/src/core/util/Comparators.java create mode 100644 java/core/src/core/util/DateFormat.java create mode 100644 java/core/src/core/util/Environment.java create mode 100644 java/core/src/core/util/ExceptionHandler.java create mode 100644 java/core/src/core/util/ExternalResource.java create mode 100644 java/core/src/core/util/FastRandom.java create mode 100644 java/core/src/core/util/FileSystem.java create mode 100644 java/core/src/core/util/Http.java create mode 100644 java/core/src/core/util/HttpDelegate.java create mode 100644 java/core/src/core/util/HttpDelegateJava.java create mode 100644 java/core/src/core/util/InternalResource.java create mode 100644 java/core/src/core/util/JSONRegistry.java create mode 100644 java/core/src/core/util/JSONSerializer.java create mode 100644 java/core/src/core/util/JSON_.java create mode 100644 java/core/src/core/util/JavaSerializer.java create mode 100644 java/core/src/core/util/LogNull.java create mode 100644 java/core/src/core/util/LogOut.java create mode 100644 java/core/src/core/util/LogPlatform.java create mode 100644 java/core/src/core/util/Maps.java create mode 100644 java/core/src/core/util/Pair.java create mode 100644 java/core/src/core/util/Passwords.java create mode 100644 java/core/src/core/util/ProxySelectorRegex.java create mode 100644 java/core/src/core/util/RunnableWithVariables.java create mode 100644 java/core/src/core/util/SecureRandom.java create mode 100644 java/core/src/core/util/SimpleSerializer.java create mode 100644 java/core/src/core/util/SqlCatalog.java create mode 100644 java/core/src/core/util/Streams.java create mode 100644 java/core/src/core/util/Strings.java create mode 100644 java/core/src/core/util/StringsPlatform.java create mode 100644 java/core/src/core/util/Triple.java create mode 100644 java/core/src/core/util/WorkerThread.java create mode 100644 java/core/src/core/util/XML.java create mode 100644 java/core/src/core/util/Zip.java diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..3f97bb7 --- /dev/null +++ b/License.txt @@ -0,0 +1,10 @@ +Portions of this code are release GPL and portions are released BSD. + +Anything to do with the web is GPLv3 Affero + keep my name in the code. +Anything to do with the iPhone is BSD + keep my name in the code. +Anything to do with the Android is GPLv3 Affero + keep my name in the code. + +License documentation will be placed in each file accordingly. + + +If you need a different license, please contact me. diff --git a/java/core/src/core/crypt/Cryptor.java b/java/core/src/core/crypt/Cryptor.java new file mode 100644 index 0000000..8b74e44 --- /dev/null +++ b/java/core/src/core/crypt/Cryptor.java @@ -0,0 +1,40 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.callback.Callback; +import core.callback.CallbackDefault; +import core.exceptions.CryptoException; + +public abstract class Cryptor +{ + static final boolean TEST_ENCRYPTION = false; + + abstract public byte[] encrypt (byte[] bytes) throws CryptoException; + abstract public byte[] decrypt (byte[] bytes) throws CryptoException; + + public Callback encrypt_() + { + return new CallbackDefault() { + + @Override + public void onSuccess(Object... arguments) throws Exception { + next(Cryptor.this.encrypt((byte[])arguments[0])); + } + }; + } + + public Callback decrypt_() + { + return new CallbackDefault() { + + @Override + public void onSuccess(Object... arguments) throws Exception { + next(Cryptor.this.decrypt((byte[])arguments[0])); + } + }; + } +} diff --git a/java/core/src/core/crypt/CryptorAES.java b/java/core/src/core/crypt/CryptorAES.java new file mode 100644 index 0000000..e8a31bc --- /dev/null +++ b/java/core/src/core/crypt/CryptorAES.java @@ -0,0 +1,105 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.callback.Callback; +import core.callback.CallbackDefault; +import core.exceptions.CryptoException; +import core.util.Arrays; +import core.util.LogNull; +import core.util.LogOut; +import core.util.SecureRandom; + +public class CryptorAES extends Cryptor +{ + static LogNull log = new LogNull(CryptorAES.class); + + public static final int AES_KEYSIZE_BYTES = 32; + public static final int AES_IVSIZE_BYTES = 16; + static SecureRandom random = new SecureRandom(); + + byte[] key; + + public CryptorAES (byte[] key) + { + this.key = key; + } + + public static final byte[] NullIV = { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 + } ; + + public static byte[] newIV() + { + byte[] iv = new byte[AES_IVSIZE_BYTES]; + random.nextBytes(iv); + + return iv; + } + + public static byte[] newKey() + { + byte[] key = new byte[AES_KEYSIZE_BYTES]; + random.nextBytes(key); + + return key; + } + + @Override + public byte[] encrypt(byte[] bytes) throws CryptoException + { + byte[] iv = newIV(); + Cryptor cryptor = new CryptorAESIV(key, iv); + return Arrays.concat(iv, cryptor.encrypt(bytes)); + } + + @Override + public byte[] decrypt(byte[] bytes) throws CryptoException + { + byte[] iv = Arrays.copyOf(bytes, 0, AES_IVSIZE_BYTES); + byte[] data = Arrays.copyOf(bytes, AES_IVSIZE_BYTES, bytes.length - AES_IVSIZE_BYTES); + + Cryptor cryptor = new CryptorAESIV(key, iv); + return cryptor.decrypt(data); + } + + public Callback decrypt_() + { + return new CallbackDefault() { + public void onSuccess(Object... arguments) throws Exception { + byte[] bytes = (byte[]) arguments[0]; + byte[] iv = Arrays.copyOf(bytes, 0, AES_IVSIZE_BYTES); + byte[] data = Arrays.copyOf(bytes, AES_IVSIZE_BYTES, bytes.length - AES_IVSIZE_BYTES); + + log.debug("decrypt_", bytes.length, iv.length, data.length); + + Cryptor cryptor = new CryptorAESIV(key, iv); + call(cryptor.decrypt_(), data); + } + }; + } + + public Callback encrypt_() + { + byte[] iv = newIV(); + Cryptor cryptor = new CryptorAESIV(key, iv); + + return + cryptor.encrypt_() + .addCallback(new CallbackDefault(iv) { + + public void onSuccess(Object... arguments) throws Exception { + byte[] iv = V(0); + byte[] data = (byte[]) arguments[0]; + log.debug("encrypt_", iv.length, data.length); + + next(Arrays.concat(iv, data)); + } + }); + } + +} diff --git a/java/core/src/core/crypt/CryptorAESIV.java b/java/core/src/core/crypt/CryptorAESIV.java new file mode 100644 index 0000000..a95926c --- /dev/null +++ b/java/core/src/core/crypt/CryptorAESIV.java @@ -0,0 +1,93 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.util.Arrays; +import core.util.SecureRandom; + +import org.bc.crypto.BufferedBlockCipher; +import org.bc.crypto.engines.AESEngine; +import org.bc.crypto.modes.CBCBlockCipher; +import org.bc.crypto.paddings.BlockCipherPadding; +import org.bc.crypto.paddings.PKCS7Padding; +import org.bc.crypto.paddings.PaddedBufferedBlockCipher; +import org.bc.crypto.params.KeyParameter; +import org.bc.crypto.params.ParametersWithIV; + +import core.exceptions.CryptoException; + + +public class CryptorAESIV extends Cryptor +{ + static SecureRandom random = new SecureRandom(); + + byte[] key; + byte[] iv; + + public CryptorAESIV (byte[] key, byte[] iv) + { + // setup cipher parameters with key and IV + this.key = key; + this.iv = iv; + } + + @Override + public byte[] decrypt(byte[] bytes) throws CryptoException + { + try + { + BlockCipherPadding padding = new PKCS7Padding(); + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher( + new CBCBlockCipher(new AESEngine()), padding); + + ParametersWithIV params = new ParametersWithIV(new KeyParameter(key), iv); + cipher.reset(); + cipher.init(false, params); + + byte[] out = new byte[cipher.getOutputSize(bytes.length)]; + int len = cipher.processBytes(bytes, 0, bytes.length, out, 0); + len += cipher.doFinal(out, len); + + if (len == out.length) + return out; + + return Arrays.copyOf(out, len); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + + @Override + public byte[] encrypt(byte[] bytes) throws CryptoException + { + try + { + BlockCipherPadding padding = new PKCS7Padding(); + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher( + new CBCBlockCipher(new AESEngine()), padding); + + ParametersWithIV params = new ParametersWithIV(new KeyParameter(key), iv); + cipher.reset(); + cipher.init(true, params); + + byte[] out = new byte[cipher.getOutputSize(bytes.length)]; + int len = cipher.processBytes(bytes, 0, bytes.length, out, 0); + len += cipher.doFinal(out, len); + + if (len == out.length) + return out; + + return Arrays.copyOf(out, len); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + +} diff --git a/java/core/src/core/crypt/CryptorAESJCE.java b/java/core/src/core/crypt/CryptorAESJCE.java new file mode 100644 index 0000000..8536048 --- /dev/null +++ b/java/core/src/core/crypt/CryptorAESJCE.java @@ -0,0 +1,53 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import core.exceptions.CryptoException; + + +public class CryptorAESJCE extends Cryptor +{ + SecretKeySpec secretKey; + IvParameterSpec iv; + + public CryptorAESJCE (SecretKeySpec secretKey, IvParameterSpec iv) + { + this.secretKey = secretKey; + this.iv = iv; + } + + public byte[] encrypt(byte[] clearText) throws CryptoException + { + try + { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); + return cipher.doFinal(clearText); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + + public byte[] decrypt(byte[] encrypted) throws CryptoException + { + try + { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, secretKey, iv); + return cipher.doFinal(encrypted); + } + catch (Exception e) + { + throw new CryptoException (e); + } + } +} diff --git a/java/core/src/core/crypt/CryptorNone.java b/java/core/src/core/crypt/CryptorNone.java new file mode 100644 index 0000000..0bc260d --- /dev/null +++ b/java/core/src/core/crypt/CryptorNone.java @@ -0,0 +1,25 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.exceptions.CryptoException; + +public class CryptorNone extends Cryptor +{ + + @Override + public byte[] encrypt(byte[] bytes) throws CryptoException + { + return bytes; + } + + @Override + public byte[] decrypt(byte[] bytes) throws CryptoException + { + return bytes; + } + +} diff --git a/java/core/src/core/crypt/CryptorRSA.java b/java/core/src/core/crypt/CryptorRSA.java new file mode 100644 index 0000000..37fa4bc --- /dev/null +++ b/java/core/src/core/crypt/CryptorRSA.java @@ -0,0 +1,27 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +public abstract class CryptorRSA extends Cryptor +{ + byte[] publicKeyBytes, privateKeyBytes; + + public byte[] getPublicKey () + { + return publicKeyBytes; + } + + public byte[] getPrivateKey () + { + return privateKeyBytes; + } + + protected void initialize (byte[] publicKeyBytes, byte[] privateKeyBytes) + { + this.privateKeyBytes = privateKeyBytes; + this.publicKeyBytes = publicKeyBytes; + } +} diff --git a/java/core/src/core/crypt/CryptorRSAAES.java b/java/core/src/core/crypt/CryptorRSAAES.java new file mode 100644 index 0000000..134b58d --- /dev/null +++ b/java/core/src/core/crypt/CryptorRSAAES.java @@ -0,0 +1,216 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import core.util.LogNull; +import core.util.LogOut; +import core.util.Streams; +import core.callback.Callback; +import core.callback.CallbackDefault; +import core.callback.CallbackSync; +import core.callbacks.Memory; +import core.exceptions.CryptoException; + +public class CryptorRSAAES extends Cryptor +{ + static LogNull log = new LogNull(CryptorRSAAES.class); + + CryptorRSA rsa; + + public static class Version { + public static final int + R2012 = 0x00, + R201303 = 0x01, + CURRENT = R201303; + }; + + public final byte INITIAL = 0x00; + public final byte VERSION = 0x01; + public final int MAX_RSA_BLOCK_SIZE = 117; + + private final static String M_VERSION = "version"; + + public CryptorRSAAES(CryptorRSA rsa) + { + this.rsa = rsa; + } + + void readVersion (InputStream is, Memory memory) throws Exception + { + int version = is.read()&0xFF; + if (version < 0 || version > Version.CURRENT) + throw new Exception("Unknown version"); + + memory.put(M_VERSION, version); + + log.debug("readVersion", version); + } + + void writeVersion (OutputStream os) throws IOException + { + os.write(Version.CURRENT); + + log.debug("writeVersion", Version.CURRENT); + } + + Callback readEncryptedEmbeddedKey_ (InputStream is, Memory memory) + { + return new CallbackDefault(memory, is) { + public void onSuccess(Object... arguments) throws Exception + { + Memory memory = V(0); + InputStream is = V(1); + + int version = (Integer)memory.get(M_VERSION); + switch (version) { + + case Version.R2012: + Streams.readInt3(is); // ignore the wrapper + next(Streams.readBoundedArray(is)); + break; + case Version.R201303: + next(Streams.readBoundedArray(is)); + break; + }; + } + }; + } + + Callback writeEncryptedEmbeddedKey_ (OutputStream os) + { + return new CallbackDefault(os) { + public void onSuccess(Object... arguments) throws Exception + { + OutputStream os = V(0); + byte[] bytes = (byte[]) arguments[0]; + Streams.writeBoundedArray(os, bytes); + next(arguments); + } + } ; + } + + Callback decryptMainBlock_(InputStream is, Memory memory) + { + return new CallbackDefault (is, memory) { + + @Override + public void onSuccess(Object... arguments) throws Exception + { + InputStream is = V(0); + Memory memory = V(1); + + byte[] key = (byte[]) arguments[0]; + int version = (Integer)memory.get(M_VERSION); + + Cryptor cryptor = null; + + switch (version) + { + case Version.R2012: + cryptor = new CryptorAESIV(key, CryptorAES.NullIV); + break; + case Version.R201303: + cryptor = new CryptorAES(key); + break; + } + + call(cryptor.decrypt_(), Streams.readFullyBytes(is)); + } + }; + } + + Callback encryptMainBlock_ (OutputStream os, Cryptor cryptor, byte[] bytes) + { + return new CallbackDefault (os, cryptor, bytes) { + public void onSuccess(Object... arguments) throws Exception + { + OutputStream os = V(0); + Cryptor cryptor = V(1); + byte[] bytes = V(2); + + call(cryptor.encrypt_().addCallback(Streams.writeBytes_(os)), bytes); + } + }; + } + + public Callback decrypt_() + { + return new CallbackDefault () { + + @Override + public void onSuccess(Object... arguments) throws Exception + { + ByteArrayInputStream is = new ByteArrayInputStream((byte[])arguments[0]); + Memory memory = new Memory(); + readVersion(is, memory); + + call( + readEncryptedEmbeddedKey_(is, memory) + .addCallback(rsa.decrypt_()) + .addCallback(decryptMainBlock_(is, memory)) + ); + } + + }; + } + + public Callback encrypt_ () + { + return new CallbackDefault () { + + @Override + public void onSuccess(Object... arguments) throws Exception + { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + writeVersion(os); + + byte[] key = CryptorAES.newKey(); + CryptorAES aes = new CryptorAES(key); + + call( + rsa.encrypt_() + .addCallback(writeEncryptedEmbeddedKey_(os)) + .addCallback(encryptMainBlock_(os, aes, (byte[])arguments[0])) + .addCallback(Streams.toByteArray_(os)), + key + ); + } + + }; + + } + + public byte[] encrypt (byte[] bytes) throws CryptoException + { + try + { + return new CallbackSync(encrypt_()).invoke(bytes).export(); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + + + public byte[] decrypt (byte[] bytes) throws CryptoException + { + try + { + return new CallbackSync(decrypt_()).invoke(bytes).export(); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } +} diff --git a/java/core/src/core/crypt/CryptorRSABC.java b/java/core/src/core/crypt/CryptorRSABC.java new file mode 100644 index 0000000..ebd3baf --- /dev/null +++ b/java/core/src/core/crypt/CryptorRSABC.java @@ -0,0 +1,172 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.SecureRandom; + +import org.bc.asn1.ASN1InputStream; +import org.bc.asn1.ASN1Integer; +import org.bc.asn1.ASN1ObjectIdentifier; +import org.bc.asn1.ASN1OctetString; +import org.bc.asn1.ASN1Primitive; +import org.bc.asn1.ASN1Sequence; +import org.bc.asn1.DERBitString; +import org.bc.asn1.pkcs.RSAPrivateKey; +import org.bc.asn1.pkcs.RSAPublicKey; +import org.bc.crypto.encodings.PKCS1Encoding; +import org.bc.crypto.engines.RSAEngine; +import org.bc.crypto.params.RSAKeyParameters; + +import core.exceptions.CryptoException; +import core.util.Arrays; + + +public class CryptorRSABC extends CryptorRSA +{ + SecureRandom random = new SecureRandom(); + + public final int MAX_RSA_BLOCK_SIZE = 117; + + RSAPublicKey publicKey; + RSAPrivateKey privateKey; + + public static final byte[] iv = Arrays.generate(16, 0); + + public CryptorRSABC (byte[] publicKey, byte[] privateKey) throws CryptoException + { + initialize(publicKey, privateKey); + + try + { + if (privateKey != null) + { + ASN1InputStream asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(privateKey)); + ASN1Primitive keyObject = asn1InputStream.readObject(); + asn1InputStream.close(); + + ASN1Sequence keySequence = (ASN1Sequence)keyObject; + ASN1Integer pkcs8Version = (ASN1Integer) keySequence.getObjectAt(0); + ASN1Sequence algorithmSequence = (ASN1Sequence) keySequence.getObjectAt(1); + ASN1ObjectIdentifier algorithmIdentifier = (ASN1ObjectIdentifier) algorithmSequence.getObjectAt(0); + + String algorithm = algorithmIdentifier.getId(); + if (!algorithm.equals("1.2.840.113549.1.1.1")) + throw new CryptoException("Unknown RSA algorithm"); + + ASN1OctetString privateKeyOctets = (ASN1OctetString) keySequence.getObjectAt(2); + + ASN1InputStream asn1PrivateKeyStream = new ASN1InputStream(privateKeyOctets.getOctetStream()); + ASN1Primitive privateKeyObject = asn1PrivateKeyStream.readObject(); + asn1PrivateKeyStream.close(); + ASN1Sequence privateKeySequence = (ASN1Sequence)privateKeyObject; + + int I=0; + ASN1Integer + s = (ASN1Integer)privateKeySequence.getObjectAt(I++), + n = (ASN1Integer)privateKeySequence.getObjectAt(I++), + e = (ASN1Integer)privateKeySequence.getObjectAt(I++), + d = (ASN1Integer)privateKeySequence.getObjectAt(I++), + p = (ASN1Integer)privateKeySequence.getObjectAt(I++), + q = (ASN1Integer)privateKeySequence.getObjectAt(I++), + d1 = (ASN1Integer)privateKeySequence.getObjectAt(I++), + d2 = (ASN1Integer)privateKeySequence.getObjectAt(I++), + c = (ASN1Integer)privateKeySequence.getObjectAt(I++) + ; + + /* + org.bouncycastle.asn1.pkcs.RSAPrivateKey.RSAPrivateKey( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger prime1, + BigInteger prime2, + BigInteger exponent1, + BigInteger exponent2, + BigInteger coefficient + ) + */ + this.privateKey = + new RSAPrivateKey( + n.getValue(), + e.getValue(), + d.getValue(), + p.getValue(), + q.getValue(), + d1.getValue(), + d2.getValue(), + c.getValue() + ); + } + + if (publicKey != null) + { + ASN1InputStream asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(publicKey)); + ASN1Primitive keyObject = asn1InputStream.readObject(); + asn1InputStream.close(); + + ASN1Sequence keySequence = (ASN1Sequence)keyObject; + ASN1Sequence algorithmSequence = (ASN1Sequence) keySequence.getObjectAt(0); + ASN1ObjectIdentifier algorithmIdentifier = (ASN1ObjectIdentifier) algorithmSequence.getObjectAt(0); + + String algorithm = algorithmIdentifier.getId(); + if (!algorithm.equals("1.2.840.113549.1.1.1")) + throw new CryptoException("Unknown RSA algorithm"); + + DERBitString keyOctets = (DERBitString) keySequence.getObjectAt(1); + ASN1InputStream asn1PublicKeyStream = new ASN1InputStream(new ByteArrayInputStream(keyOctets.getBytes())); + ASN1Primitive publicKeyObject = asn1PublicKeyStream.readObject(); + asn1PublicKeyStream.close(); + + ASN1Sequence publicKeySequence = (ASN1Sequence)publicKeyObject; + + int I=0; + ASN1Integer + n = (ASN1Integer)publicKeySequence.getObjectAt(I++), + e = (ASN1Integer)publicKeySequence.getObjectAt(I++); + + this.publicKey = new RSAPublicKey(n.getValue(), e.getValue()); + } + } + catch (IOException e) + { + throw new CryptoException(e); + } + } + + public byte[] encrypt (byte[] block) throws CryptoException + { + try + { + PKCS1Encoding e = new PKCS1Encoding(new RSAEngine()); + RSAKeyParameters key = new RSAKeyParameters(false, publicKey.getModulus(), publicKey.getPublicExponent()); + e.init(true, key); + + return e.processBlock(block, 0, block.length); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + + public byte[] decrypt (byte[] block) throws CryptoException + { + try + { + PKCS1Encoding e = new PKCS1Encoding(new RSAEngine()); + RSAKeyParameters key = new RSAKeyParameters(false, privateKey.getModulus(), privateKey.getPrivateExponent()); + e.init(false, key); + + return e.processBlock(block, 0, block.length); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } +} diff --git a/java/core/src/core/crypt/CryptorRSAFactory.java b/java/core/src/core/crypt/CryptorRSAFactory.java new file mode 100644 index 0000000..bd7efcb --- /dev/null +++ b/java/core/src/core/crypt/CryptorRSAFactory.java @@ -0,0 +1,78 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.bc.crypto.util.PrivateKeyFactory; +import org.bc.crypto.util.PublicKeyFactory; + +import core.exceptions.CryptoException; +import core.callback.Callback; +import core.util.Base64; +import core.util.Pair; + +public class CryptorRSAFactory +{ + public Pair generate (int bits) throws CryptoException + { + try + { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(bits); + KeyPair keyPair = keyPairGenerator.genKeyPair(); + byte[] privateKey = keyPair.getPrivate().getEncoded(); + byte[] publicKey = keyPair.getPublic().getEncoded(); + + return new Pair(publicKey, privateKey); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + + public void generate (int bits, Callback callback) + { + try + { + callback.invoke(generate(bits)); + } + catch (CryptoException e) + { + callback.invoke(e); + } + } + + public static CryptorRSA fromResources(InputStream publicKey, InputStream privateKey) throws Exception + { + return new CryptorRSAJCE(publicKey, privateKey); + } + + public static CryptorRSA fromString(String publicKey, String privateKey) throws Exception + { + return + new CryptorRSAJCE( + publicKey != null ? + KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec( + Base64.decode(publicKey)) + ) : + null, + privateKey != null ? + KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec( + Base64.decode(privateKey)) + ) : + null + ); + } + +} diff --git a/java/core/src/core/crypt/CryptorRSAFactoryEnvironment.java b/java/core/src/core/crypt/CryptorRSAFactoryEnvironment.java new file mode 100644 index 0000000..20a6029 --- /dev/null +++ b/java/core/src/core/crypt/CryptorRSAFactoryEnvironment.java @@ -0,0 +1,83 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.bc.util.encoders.Base64; + +import core.constants.ConstantsEnvironmentKeys; +import core.exceptions.CryptoException; +import core.util.Environment; + +public class CryptorRSAFactoryEnvironment +{ + public static CryptorRSA createJCE (Environment env) throws CryptoException + { + try + { + PublicKey publicKey = null; + PrivateKey privateKey = null; + + String publicKeyString = env.get(ConstantsEnvironmentKeys.PUBLIC_ENCRYPTION_KEY); + if (publicKeyString != null) + { + publicKey = + KeyFactory.getInstance("RSA").generatePublic( + new X509EncodedKeySpec((byte[])Base64.decode(publicKeyString)) + ); + } + String privateKeyString = env.get(ConstantsEnvironmentKeys.PRIVATE_DECRYPTION_KEY); + if (privateKeyString != null) + { + privateKey = + KeyFactory.getInstance("RSA").generatePrivate( + new PKCS8EncodedKeySpec((byte[])Base64.decode(privateKeyString)) + ); + } + + return new CryptorRSAJCE (publicKey, privateKey); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } +/* + public static CryptorRSA createBC (Environment env) throws CryptoException + { + try + { + Base64 base64 = new Base64(); + + String publicKeyString = env.get(ConstantsEnvironmentKeys.PUBLIC_ENCRYPTION_KEY); + byte[] publicKeyBytes = null; + if (publicKeyString != null) + publicKeyBytes = (byte[])base64.decode(publicKeyString); + + String privateKeyString = env.get(ConstantsEnvironmentKeys.PRIVATE_DECRYPTION_KEY); + byte[] privateKeyBytes = null; + if (privateKeyString != null) + privateKeyBytes = base64.decode(privateKeyString); + + return new CryptorRSABC (publicKeyBytes, privateKeyBytes); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } +*/ + public static CryptorRSA create (Environment env) throws CryptoException + { + return createJCE(env); + } +} diff --git a/java/core/src/core/crypt/CryptorRSAJCE.java b/java/core/src/core/crypt/CryptorRSAJCE.java new file mode 100644 index 0000000..d6e6780 --- /dev/null +++ b/java/core/src/core/crypt/CryptorRSAJCE.java @@ -0,0 +1,91 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import java.io.InputStream; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import javax.crypto.Cipher; +import core.exceptions.CryptoException; + +public class CryptorRSAJCE extends CryptorRSA +{ + SecureRandom random = new SecureRandom(); + + public final int MAX_RSA_BLOCK_SIZE = 117; + + PublicKey publicKey; + PrivateKey privateKey; + + public CryptorRSAJCE (InputStream keyStore, InputStream trustStore) throws Exception + { + if (keyStore != null) + { + KeyStore kks = KeyStore.getInstance("JKS"); + kks.load(keyStore, "password".toCharArray()); + privateKey = (PrivateKey)kks.getKey(kks.aliases().nextElement(), "password".toCharArray()); + + if (privateKey == null) + throw new CryptoException(new Exception("RSA Private Key not found in KeyStore.")); + } + + if (trustStore != null) + { + KeyStore tks = KeyStore.getInstance("JKS"); + tks.load(trustStore, "password".toCharArray()); + publicKey = tks.getCertificate(tks.aliases().nextElement()).getPublicKey(); + + if (publicKey == null) + throw new CryptoException(new Exception("RSA Public Key not found in KeyStore.")); + } + + initialize( + publicKey!=null ? publicKey.getEncoded() : null, + privateKey!=null ? privateKey.getEncoded() : null + ); + } + + public CryptorRSAJCE (PublicKey publicKey, PrivateKey privateKey) + { + this.publicKey = publicKey; + this.privateKey = privateKey; + + initialize( + publicKey!=null ? publicKey.getEncoded() : null, + privateKey!=null ? privateKey.getEncoded() : null + ); + } + + public byte[] encrypt (byte[] block) throws CryptoException + { + try + { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(block, 0, block.length); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + + public byte[] decrypt (byte[] block) throws CryptoException + { + try + { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(block, 0, block.length); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } +} diff --git a/java/core/src/core/crypt/CryptorSeed.java b/java/core/src/core/crypt/CryptorSeed.java new file mode 100644 index 0000000..9fdcf06 --- /dev/null +++ b/java/core/src/core/crypt/CryptorSeed.java @@ -0,0 +1,16 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +public class CryptorSeed +{ + public byte[] seed; + + public CryptorSeed(byte[] seed) + { + this.seed = seed; + } +} diff --git a/java/core/src/core/crypt/HashMd5.java b/java/core/src/core/crypt/HashMd5.java new file mode 100644 index 0000000..31d896f --- /dev/null +++ b/java/core/src/core/crypt/HashMd5.java @@ -0,0 +1,26 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import org.bc.crypto.digests.MD5Digest; + +public class HashMd5 +{ + public HashMd5 () + { + + } + + public byte[] hash (byte[] bytes) + { + MD5Digest digest = new MD5Digest(); + + byte[] out = new byte[digest.getDigestSize()]; + digest.update(bytes, 0, bytes.length); + digest.doFinal(out, 0); + return out; + } +} diff --git a/java/core/src/core/crypt/HashSha256.java b/java/core/src/core/crypt/HashSha256.java new file mode 100644 index 0000000..4a09f13 --- /dev/null +++ b/java/core/src/core/crypt/HashSha256.java @@ -0,0 +1,26 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import org.bc.crypto.digests.SHA256Digest; + +public class HashSha256 +{ + public HashSha256 () + { + + } + + public byte[] hash (byte[] bytes) + { + SHA256Digest digest = new SHA256Digest(); + + byte[] out = new byte[digest.getDigestSize()]; + digest.update(bytes, 0, bytes.length); + digest.doFinal(out, 0); + return out; + } +} diff --git a/java/core/src/core/crypt/HmacSha1.java b/java/core/src/core/crypt/HmacSha1.java new file mode 100644 index 0000000..18deb9c --- /dev/null +++ b/java/core/src/core/crypt/HmacSha1.java @@ -0,0 +1,32 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import org.bc.crypto.digests.SHA1Digest; +import org.bc.crypto.macs.HMac; +import org.bc.crypto.params.KeyParameter; + +public class HmacSha1 +{ + HMac mac; + byte[] key; + + public HmacSha1(byte[] key) + { + mac = new HMac(new SHA1Digest()); + mac.init(new KeyParameter(key)); + } + + public byte[] mac(byte[] bytes) + { + mac.update(bytes, 0, bytes.length); + byte[] out = new byte[mac.getMacSize()]; + mac.doFinal(out, 0); + + return out; + } + +} diff --git a/java/core/src/core/crypt/KeyPairFromPassword.java b/java/core/src/core/crypt/KeyPairFromPassword.java new file mode 100644 index 0000000..d4e41d9 --- /dev/null +++ b/java/core/src/core/crypt/KeyPairFromPassword.java @@ -0,0 +1,75 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.callback.Callback; +import core.callback.CallbackChain; +import core.callback.CallbackDefault; +import core.exceptions.CryptoException; + +public class KeyPairFromPassword +{ + String password; + public PBE[] pbe = { new PBE(), new PBE() }; + + static final int CRYPTOR = 0, VERIFIER = 1; + + public KeyPairFromPassword (String password) + { + this.password = password; + } + + public Callback generate_() + { + CallbackChain chain = new CallbackChain(); + + return chain + .addCallback( + pbe[CRYPTOR].generate_( + password, + PBE.DEFAULT_SALT[CRYPTOR], + PBE.DEFAULT_ITERATIONS, + PBE.DEFAULT_KEYLENGTH + ) + ) + .addCallback( + pbe[VERIFIER].generate_( + password, + PBE.DEFAULT_SALT[VERIFIER], + PBE.DEFAULT_ITERATIONS, + PBE.DEFAULT_KEYLENGTH + ) + ) + .addCallback( + new CallbackDefault() { + public void onSuccess(Object... arguments) throws Exception { + next(KeyPairFromPassword.this); + } + } + ); + } + + public void generate() throws CryptoException + { + for (int i=0; i<2; ++i) + pbe[i].generate( + password, + PBE.DEFAULT_SALT[i], + PBE.DEFAULT_ITERATIONS, + PBE.DEFAULT_KEYLENGTH + ); + } + + public byte[] getVerifier () + { + return pbe[VERIFIER].key; + } + + public PBE getCryptor () + { + return pbe[CRYPTOR]; + } +} diff --git a/java/core/src/core/crypt/KeyPairFromPasswordCryptor.java b/java/core/src/core/crypt/KeyPairFromPasswordCryptor.java new file mode 100644 index 0000000..1e16f08 --- /dev/null +++ b/java/core/src/core/crypt/KeyPairFromPasswordCryptor.java @@ -0,0 +1,41 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.callback.Callback; +import core.exceptions.CryptoException; + +public class KeyPairFromPasswordCryptor extends Cryptor +{ + KeyPairFromPassword keyPair; + + public KeyPairFromPasswordCryptor (KeyPairFromPassword keyPair) + { + this.keyPair = keyPair; + } + + public byte[] encrypt (byte[] clearBlock) throws CryptoException + { + return keyPair.getCryptor().encrypt(clearBlock); + } + + public byte[] decrypt (byte[] encryptedBlock) throws CryptoException + { + return keyPair.getCryptor().decrypt(encryptedBlock); + } + + @Override + public Callback encrypt_() + { + return keyPair.getCryptor().encrypt_(); + } + + @Override + public Callback decrypt_() + { + return keyPair.getCryptor().decrypt_(); + } +} diff --git a/java/core/src/core/crypt/PBE.java b/java/core/src/core/crypt/PBE.java new file mode 100644 index 0000000..2913a8d --- /dev/null +++ b/java/core/src/core/crypt/PBE.java @@ -0,0 +1,105 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import org.bc.crypto.PBEParametersGenerator; +import org.bc.crypto.digests.SHA256Digest; +import org.bc.crypto.generators.PKCS5S2ParametersGenerator; +import org.bc.crypto.params.KeyParameter; + +import core.callback.Callback; +import core.callback.CallbackDefault; +import core.crypt.Cryptor; +import core.crypt.CryptorAES; +import core.exceptions.CryptoException; +import core.util.Arrays; +import core.util.Base64; +import core.util.LogNull; +import core.util.LogOut; + +/** + * http://stackoverflow.com/questions/992019/java-256-bit-aes-password-based-encryption + */ +public class PBE extends Cryptor +{ + LogNull log = new LogNull(PBE.class); + + public byte[] key = null; + Cryptor cryptorAES; + + public static final int DEFAULT_ITERATIONS = 131072; + public static final int SHORT_ITERATIONS = 32768; + public static final int DEFAULT_KEYLENGTH = 256; + + /** + * bytes used to salt the key (set before making an instance) + */ + public static final byte[] DEFAULT_SALT_0 = { + (byte)0xc8, (byte)0x73, (byte)0x41, (byte)0x8c, + (byte)0x7e, (byte)0xd8, (byte)0xee, (byte)0x89 + }; + + /** + * bytes used to salt the key (set before making an instance) + */ + public static final byte[] DEFAULT_SALT_1 = { + (byte)0x12, (byte)0x53, (byte)0x14, (byte)0xbb, + (byte)0x7e, (byte)0x97, (byte)0xce, (byte)0x55 + }; + + /** + * bytes used to salt the key (set before making an instance) + */ + public static final byte[] DEFAULT_SALT_2 = { + (byte)0x0a, (byte)0x48, (byte)0x33, (byte)0xfe, + (byte)0xa7, (byte)0xc2, (byte)0x2c, (byte)0x71 + }; + + public static final byte[][] DEFAULT_SALT = { DEFAULT_SALT_0, DEFAULT_SALT_1, DEFAULT_SALT_2 }; + + public PBE () + { + + } + public PBE (String password, byte[] salt, int iterationCount, int keyLength) throws CryptoException + { + generate(password,salt, iterationCount, keyLength); + } + + public void generate (String password, byte[] salt, int iterationCount, int keyLength) + throws CryptoException + { + key = PBEPlatform.generate(password, salt, iterationCount, keyLength); + cryptorAES = new CryptorAES (key); + + log.debug("computed", new String(Base64.encode(key)), " using ", new String(password),"salt",new String(Base64.encode(salt)), iterationCount, keyLength); + } + + public Callback generate_ (String password, byte[] salt, int iterationCount, int keyLength) + { + return + new CallbackDefault(password, salt, iterationCount, keyLength) { + public void onSuccess(Object... arguments) throws Exception { + String password = V(0); + byte[] salt = V(1); + int iterationCount = (Integer)V(2); + int keyLength = (Integer)V(3); + + generate(password, salt, iterationCount, keyLength); + } + }; + } + + public byte[] encrypt(byte[] clearText) throws CryptoException + { + return cryptorAES.encrypt(clearText); + } + + public byte[] decrypt(byte[] encrypted) throws CryptoException + { + return cryptorAES.decrypt(encrypted); + } +} diff --git a/java/core/src/core/crypt/PBEPlatform.java b/java/core/src/core/crypt/PBEPlatform.java new file mode 100644 index 0000000..7b5535b --- /dev/null +++ b/java/core/src/core/crypt/PBEPlatform.java @@ -0,0 +1,16 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.exceptions.CryptoException; + +public class PBEPlatform +{ + static byte[] generate (String password, byte[] salt, int iterationCount, int keyLength) throws CryptoException + { + return PBEPlatformBC.generate(password, salt, iterationCount, keyLength); + } +} diff --git a/java/core/src/core/crypt/PBEPlatformBC.java b/java/core/src/core/crypt/PBEPlatformBC.java new file mode 100644 index 0000000..666825a --- /dev/null +++ b/java/core/src/core/crypt/PBEPlatformBC.java @@ -0,0 +1,29 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import org.bc.crypto.PBEParametersGenerator; +import org.bc.crypto.digests.SHA256Digest; +import org.bc.crypto.generators.PKCS5S2ParametersGenerator; +import org.bc.crypto.params.KeyParameter; + +import core.exceptions.CryptoException; + +public class PBEPlatformBC +{ + public static byte[] generate (String password, byte[] salt, int iterationCount, int keyLength) + throws CryptoException + { + PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA256Digest()); + generator.init( + PBEParametersGenerator.PKCS5PasswordToBytes(password.toCharArray()), + salt, + iterationCount + ); + + return ((KeyParameter)generator.generateDerivedParameters(keyLength)).getKey(); + } +} diff --git a/java/core/src/core/crypt/PBEPlatformNative.java b/java/core/src/core/crypt/PBEPlatformNative.java new file mode 100644 index 0000000..d811ce7 --- /dev/null +++ b/java/core/src/core/crypt/PBEPlatformNative.java @@ -0,0 +1,13 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import core.exceptions.CryptoException; + +public class PBEPlatformNative +{ + public native static byte[] generate (String password, byte[] salt, int iterationCount, int keyLength); +} diff --git a/java/core/src/core/crypt/PasswordValidator.java b/java/core/src/core/crypt/PasswordValidator.java new file mode 100644 index 0000000..bf9af6b --- /dev/null +++ b/java/core/src/core/crypt/PasswordValidator.java @@ -0,0 +1,30 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.crypt; + +import java.io.Serializable; + + +public class PasswordValidator implements Serializable +{ + private static final long serialVersionUID = 1L; + + public String user; + public String password; + byte[] payload; + + public PasswordValidator (String user, String password, byte[] payload) + { + this.user = user; + this.password = password; + this.payload = payload; + } + + public PasswordValidator (String user, String password) + { + this(user, password, null); + } +} diff --git a/java/core/src/core/exceptions/CryptoException.java b/java/core/src/core/exceptions/CryptoException.java new file mode 100644 index 0000000..dedcd22 --- /dev/null +++ b/java/core/src/core/exceptions/CryptoException.java @@ -0,0 +1,19 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.exceptions; + +public class CryptoException extends Exception +{ + public CryptoException (Exception e) + { + super(e); + } + + public CryptoException(String s) + { + super(s); + } +} diff --git a/java/core/src/core/exceptions/InternalException.java b/java/core/src/core/exceptions/InternalException.java new file mode 100644 index 0000000..9f14a80 --- /dev/null +++ b/java/core/src/core/exceptions/InternalException.java @@ -0,0 +1,23 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.exceptions; + +import org.timepedia.exporter.client.Export; +import org.timepedia.exporter.client.Exportable; + +@Export +public class InternalException extends PublicMessageException implements Exportable +{ + public InternalException (Exception e) + { + super("An internal error has occurred with " + e.getClass().getName(), e); + } + + public InternalException (String with, Exception e) + { + super("An internal error has occurred with " + with, e); + } +} diff --git a/java/core/src/core/exceptions/PublicMessageException.java b/java/core/src/core/exceptions/PublicMessageException.java new file mode 100644 index 0000000..c0855e9 --- /dev/null +++ b/java/core/src/core/exceptions/PublicMessageException.java @@ -0,0 +1,32 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.exceptions; + +import org.timepedia.exporter.client.Export; +import org.timepedia.exporter.client.Exportable; + +@Export +public class PublicMessageException extends RuntimeException implements Exportable +{ + public String message; + + public PublicMessageException (String message) + { + this.message = message; + } + + public PublicMessageException (String message, Exception e) + { + super(e); + this.message = message; + } + + @Override + public String getMessage () + { + return message; + } +} diff --git a/java/core/src/core/exceptions/UserExistsException.java b/java/core/src/core/exceptions/UserExistsException.java new file mode 100644 index 0000000..fe59f11 --- /dev/null +++ b/java/core/src/core/exceptions/UserExistsException.java @@ -0,0 +1,16 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.exceptions; + +import java.io.IOException; + +public class UserExistsException extends PublicMessageException +{ + public UserExistsException () + { + super("User already exists"); + } +} diff --git a/java/core/src/core/io/IoChain.java b/java/core/src/core/io/IoChain.java new file mode 100644 index 0000000..2850ef0 --- /dev/null +++ b/java/core/src/core/io/IoChain.java @@ -0,0 +1,98 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +public abstract class IoChain +{ + protected boolean finished = false; + protected IoChain sender, receiver; + + public IoChain (IoChain sender) + { + if (sender != null) + setSender(sender); + } + + public void setSender (IoChain sender) + { + this.sender = sender; + sender.setReceiver (this); + } + + public void setReceiver (IoChain receiver) + { + this.receiver = receiver; + } + + public void send (byte[] packet) throws Exception + { + this.sender.send(packet); + } + + protected void onReceive (byte[] object) throws Exception + { + receiver.onReceive(object); + } + + public void receive(byte[] object) + { + try + { + onReceive(object); + } + catch (Exception e) + { + onException(e); + } + } + + public void onException (Exception e) + { + if (receiver != null) + receiver.onException(e); + } + + public IoChain getFinalSender () + { + if (sender != null) + return sender.getFinalSender(); + + return this; + } + + public void open () throws Exception + { + if (receiver != null) + receiver.open(); + } + + protected void close () throws Exception + { + if (sender != null) + sender.close(); + } + + public void stop () throws Exception + { + if (receiver != null) + receiver.stop(); + else + close(); + } + + public void run () throws Exception + { + if (sender != null) + sender.run(); + else + open(); + } + + public boolean isFinished () + { + return finished; + } +} diff --git a/java/core/src/core/io/IoChainAccumulator.java b/java/core/src/core/io/IoChainAccumulator.java new file mode 100644 index 0000000..5fed6a9 --- /dev/null +++ b/java/core/src/core/io/IoChainAccumulator.java @@ -0,0 +1,83 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +import java.util.ArrayList; +import java.util.List; + +import core.util.LogNull; +import core.util.LogOut; + +public class IoChainAccumulator extends IoChain +{ + LogNull log = new LogNull(IoChainAccumulator.class); + List out; + boolean opened, closed; + Exception exception; + + public IoChainAccumulator() + { + super(null); + out = new ArrayList(); + } + + @Override + public void send(byte[] packet) throws Exception + { + log.debug("accumulating packet"); + out.add(packet); + } + + @Override + protected void onReceive(byte[] object) throws Exception + { + super.onReceive(object); + } + + @Override + public void open () throws Exception + { + opened = true; + super.open(); + } + + @Override + protected void close () throws Exception + { + closed = true; + super.close(); + } + + public void onException(Exception e) + { + this.exception = e; + super.onException(e); + } + + public List getAndClearPackets() + { + List result = out; + out = new ArrayList(); + return result; + } + + public Exception getAndClearException () + { + Exception e = this.exception; + this.exception = null; + return e; + } + + public boolean isOpened () + { + return opened; + } + + public boolean isClosed () + { + return closed; + } +} diff --git a/java/core/src/core/io/IoChainBase64.java b/java/core/src/core/io/IoChainBase64.java new file mode 100644 index 0000000..1e97467 --- /dev/null +++ b/java/core/src/core/io/IoChainBase64.java @@ -0,0 +1,41 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +import java.io.IOException; + +import core.util.Base64; + +import core.util.LogNull; +import core.util.LogOut; + +public class IoChainBase64 extends IoChain +{ + static LogNull log = new LogNull(IoChainBase64.class); + + public IoChainBase64(IoChain sender) + { + super(sender); + } + + @Override + public void send(byte[] packet) throws Exception + { + byte[] encoded = Base64.encodeBytes(packet); + // log.debug("send base64 check:", Strings.toString(Base64.decode(encoded))); + + super.send(encoded); + } + + @Override + protected void onReceive (byte[] packet) throws Exception + { + byte[] decoded = Base64.decodeBytes(packet); + // log.debug("onReceive:",new String(packet)); + + super.onReceive(decoded); + } +} diff --git a/java/core/src/core/io/IoChainFinishedException.java b/java/core/src/core/io/IoChainFinishedException.java new file mode 100644 index 0000000..e8609a4 --- /dev/null +++ b/java/core/src/core/io/IoChainFinishedException.java @@ -0,0 +1,10 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +public class IoChainFinishedException extends Exception { + +} diff --git a/java/core/src/core/io/IoChainNewLinePackets.java b/java/core/src/core/io/IoChainNewLinePackets.java new file mode 100644 index 0000000..51b4bf9 --- /dev/null +++ b/java/core/src/core/io/IoChainNewLinePackets.java @@ -0,0 +1,65 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; + +import core.util.LogNull; +import core.util.Strings; + +public class IoChainNewLinePackets extends IoChain +{ + LogNull log = new LogNull(IoChainNewLinePackets.class); + List packets = new ArrayList(); + ByteArrayOutputStream unfinished = new ByteArrayOutputStream(); + + public IoChainNewLinePackets(IoChain sender) + { + super(sender); + } + + @Override + protected void onReceive(byte[] bytes) throws Exception + { + for (byte b: bytes) + { + if (b == '\n' || b == '\r') + { + log.debug("found newline"); + if (unfinished.size()>0) + { + packets.add(unfinished.toByteArray()); + unfinished = new ByteArrayOutputStream(); + } + } + else + { + unfinished.write(b); + } + } + + while (!packets.isEmpty()) + { + log.debug("recieving packet"); + + byte[] packet = packets.get(0); + packets.remove(0); + super.onReceive(packet); + } + } + + @Override + public void send (byte[] bytes) throws Exception + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bos.write(bytes); + bos.write(Strings.toBytes("\n")); + + super.send(bos.toByteArray()); + } +} diff --git a/java/core/src/core/io/IoChainSSLSocket.java b/java/core/src/core/io/IoChainSSLSocket.java new file mode 100644 index 0000000..ec1d22b --- /dev/null +++ b/java/core/src/core/io/IoChainSSLSocket.java @@ -0,0 +1,32 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +public class IoChainSSLSocket extends IoChainSocket +{ + public IoChainSSLSocket(SSLContext context, String host, int port) throws Exception + { + useSocket(createSSLSocket(context, host, port)); + } + + public Socket createSSLSocket (SSLContext context, String host, int port) throws UnknownHostException, IOException + { + SSLSocketFactory sslsocketfactory = context.getSocketFactory(); + SSLSocket socket = (SSLSocket) sslsocketfactory.createSocket(host, port); + socket.setUseClientMode(true); + + return socket; + } + +} diff --git a/java/core/src/core/io/IoChainSizedPackets.java b/java/core/src/core/io/IoChainSizedPackets.java new file mode 100644 index 0000000..7a4f4a8 --- /dev/null +++ b/java/core/src/core/io/IoChainSizedPackets.java @@ -0,0 +1,87 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import core.util.CircularByteBuffer; + +public class IoChainSizedPackets extends IoChain +{ + DataInputStream in; + CircularByteBuffer internalIn; + + Integer nextPacket; + + public IoChainSizedPackets (IoChain sender) throws IOException + { + super(sender); + + internalIn = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE); + this.in = new DataInputStream(internalIn.getInputStream()); + } + + public boolean ready () throws IOException + { + if (nextPacket == null) + { + if (in.available() >= Integer.SIZE/8) + nextPacket = in.readInt(); + } + + if (nextPacket != null) + return in.available() >= nextPacket; + + return false; + } + + @Override + protected void onReceive(byte[] bytes) throws Exception + { + internalIn.getOutputStream().write(bytes); + + while (ready()) + read(); + } + + @Override + public void send(byte[] bytes) throws Exception + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + int size = bytes.length; + dos.writeInt(size); + dos.write(bytes); + dos.flush(); + + super.send(bos.toByteArray()); + } + + public void read () throws Exception + { + if (!ready()) + throw new IOException(); + + byte[] buf = new byte[nextPacket]; + if (in.read(buf, 0, nextPacket) != nextPacket) + throw new IOException (); + + nextPacket = null; + + super.onReceive(buf); + } + + public boolean eof () throws IOException + { + return false; + } +} diff --git a/java/core/src/core/io/IoChainSocket.java b/java/core/src/core/io/IoChainSocket.java new file mode 100644 index 0000000..d3619ef --- /dev/null +++ b/java/core/src/core/io/IoChainSocket.java @@ -0,0 +1,111 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +import core.util.CircularByteBuffer; + +public class IoChainSocket extends IoChain implements Runnable +{ + String host; + int port; + Socket socket; + + InputStream in; + OutputStream out; + + protected IoChainSocket() throws Exception + { + super(null); + } + + protected void useSocket (Socket socket) throws Exception + { + this.socket = socket; + + in = socket.getInputStream(); + out = socket.getOutputStream(); + } + + public IoChainSocket(String host, int port) + { + super(null); + + this.host = host; + this.port = port; + } + + @Override + public void open () throws Exception + { + if (socket == null) + useSocket(new Socket(host, port)); + + super.open(); + } + + @Override + protected void close () throws Exception + { + if (socket != null) + { + socket.close(); + socket = null; + } + } + + public boolean pumpBlock () throws Exception + { + CircularByteBuffer buffer = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE); + + boolean first = true; + while (socket!= null && (first || in.available()>0)) + { + first = false; + int c = in.read(); + + if (c == -1) + throw new IoChainFinishedException(); + + buffer.getOutputStream().write(c); + } + + if (buffer.getAvailable() > 0) + { + byte[] b = new byte[buffer.getAvailable()]; + buffer.getInputStream().read(b); + + receive(b); + } + + return socket != null; + } + + @Override + public void send(byte[] bytes) throws IOException + { + out.write(bytes); + } + + @Override + public void run () + { + try + { + open(); + + while (pumpBlock()) ; + } + catch (Exception e) + { + onException(e); + } + } +} diff --git a/java/core/src/core/io/IoChainThread.java b/java/core/src/core/io/IoChainThread.java new file mode 100644 index 0000000..df4da8f --- /dev/null +++ b/java/core/src/core/io/IoChainThread.java @@ -0,0 +1,29 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +public class IoChainThread extends Thread +{ + IoChain session; + + public IoChainThread (IoChain session) + { + this.session = session; + } + + @Override + public void run() + { + try + { + ((IoChain)session).run(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/java/core/src/core/io/SSLContextGenerator.java b/java/core/src/core/io/SSLContextGenerator.java new file mode 100644 index 0000000..3b88fad --- /dev/null +++ b/java/core/src/core/io/SSLContextGenerator.java @@ -0,0 +1,47 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.io; + +import java.io.InputStream; +import java.security.KeyStore; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +public class SSLContextGenerator +{ + static public SSLContext getSslContext(InputStream clientKeyStore, InputStream serverKeyStore) + { + SSLContext sslContext = null; + + try + { + KeyStore cks = KeyStore.getInstance("JKS"); + cks.load(clientKeyStore, "password".toCharArray()); + + KeyStore sks = KeyStore.getInstance("JKS"); + sks.load(serverKeyStore, "password".toCharArray()); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(cks, "password".toCharArray()); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509", "SunJSSE"); + tmf.init(sks); + + // this might need to be SSL not TLS + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + return sslContext; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return sslContext; + } +} \ No newline at end of file diff --git a/java/core/src/core/server/captcha/Captcha.java b/java/core/src/core/server/captcha/Captcha.java new file mode 100644 index 0000000..839952a --- /dev/null +++ b/java/core/src/core/server/captcha/Captcha.java @@ -0,0 +1,146 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.server.captcha; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Random; + +import core.server.captcha.sql.Catalog; +import core.util.Passwords; + + +public class Captcha +{ + Random random = new SecureRandom(); + Catalog catalog = new Catalog(); + + public static final String + SignUp = "SignUp", + CreateBucket = "CreateBucket"; + + public void prune () throws SQLException, IOException + { + Connection connection = openConnection(); + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(Catalog.PRUNE_TOKENS)); + log(statement); + + statement.executeUpdate(); + } + finally + { + closeConnection(connection); + } + } + + protected void insertCaptchToken (String token, String use) throws SQLException, IOException + { + Connection connection = openConnection(); + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(Catalog.ADD_TOKEN)); + statement.setString(1, token + "_" + use); + log(statement); + statement.executeUpdate(); + } + finally + { + closeConnection(connection); + } + } + + public String captchaSucceeded () throws SQLException, IOException + { + String token = BigInteger.valueOf(Math.abs(random.nextLong())).toString(32); + insertCaptchToken (token, SignUp); + insertCaptchToken (token, CreateBucket); + + return token; + } + + public void useToken (String token, String use) throws SQLException, IOException + { + prune(); + + Connection connection = openConnection(); + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(Catalog.CHECK_TOKEN)); + statement.setString(1, token + "_" + use); + log(statement); + + ResultSet rs = statement.executeQuery(); + if (!rs.next()) + throw new IOException ("Null captcha"); + rs.close(); + + statement = connection.prepareStatement (catalog.getSingle(Catalog.USE_TOKEN)); + statement.setString(1, token + "_" + use); + log(statement); + + statement.executeUpdate(); + } + finally + { + closeConnection(connection); + } + } + + public void ensureTables() throws SQLException, IOException + { + Connection connection = openConnection(); + + try + { + for (String sql : catalog.getMulti(Catalog.CREATE_TABLES)) + { + PreparedStatement statement = connection.prepareStatement (sql); + log(statement); + statement.executeUpdate(); + } + } + finally + { + closeConnection(connection); + } + } + + public Connection openConnection () throws SQLException, IOException + { + return DriverManager.getConnection(Catalog.CONNECTION_STRING, Catalog.USER, Passwords.getPasswordFor(Catalog.USER)); + } + + public void closeConnection (Connection connection) + { + try + { + if (connection != null) + connection.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + public void log (Statement sql) + { + System.out.println (sql); + } +} diff --git a/java/core/src/core/server/captcha/sql/Catalog.java b/java/core/src/core/server/captcha/sql/Catalog.java new file mode 100644 index 0000000..c8e8fcc --- /dev/null +++ b/java/core/src/core/server/captcha/sql/Catalog.java @@ -0,0 +1,22 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.server.captcha.sql; + +import core.constants.ConstantsServer; +import core.util.SqlCatalog; + + +public final class Catalog extends SqlCatalog +{ + static public final String CONNECTION_STRING = ConstantsServer.DBCONNECTION_PREFIX + "captcha"; + public static final String USER = "captcha"; + + static public final String CREATE_TABLES = "create_tables"; + static public final String USE_TOKEN = "use_token"; + static public final String CHECK_TOKEN = "check_token"; + static public final String ADD_TOKEN = "add_token"; + static public final String PRUNE_TOKENS = "prune_tokens"; +} diff --git a/java/core/src/core/server/captcha/sql/add_token b/java/core/src/core/server/captcha/sql/add_token new file mode 100644 index 0000000..1954623 --- /dev/null +++ b/java/core/src/core/server/captcha/sql/add_token @@ -0,0 +1 @@ +INSERT INTO captcha (token) values (?) \ No newline at end of file diff --git a/java/core/src/core/server/captcha/sql/check_token b/java/core/src/core/server/captcha/sql/check_token new file mode 100644 index 0000000..4100394 --- /dev/null +++ b/java/core/src/core/server/captcha/sql/check_token @@ -0,0 +1 @@ +SELECT true FROM captcha WHERE token=? \ No newline at end of file diff --git a/java/core/src/core/server/captcha/sql/create_tables b/java/core/src/core/server/captcha/sql/create_tables new file mode 100644 index 0000000..c604476 --- /dev/null +++ b/java/core/src/core/server/captcha/sql/create_tables @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS captcha ( + token VARCHAR(255), + mark TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (token) +) \ No newline at end of file diff --git a/java/core/src/core/server/captcha/sql/prune_tokens b/java/core/src/core/server/captcha/sql/prune_tokens new file mode 100644 index 0000000..0bba9a4 --- /dev/null +++ b/java/core/src/core/server/captcha/sql/prune_tokens @@ -0,0 +1 @@ +DELETE FROM captcha WHERE mark < DATE_SUB(now(), INTERVAL 2 HOUR); \ No newline at end of file diff --git a/java/core/src/core/server/captcha/sql/use_token b/java/core/src/core/server/captcha/sql/use_token new file mode 100644 index 0000000..0991a32 --- /dev/null +++ b/java/core/src/core/server/captcha/sql/use_token @@ -0,0 +1 @@ +DELETE FROM captcha WHERE token=? diff --git a/java/core/src/core/server/db/UserDb.java b/java/core/src/core/server/db/UserDb.java new file mode 100644 index 0000000..97feee8 --- /dev/null +++ b/java/core/src/core/server/db/UserDb.java @@ -0,0 +1,479 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.server.srp.db; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.Date; + +import core.exceptions.CryptoException; +import core.exceptions.PublicMessageException; +import core.exceptions.UserExistsException; +import core.server.srp.db.sql.Catalog; +import core.util.LogOut; +import core.util.Pair; +import core.util.Passwords; +import core.util.Strings; +import core.util.Triple; +import core.util.Base64; + +public abstract class UserDb +{ + static LogOut log = new LogOut(UserDb.class); + + SecureRandom random = new SecureRandom(); + Catalog catalog; + + protected UserDb (Catalog catalog) + { + this.catalog = catalog; + } + + public void testCreateUser (String version, String userName) throws Exception + { + checkRoomForNewUser(); + testIllegalUserName(userName); + + if (getUser(userName)!=null) + throw new UserExistsException(); + } + + public void checkRoomForNewUser () throws Exception + { + Connection connection = openConnection(); + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.ROOM_FOR_NEW_USER)); + ResultSet results = statement.executeQuery(); + if (results.next()) + { + boolean hasRoom = results.getBoolean("room"); + if (hasRoom) + return; + } + + throw new PublicMessageException("No room for new users"); + } + finally + { + closeConnection(connection); + } + + } + + public Integer getUserId (String userName) throws IOException, SQLException + { + return getUser(userName).first; + } + + public void createUser(String version, String userName, byte[] v, byte[] s) throws Exception + { + checkRoomForNewUser(); + testIllegalUserName(userName); + + if (getUser(userName)!=null) + throw new UserExistsException(); + + Connection connection = openConnection(); + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.CREATE_USER)); + statement.setString(1, version); + statement.setString(2, userName); + statement.setString(3, Base64.encode(v)); + statement.setString(4, Base64.encode(s)); + log(statement); + + statement.executeUpdate(); + } + finally + { + closeConnection(connection); + } + } + + + public Pair > getUser (String userName) throws IOException, SQLException + { + Connection connection = openConnection(); + + Pair> result = null; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.GET_USER)); + statement.setString(1, userName); + log(statement); + + ResultSet results = statement.executeQuery(); + if (results.next()) + { + result = + new Pair >( + results.getInt("id"), + new Triple ( + results.getString("version"), + Base64.decode(results.getString("v")), + Base64.decode(results.getString("s")) + ) + ); + } + } + finally + { + closeConnection(connection); + } + + return result; + } + + public Triple getVVS (String userName) throws IOException, SQLException + { + Triple vvs = getUser(userName).second; + return + new Triple( + vvs.first, + new BigInteger (vvs.second), + new BigInteger (vvs.third) + ); + } + + protected byte[] setMailBlock (String userName, byte[] block) throws IOException, SQLException + { + Integer id = getUser(userName).first; + + Connection connection = openConnection(); + + byte[] result = null; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.SET_USER_MAIL_BLOCK)); + statement.setInt(1, id); + statement.setString (2, Base64.encode(block)); + log(statement); + + statement.executeUpdate(); + } + finally + { + closeConnection(connection); + } + + return result; + } + + abstract public byte[] setBlock (String userName, byte[] block) throws IOException, SQLException, CryptoException; + abstract public byte[] getBlock (String userName) throws IOException, SQLException, CryptoException; + + protected byte[] setKeyBlock (String userName, byte[] block) throws IOException, SQLException + { + Integer id = getUser(userName).first; + + Connection connection = openConnection(); + + byte[] result = null; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.SET_USER_KEY_BLOCK)); + statement.setInt(1, id); + statement.setString (2, Base64.encode(block)); + log(statement); + + statement.executeUpdate(); + } + finally + { + closeConnection(connection); + } + + return result; + } + + protected byte[] getMailBlock (String userName) throws IOException, SQLException + { + Connection connection = openConnection(); + + byte[] result = null; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.GET_USER_MAIL_BLOCK)); + statement.setString(1, userName); + log(statement); + + ResultSet results = statement.executeQuery(); + if (results.next()) + result = Base64.decode(results.getString("block")); + } + finally + { + closeConnection(connection); + } + + return result; + } + + public byte[] getDeletedMailBlock(String userName) throws IOException, SQLException + { + Connection connection = openConnection(); + + byte[] result = null; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.GET_DELETED_USER_MAIL_BLOCK)); + statement.setString(1, userName); + log(statement); + + ResultSet results = statement.executeQuery(); + if (results.next()) + result = Base64.decode(results.getString("block")); + } + finally + { + closeConnection(connection); + } + + return result; + } + + public String getDeletedUser() throws IOException, SQLException + { + Connection connection = openConnection(); + + String result = null; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.GET_DELETED_USER)); + log(statement); + + ResultSet results = statement.executeQuery(); + if (results.next()) + result = results.getString("name"); + } + finally + { + closeConnection(connection); + } + + return result; + } + + protected byte[] getKeyBlock (String userName) throws IOException, SQLException + { + Connection connection = openConnection(); + + byte[] result = null; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.GET_USER_KEY_BLOCK)); + statement.setString(1, userName); + log(statement); + + ResultSet results = statement.executeQuery(); + if (results.next()) + result = Base64.decode(results.getString("block")); + } + finally + { + closeConnection(connection); + } + + return result; + } + + public void ensureTables() throws SQLException, IOException + { + Connection connection = openConnection(); + try + { + for (String sql : catalog.getMulti(catalog.CREATE_TABLES)) + { + PreparedStatement statement = connection.prepareStatement (sql); + log(statement); + statement.executeUpdate(); + } + } + finally + { + closeConnection(connection); + } + } + + public void rateLimitFailure (String userName) throws SQLException, IOException + { + Connection connection = openConnection(); + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.GET_LAST_FAILURE)); + statement.setString(1, userName); + log(statement); + + ResultSet rs = statement.executeQuery(); + if (rs.next()) + { + Timestamp timeStamp = rs.getTimestamp("mark"); + Date now = new Date(); + + if (now.getTime() - timeStamp.getTime() < catalog.FAILURE_TIMEOUT_SECONDS * 1000) + throw new PublicMessageException ("Too many failures, try again later."); + } + } + finally + { + closeConnection(connection); + } + } + + public void markFailure (String userName) throws SQLException, IOException + { + Connection connection = openConnection(); + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(catalog.MARK_FAILURE)); + statement.setString(1, userName); + statement.executeUpdate(); + } + finally + { + closeConnection(connection); + } + + } + + public void deleteUser(String userName) throws IOException, SQLException + { + Connection connection = openConnection(); + try + { + String[] texts = catalog.getMulti(catalog.DELETE); + for (String text : texts) + { + PreparedStatement statement = connection.prepareStatement (text); + statement.setString(1, userName); + + log(statement); + statement.executeUpdate(); + } + } + finally + { + closeConnection(connection); + } + } + + public void expungeUser(String userName) throws IOException, SQLException + { + Connection connection = openConnection(); + try + { + String[] texts = catalog.getMulti(catalog.EXPUNGE); + for (String text : texts) + { + PreparedStatement statement = connection.prepareStatement (text); + statement.setString(1, userName); + statement.executeUpdate(); + } + } + finally + { + closeConnection(connection); + } + } + + public Connection openConnection () throws IOException, SQLException + { + log.debug("Connecting to", catalog.CONNECTION_STRING); + return DriverManager.getConnection(catalog.CONNECTION_STRING, catalog.USER, Passwords.getPasswordFor(catalog.USER)); + } + + public void closeConnection (Connection connection) + { + try + { + if (connection != null) + connection.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + public void log (Statement sql) + { + System.out.println (sql); + } + + protected void testIllegalUserName(String userName) throws Exception + { + // http://www.ietf.org/rfc/rfc2142.txt + final String[] illegalStartsWith = { + "info", + "marketing", + "sales", + "support", + + "abuse", + "noc", + "security", + + "postmaster", + "hostmaster", + "usenet", + "news", + "webmaster", + "www", + "uucp", + "ftp", + + "admin", + "system", + "root", + "test", + "root", + "hostma", + "web", + "post", + "mail", + }; + + final String[] illegalParts = { + "postmaster", + "webmaster", + "root", + "admin", + "system", + }; + + String username = userName.toLowerCase(); + for (String illegal : illegalParts) + { + if (username.indexOf(illegal) != -1) + throw new Exception("Illegal username"); + } + + for (String illegal : illegalStartsWith) + { + if (username.startsWith(illegal)) + throw new Exception("Illegal username"); + } + } +} diff --git a/java/core/src/core/server/db/sql/Catalog.java b/java/core/src/core/server/db/sql/Catalog.java new file mode 100644 index 0000000..7e4a9cd --- /dev/null +++ b/java/core/src/core/server/db/sql/Catalog.java @@ -0,0 +1,51 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.server.srp.db.sql; + +import java.io.IOException; +import java.sql.SQLException; + +import core.constants.ConstantsServer; +import core.util.Passwords; +import core.util.SqlCatalog; +import core.util.Streams; + + +public final class Catalog extends SqlCatalog +{ + public String CONNECTION_STRING = ConstantsServer.DBCONNECTION_PREFIX + "mail"; + public String USER = "mail"; + + public int FAILURE_TIMEOUT_SECONDS = 60; + + public final String + CREATE_TABLES = "create_tables", + CREATE_USER = "create_user", + GET_USER = "get_user", + GET_USER_MAIL_BLOCK = "get_user_mail_block", + SET_USER_MAIL_BLOCK = "set_user_mail_block", + GET_USER_KEY_BLOCK = "get_user_key_block", + SET_USER_KEY_BLOCK = "set_user_key_block", + GET_LAST_FAILURE = "get_last_failure", + MARK_FAILURE = "mark_failure", + + DELETE = "delete_user.sql", + EXPUNGE = "expunge_deleted_user.sql", + GET_DELETED_USER = "get_deleted_user.sql", + GET_DELETED_USER_MAIL_BLOCK = "get_deleted_user_mail_block.sql", + + ROOM_FOR_NEW_USER = "room_for_new_user"; + + public Catalog () + { + + } + + public String getPassword () throws IOException + { + return Passwords.getPasswordFor(USER); + } +} diff --git a/java/core/src/core/server/db/sql/create_tables b/java/core/src/core/server/db/sql/create_tables new file mode 100644 index 0000000..ecd8894 --- /dev/null +++ b/java/core/src/core/server/db/sql/create_tables @@ -0,0 +1,55 @@ +CREATE TABLE IF NOT EXISTS registry ( + k VARCHAR(50), + v VARCHAR(255), + mark TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (k) +); + +INSERT IGNORE INTO registry (k,v) VALUES ("max_users", "1000"); + +CREATE TABLE IF NOT EXISTS user ( + version VARCHAR(50), + id INTEGER AUTO_INCREMENT, + name VARCHAR(255) COLLATE utf8_general_ci NOT NULL DEFAULT '', + v TEXT, + s TEXT, + mark TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (name), + UNIQUE KEY (id) +); + +CREATE TABLE IF NOT EXISTS mail_block ( + user_id INTEGER, + block TEXT, + mark TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (user_id) +); + +CREATE TABLE IF NOT EXISTS key_block ( + user_id INTEGER, + block TEXT, + mark TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (user_id) +); + +CREATE TABLE IF NOT EXISTS failure ( + user_id INTEGER, + mark TIMESTAMP, + PRIMARY KEY (user_id) +); + +CREATE TABLE IF NOT EXISTS deleted_user ( + version VARCHAR(50), + id INTEGER AUTO_INCREMENT, + name VARCHAR(255) COLLATE utf8_general_ci NOT NULL DEFAULT '', + v TEXT, + s TEXT, + mark TIMESTAMP NULL DEFAULT NULL, + deleted TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS deleted_mail_block LIKE mail_block; +CREATE TABLE IF NOT EXISTS deleted_key_block LIKE key_block; +DROP TABLE IF EXISTS metadata; + \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/create_user b/java/core/src/core/server/db/sql/create_user new file mode 100644 index 0000000..65db5c4 --- /dev/null +++ b/java/core/src/core/server/db/sql/create_user @@ -0,0 +1 @@ +INSERT INTO user (version, name, v, s) VALUES (?, ?, ?, ?) \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/delete_user.sql b/java/core/src/core/server/db/sql/delete_user.sql new file mode 100644 index 0000000..6e4c106 --- /dev/null +++ b/java/core/src/core/server/db/sql/delete_user.sql @@ -0,0 +1,24 @@ +REPLACE INTO deleted_mail_block + SELECT mail_block.* + FROM mail_block, user + WHERE user.name=? AND mail_block.user_id = user.id; + +REPLACE INTO deleted_key_block + SELECT key_block.* + FROM key_block, user + WHERE user.name=? AND key_block.user_id = user.id; + +REPLACE INTO deleted_user(version, id, name, v, s, mark) + SELECT version, id, name, v, s, mark + FROM user + WHERE user.name=?; + +DELETE mail_block.* + FROM mail_block, user + WHERE user.name=? AND mail_block.user_id = user.id; + +DELETE key_block.* + FROM key_block, user + WHERE user.name=? AND key_block.user_id = user.id; + +DELETE FROM user WHERE user.name=? \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/expunge_deleted_user.sql b/java/core/src/core/server/db/sql/expunge_deleted_user.sql new file mode 100644 index 0000000..9231dae --- /dev/null +++ b/java/core/src/core/server/db/sql/expunge_deleted_user.sql @@ -0,0 +1,9 @@ +DELETE deleted_mail_block.* + FROM deleted_mail_block, deleted_user + WHERE deleted_user.name=? AND deleted_mail_block.user_id = deleted_user.id; + +DELETE deleted_key_block.* + FROM deleted_key_block, deleted_user + WHERE deleted_user.name=? AND deleted_key_block.user_id = deleted_user.id; + +DELETE FROM deleted_user WHERE deleted_user.name=? \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/get_deleted_user.sql b/java/core/src/core/server/db/sql/get_deleted_user.sql new file mode 100644 index 0000000..3dac938 --- /dev/null +++ b/java/core/src/core/server/db/sql/get_deleted_user.sql @@ -0,0 +1,7 @@ +select + * +from + deleted_user +order by + deleted +limit 1 \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/get_deleted_user_mail_block.sql b/java/core/src/core/server/db/sql/get_deleted_user_mail_block.sql new file mode 100644 index 0000000..222f5b7 --- /dev/null +++ b/java/core/src/core/server/db/sql/get_deleted_user_mail_block.sql @@ -0,0 +1,8 @@ +SELECT + deleted_mail_block.* +FROM + deleted_user, + deleted_mail_block +WHERE + deleted_mail_block.user_id = deleted_user.id AND + deleted_user.name = ? \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/get_last_failure b/java/core/src/core/server/db/sql/get_last_failure new file mode 100644 index 0000000..34f1ae7 --- /dev/null +++ b/java/core/src/core/server/db/sql/get_last_failure @@ -0,0 +1,8 @@ +SELECT + failure.mark +FROM + user, + failure +WHERE + user.name = ? AND + failure.user_id = user.id \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/get_user b/java/core/src/core/server/db/sql/get_user new file mode 100644 index 0000000..90c2ac7 --- /dev/null +++ b/java/core/src/core/server/db/sql/get_user @@ -0,0 +1,6 @@ +SELECT + * +FROM + user +WHERE + user.name = ? diff --git a/java/core/src/core/server/db/sql/get_user_key_block b/java/core/src/core/server/db/sql/get_user_key_block new file mode 100644 index 0000000..99494fc --- /dev/null +++ b/java/core/src/core/server/db/sql/get_user_key_block @@ -0,0 +1,8 @@ +SELECT + key_block.* +FROM + user, + key_block +WHERE + key_block.user_id = user.id AND + user.name = ? \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/get_user_mail_block b/java/core/src/core/server/db/sql/get_user_mail_block new file mode 100644 index 0000000..a37c5bd --- /dev/null +++ b/java/core/src/core/server/db/sql/get_user_mail_block @@ -0,0 +1,8 @@ +SELECT + mail_block.* +FROM + user, + mail_block +WHERE + mail_block.user_id = user.id AND + user.name = ? \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/mark_failure b/java/core/src/core/server/db/sql/mark_failure new file mode 100644 index 0000000..baebf51 --- /dev/null +++ b/java/core/src/core/server/db/sql/mark_failure @@ -0,0 +1,8 @@ +REPLACE INTO failure (user_id, mark) +SELECT + id as user_id, + now() as mark +FROM + user +WHERE + name = ? \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/room_for_new_user b/java/core/src/core/server/db/sql/room_for_new_user new file mode 100644 index 0000000..c72a6b4 --- /dev/null +++ b/java/core/src/core/server/db/sql/room_for_new_user @@ -0,0 +1,4 @@ +select + count(*) < (select convert(v, unsigned integer) from registry where k="max_users") as room +from + user \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/set_user_key_block b/java/core/src/core/server/db/sql/set_user_key_block new file mode 100644 index 0000000..4f8e6e8 --- /dev/null +++ b/java/core/src/core/server/db/sql/set_user_key_block @@ -0,0 +1 @@ +REPLACE INTO key_block (user_id, block) VALUES (?, ?) \ No newline at end of file diff --git a/java/core/src/core/server/db/sql/set_user_mail_block b/java/core/src/core/server/db/sql/set_user_mail_block new file mode 100644 index 0000000..3bcd3ab --- /dev/null +++ b/java/core/src/core/server/db/sql/set_user_mail_block @@ -0,0 +1 @@ +REPLACE INTO mail_block (user_id, block) VALUES (?, ?) \ No newline at end of file diff --git a/java/core/src/core/server/log/Tail.java b/java/core/src/core/server/log/Tail.java new file mode 100644 index 0000000..4ff3c15 --- /dev/null +++ b/java/core/src/core/server/log/Tail.java @@ -0,0 +1,51 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.server.log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Date; + +public class Tail +{ + static final int TAIL_INTERVAL = 1000; + + String fileName; + String lastTail; + Date lastTailTime; + + public Tail (String fileName) + { + this.fileName = fileName; + } + + public String getTail () throws IOException + { + Date now = new Date(); + if (lastTailTime != null && + now.getTime() < lastTailTime.getTime() + TAIL_INTERVAL) + return lastTail; + + lastTailTime = now; + lastTail = null; + + File file = new File(fileName); + RandomAccessFile fileHandler = new RandomAccessFile( file, "r" ); + long fileLength = file.length() - 1; + long blockLength = Math.min(fileLength, 4096); + + fileHandler.seek(fileLength - blockLength); + + byte[] bytes = new byte[(int) blockLength]; + fileHandler.readFully(bytes); + fileHandler.close(); + + lastTail = new String(bytes, "UTF-8"); + return lastTail; + } +} diff --git a/java/core/src/core/server/mailextra/MailExtraDb.java b/java/core/src/core/server/mailextra/MailExtraDb.java new file mode 100644 index 0000000..2a60731 --- /dev/null +++ b/java/core/src/core/server/mailextra/MailExtraDb.java @@ -0,0 +1,220 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.server.mailextra; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.bc.util.Strings; +import org.json.JSONException; +import org.json.JSONObject; + +import core.util.Base64; +import core.constants.ConstantsPushNotifications; +import core.crypt.HashSha256; +import core.server.mailextra.sql.Catalog; +import core.util.LogOut; +import core.util.Pair; +import core.util.Passwords; +import core.util.Triple; + +public class MailExtraDb +{ + LogOut log = new LogOut(MailExtraDb.class); + Catalog catalog = new Catalog(); + + public String getUserHash (String email) + { + HashSha256 hasher = new HashSha256(); + return Base64.encode(hasher.hash(Strings.toByteArray(email.toLowerCase()))); + } + + public Triple getDeviceFor (String email) throws SQLException, IOException + { + return getDeviceForHash(getUserHash(email)); + } + + public Triple getDeviceForHash (String hash) throws SQLException, IOException + { + + Connection connection = openConnection(); + Triple result; + + try + { + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(Catalog.GET_NOTIFICATIONS_FOR)); + statement.setString(1, hash); + log.debug(statement); + + ResultSet rs = statement.executeQuery(); + if (!rs.next()) + return null; + + result = new Triple(rs.getString("notification_type"), rs.getString("device_type"), rs.getString("device_id")); + } + finally + { + closeConnection(connection); + } + + return result; + } + + public void setNotificationsFor (String email, String notificationType, String deviceType, String deviceId) throws SQLException, IOException + { + setNotificationsFor (getUserHash(email), deviceType, deviceId, notificationType); + } + + public void setNotificationsForHash (String hash, String notificationType, String deviceType, String deviceId) throws SQLException, IOException + { + Connection connection = openConnection(); + + try + { + String statements[] = catalog.getMulti(Catalog.SET_NOTIFICATIONS_FOR); + + boolean first = true; + for (String s : statements) + { + PreparedStatement statement = connection.prepareStatement (s); + + if (first) + { + statement.setString(1, hash); + statement.setString(2, notificationType); + statement.setString(3, deviceType); + statement.setString(4, deviceId); + } + + log.debug(statement); + statement.executeUpdate(); + first = false; + } + } + finally + { + closeConnection(connection); + } + } + + public void addDaysTo (String email, int days) throws SQLException, IOException + { + Connection connection = openConnection(); + + try + { + String statements[] = catalog.getMulti(Catalog.ADD_DAYS_TO); + + boolean first = true; + for (String s : statements) + { + PreparedStatement statement = connection.prepareStatement (s); + + if (first) + { + statement.setString(1, email); + statement.setInt(2, days); + } + + + log.debug(statement); + statement.executeUpdate(); + first = false; + } + } + finally + { + closeConnection(connection); + } + } + + public int getDaysLeft (String email) throws SQLException, IOException + { + Connection connection = openConnection(); + + try + { + log.debug(catalog.getSingle(Catalog.GET_DAYS_LEFT)); + PreparedStatement statement = connection.prepareStatement (catalog.getSingle(Catalog.GET_DAYS_LEFT)); + statement.setString(1, email); + log.debug(statement); + + ResultSet rs = statement.executeQuery(); + if (!rs.next()) + throw new IOException ("No results"); + + int days = rs.getInt("days"); + rs.close(); + + return days; + } + finally + { + closeConnection(connection); + } + } + + public void ensureTables() throws SQLException, IOException + { + Connection connection = openConnection(); + + try + { + for (String sql : catalog.getMulti(Catalog.CREATE_TABLES)) + { + PreparedStatement statement = connection.prepareStatement (sql); + log.debug(statement); + statement.executeUpdate(); + } + } + finally + { + closeConnection(connection); + } + } + + public Connection openConnection () throws SQLException, IOException + { + return DriverManager.getConnection(Catalog.CONNECTION_STRING, Catalog.USER, Passwords.getPasswordFor(Catalog.USER)); + } + + public void closeConnection (Connection connection) + { + try + { + if (connection != null) + connection.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + public void handlePushNotificationsJson(JSONObject json) throws JSONException, SQLException, IOException + { + String user = json.getString(ConstantsPushNotifications.USER); + + setNotificationsForHash( + user, + json.getString(ConstantsPushNotifications.NOTIFICATION_TYPE), + json.has(ConstantsPushNotifications.DEVICE_TYPE) ? + json.getString(ConstantsPushNotifications.DEVICE_TYPE) : null, + json.has(ConstantsPushNotifications.DEVICE_ID) ? + json.getString(ConstantsPushNotifications.DEVICE_ID) : null + ); + } + + public void removeDevice(String deviceType, String deviceId) + { + } + +} diff --git a/java/core/src/core/server/mailextra/sql/Catalog.java b/java/core/src/core/server/mailextra/sql/Catalog.java new file mode 100644 index 0000000..b23cf13 --- /dev/null +++ b/java/core/src/core/server/mailextra/sql/Catalog.java @@ -0,0 +1,25 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.server.mailextra.sql; + +import java.io.IOException; + +import core.util.SqlCatalog; +import core.util.Streams; + +public class Catalog extends SqlCatalog +{ + static public final String CONNECTION_STRING = "jdbc:mysql://localhost/mail_extra"; + public static final String USER = "mail_extra"; + + static public final String GET_NOTIFICATIONS_FOR = "get_notifications_for.sql"; + static public final String SET_NOTIFICATIONS_FOR = "set_notifications_for.sql"; + static public final String PRUNE_DEVICES = "prune_devices.sql"; + + static public final String CREATE_TABLES = "create_tables"; + static public final String ADD_DAYS_TO = "add_days_to"; + static public final String GET_DAYS_LEFT = "get_days_left"; +} diff --git a/java/core/src/core/server/mailextra/sql/add_days_to b/java/core/src/core/server/mailextra/sql/add_days_to new file mode 100644 index 0000000..1b53abe --- /dev/null +++ b/java/core/src/core/server/mailextra/sql/add_days_to @@ -0,0 +1,10 @@ +SET @email = ?, @days = ?; + +INSERT INTO expirations (email, expiration) VALUES(@email, DATE_ADD(CURDATE(), INTERVAL @days DAY)) +ON DUPLICATE KEY UPDATE + expiration = + IF( + DATEDIFF(expiration, CURDATE())>0, + DATE_ADD(expiration, INTERVAL @days DAY), + VALUES(expiration) + ) diff --git a/java/core/src/core/server/mailextra/sql/create_tables b/java/core/src/core/server/mailextra/sql/create_tables new file mode 100644 index 0000000..a606ad3 --- /dev/null +++ b/java/core/src/core/server/mailextra/sql/create_tables @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS expirations ( + email VARCHAR(255) NOT NULL, + expiration DATE NOT NULL, + PRIMARY KEY (email) +); + +CREATE TABLE IF NOT EXISTS `device` ( + `email` VARCHAR(255) NOT NULL, + `notification_type` ENUM('NONE','SHORT','LONG') NOT NULL, + `device_type` VARCHAR(255) DEFAULT NULL, + `device_id` VARCHAR(255) DEFAULT NULL, + `mark` date NOT NULL, + PRIMARY KEY (`email`), + KEY `device` (`device_type`,`device_id`) +); \ No newline at end of file diff --git a/java/core/src/core/server/mailextra/sql/get_days_left b/java/core/src/core/server/mailextra/sql/get_days_left new file mode 100644 index 0000000..71f7794 --- /dev/null +++ b/java/core/src/core/server/mailextra/sql/get_days_left @@ -0,0 +1,6 @@ +SELECT + DATEDIFF(expiration, CURDATE()) as days +FROM + expirations +WHERE + expirations.email = ? diff --git a/java/core/src/core/server/mailextra/sql/get_notifications_for.sql b/java/core/src/core/server/mailextra/sql/get_notifications_for.sql new file mode 100644 index 0000000..6db878c --- /dev/null +++ b/java/core/src/core/server/mailextra/sql/get_notifications_for.sql @@ -0,0 +1,10 @@ +SELECT + device.notification_type, + device.device_type, + device.device_id, + device.mark +FROM + device +WHERE + device.email = ? + diff --git a/java/core/src/core/server/mailextra/sql/prune_notifications.sql b/java/core/src/core/server/mailextra/sql/prune_notifications.sql new file mode 100644 index 0000000..e69de29 diff --git a/java/core/src/core/server/mailextra/sql/set_notifications_for.sql b/java/core/src/core/server/mailextra/sql/set_notifications_for.sql new file mode 100644 index 0000000..26e5b3f --- /dev/null +++ b/java/core/src/core/server/mailextra/sql/set_notifications_for.sql @@ -0,0 +1,4 @@ +SET @email = ?, @notification_type = ?, @device_type = ?, @device_id = ?; + +DELETE FROM device WHERE device_type = @device_type AND device_id = @device_id; +REPLACE INTO device (email, device_type, device_id, notification_type, mark) VALUES(@email, @device_type, @device_id, @notification_type, now()) diff --git a/java/core/src/core/srp/SRPPacketSerializerJSON.java b/java/core/src/core/srp/SRPPacketSerializerJSON.java new file mode 100644 index 0000000..5e40d39 --- /dev/null +++ b/java/core/src/core/srp/SRPPacketSerializerJSON.java @@ -0,0 +1,301 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp; + +import java.math.BigInteger; + +import core.util.Base64; +import org.json.JSONObject; + +import core.util.CallSingle; +import core.util.JSONRegistry; +import core.util.Strings; + + +public class SRPPacketSerializerJSON +{ + static public void register () {} + + static { + JSONRegistry.register ( + "core.srp.SRPPackets$Packet1_ClientSendsHello", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.Packet1_ClientSendsHello p = (SRPPackets.Packet1_ClientSendsHello)v; + object.put("user", p.user); + object.put("version", p.version); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new SRPPackets.Packet1_ClientSendsHello( + v.get("user").toString(), + v.get("version").toString() + ); + } + } + ); + + JSONRegistry.register ( + "core.srp.SRPPackets$Packet2_ServerSendSalt", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.Packet2_ServerSendSalt p = (SRPPackets.Packet2_ServerSendSalt)v; + object.put("salt", Base64.encode(p.salt)); + object.put("version", p.version); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new SRPPackets.Packet2_ServerSendSalt( + v.get("version").toString(), + Base64.decode(v.get("salt").toString()) + ); + } + } + ); + + JSONRegistry.register ( + "core.srp.SRPPackets$Packet3_ClientSendPublicKey", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.Packet3_ClientSendPublicKey p = (SRPPackets.Packet3_ClientSendPublicKey)v; + object.put("publicKey", new String(Base64.encode(p.publicKey))); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new SRPPackets.Packet3_ClientSendPublicKey( + Base64.decode(v.get("publicKey").toString()) + ); + } + } + ); + + JSONRegistry.register ( + "core.srp.SRPPackets$Packet4_ServerSendPublicKey", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.Packet4_ServerSendPublicKey p = (SRPPackets.Packet4_ServerSendPublicKey)v; + object.put("publicKey", new String(Base64.encode(p.publicKey))); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new SRPPackets.Packet4_ServerSendPublicKey( + new BigInteger(Base64.decode(v.get("publicKey").toString())) + ); + } + } + ); + + JSONRegistry.register ( + "core.srp.SRPPackets$Packet5_ClientSendEvidence", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.Packet5_ClientSendEvidence p = (SRPPackets.Packet5_ClientSendEvidence)v; + object.put("evidence", new String(Base64.encode(p.evidence))); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new SRPPackets.Packet5_ClientSendEvidence( + Base64.decode(v.get("evidence").toString()) + ); + } + } + ); + + JSONRegistry.register ( + "core.srp.SRPPackets$Packet6_ServerSendEvidenceAndPayload", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.Packet6_ServerSendEvidenceAndPayload p = (SRPPackets.Packet6_ServerSendEvidenceAndPayload)v; + object.put("evidence", new String(Base64.encode(p.evidence))); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new SRPPackets.Packet6_ServerSendEvidenceAndPayload( + new BigInteger(Base64.decode(v.get("evidence").toString())) + ); + } + } + ); + + JSONRegistry.register ( + "core.srp.SRPPackets$PacketInit_ClientTestCreate", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.PacketInit_ClientTestCreate p = (SRPPackets.PacketInit_ClientTestCreate)v; + object.put("user", p.user); + object.put("version", p.version); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new + SRPPackets.PacketInit_ClientTestCreate( + v.getString("version"), + v.getString("user") + ); + } + } + ); + + JSONRegistry.register ( + "core.srp.SRPPackets$PacketInit_EncryptedPacket", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.PacketInit_EncryptedPacket p = (SRPPackets.PacketInit_EncryptedPacket)v; + object.put("encryptedBlock", new String(Base64.encode(p.encryptedBlock))); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new + SRPPackets.PacketInit_EncryptedPacket( + Base64.decode(v.getString("encryptedBlock")) + ); + } + } + ); + + + JSONRegistry.register ( + "core.srp.SRPPackets$PacketInit_ServerResponse", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.PacketInit_ServerResponse p = (SRPPackets.PacketInit_ServerResponse)v; + object.put("succeeded", p.succeeded); + object.put("reason", p.reason); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + return new + SRPPackets.PacketInit_ServerResponse( + v.getBoolean("succeeded"), + v.has("reason") ? v.getString("reason") : null + ); + } + } + ); + + + JSONRegistry.register ( + "core.srp.SRPPackets$PacketInit_ClientPreAutheticationInitialization", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + SRPPackets.PacketInit_ClientPreAutheticationInitialization p = (SRPPackets.PacketInit_ClientPreAutheticationInitialization)v; + object.put("version", p.version); + object.put("user", p.user); + object.put("s", new String(Base64.encode(p.s))); + object.put("v", new String(Base64.encode(p.v))); + + if (p.extra != null) + object.put("extra", new String(Base64.encode(p.extra))); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + SRPPackets.PacketInit_ClientPreAutheticationInitialization p = + new SRPPackets.PacketInit_ClientPreAutheticationInitialization( + v.getString("version"), + v.getString("user"), + Base64.decode(v.getString("v")), + Base64.decode(v.getString("s")) + ); + + if (v.has("extra")) + p.extra = Base64.decode(v.getString("extra")); + + return p; + } + } + ); + } +} diff --git a/java/core/src/core/srp/SRPPackets.java b/java/core/src/core/srp/SRPPackets.java new file mode 100644 index 0000000..21fd4ef --- /dev/null +++ b/java/core/src/core/srp/SRPPackets.java @@ -0,0 +1,182 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp; + +import java.math.BigInteger; + +import core.callback.Callback; +import core.callback.CallbackDefault; + + +public class SRPPackets +{ + public static void register () { } + static { + SRPPacketSerializerJSON.register(); + } + + public static class PacketInit_ClientTestCreate + { + public String version; + public String user; + + public PacketInit_ClientTestCreate(String version, String user) + { + this.version = version; + this.user = user; + } + } + + public static class PacketInit_ClientPreAutheticationInitialization + { + public String user; + public byte[] v; + public byte[] s; + public byte[] extra; + public String version; + + public PacketInit_ClientPreAutheticationInitialization(String version, String user, byte[] v, byte[] s) + { + this.version = version; + this.user = user; + this.v = v; + this.s = s; + extra = null; + } + } + + public static class PacketInit_EncryptedPacket + { + public byte[] encryptedBlock; + + public PacketInit_EncryptedPacket (byte[] encryptedBlock) + { + this.encryptedBlock = encryptedBlock; + } + + public static Callback wrap_() + { + return new CallbackDefault() { + @Override + public void onSuccess(Object... arguments) throws Exception { + callback.invoke(new PacketInit_EncryptedPacket((byte[])(arguments[0]))); + } + }; + } + + public static Callback unwrap_() + { + return new CallbackDefault() { + @Override + public void onSuccess(Object... arguments) throws Exception { + PacketInit_EncryptedPacket packet = (PacketInit_EncryptedPacket)(arguments[0]); + callback.invoke(packet.encryptedBlock); + } + }; + } + } + + public static class PacketInit_ServerResponse + { + public boolean succeeded; + public String reason; + + public PacketInit_ServerResponse(boolean succeeded, String reason) + { + this.succeeded = succeeded; + this.reason = reason; + } + } + + public static class Packet1_ClientSendsHello + { + public String user; + public String version; + + public Packet1_ClientSendsHello (String user, String version) + { + this.user = user; + this.version = version; + } + } + + public static class Packet2_ServerSendSalt + { + public byte[] salt; + public String version; + + public Packet2_ServerSendSalt (String version, byte[] salt) + { + this.salt = salt; + this.version = version; + } + + public byte[] getSalt () + { + return salt; + } + } + + public static class Packet3_ClientSendPublicKey + { + public byte[] publicKey; + + public Packet3_ClientSendPublicKey (byte[] publicKey) + { + this.publicKey = publicKey; + } + + public byte[] getPublicKey () + { + return publicKey; + } + } + + public static class Packet4_ServerSendPublicKey + { + public byte[] publicKey; + + public Packet4_ServerSendPublicKey (BigInteger publicKey) + { + this.publicKey = publicKey.toByteArray(); + } + + public byte[] getPublicKey () + { + return publicKey; + } + } + + public static class Packet5_ClientSendEvidence + { + public byte[] evidence; + + public Packet5_ClientSendEvidence (byte[] evidence) + { + this.evidence = evidence; + } + + public byte[] getEvidence () + { + return evidence; + } + } + + public static class Packet6_ServerSendEvidenceAndPayload + { + public byte[] evidence; + + public Packet6_ServerSendEvidenceAndPayload (BigInteger evidence) + { + this.evidence = evidence.toByteArray(); + } + + public byte[] getEvidence () + { + return evidence; + } + } +} diff --git a/java/core/src/core/srp/SRPSession.java b/java/core/src/core/srp/SRPSession.java new file mode 100644 index 0000000..5d7f8cf --- /dev/null +++ b/java/core/src/core/srp/SRPSession.java @@ -0,0 +1,43 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp; + +import core.crypt.Cryptor; +import core.crypt.CryptorAES; +import core.exceptions.CryptoException; + + +public class SRPSession +{ + protected byte[] sessionKey; + + public byte[] streamDecrypt (byte[] bytes) throws CryptoException + { + try + { + Cryptor cryptor = new CryptorAES(sessionKey); + return cryptor.decrypt(bytes); + } + catch (Exception e) + { + e.printStackTrace(); + throw new CryptoException(e); + } + } + + public byte[] streamEncrypt (byte[] bytes) throws CryptoException + { + try + { + Cryptor cryptor = new CryptorAES(sessionKey); + return cryptor.encrypt(bytes); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } +} diff --git a/java/core/src/core/srp/client/SRPClient.java b/java/core/src/core/srp/client/SRPClient.java new file mode 100644 index 0000000..6a68018 --- /dev/null +++ b/java/core/src/core/srp/client/SRPClient.java @@ -0,0 +1,91 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.client; + +import core.callback.Callback; +import com.jordanzimmerman.SRPClientSession; +import com.jordanzimmerman.SRPFactory; +import com.jordanzimmerman.SRPVerifier; + +import core.callback.CallbackDefault; +import core.callbacks.JSONSerialize; +import core.callbacks.IoSend; +import core.crypt.KeyPairFromPassword; +import core.exceptions.CryptoException; +import core.io.IoChain; +import core.srp.SRPPackets; +import core.srp.SRPSession; + +public class SRPClient extends SRPSession +{ + static { SRPPackets.register(); } + + SRPFactory factory; + SRPClientAsync session; + + public SRPClient (byte[] password) + { + session = new SRPClientAsync(password); + } + + public static SRPVerifier createVerifierFromKeyPair (KeyPairFromPassword keyPair) throws CryptoException + { + SRPFactory factory = SRPFactory.getInstance(); + SRPVerifier verifier = factory.makeVerifier(keyPair.getVerifier()); + + return verifier; + } + + public SRPPackets.Packet1_ClientSendsHello step1_generateHello (String user, String version) + { + return new SRPPackets.Packet1_ClientSendsHello(user, version); + } + + public Callback step2_generatePublicKey_send_ (SRPPackets.Packet2_ServerSendSalt packet, IoChain sender) + { + return + session.setSalt_(packet.getSalt()) + .addCallback( + new CallbackDefault() { + public void onSuccess(Object...arguments) { + next(new SRPPackets.Packet3_ClientSendPublicKey(session.getPublicKey())); + } + } + ) + .addCallback(new JSONSerialize()) + .addCallback(new IoSend(sender)); + } + + public Callback step3_generateEvidenceUsingServerProvidedInputs_send (SRPPackets.Packet4_ServerSendPublicKey packet, IoChain sender) throws CryptoException + { + return + session.setServerPublicKey_(packet.getPublicKey()) + .addCallback( + new CallbackDefault() { + public void onSuccess(Object...arguments) { + next(new SRPPackets.Packet5_ClientSendEvidence(session.getEvidenceValue())); + } + } + ) + .addCallback(new JSONSerialize()) + .addCallback(new IoSend(sender)); + } + + public Callback step4_validateServerEvidence (SRPPackets.Packet6_ServerSendEvidenceAndPayload packet) throws CryptoException + { + return + session.validateServerEvidenceValue_M2_(packet.getEvidence()) + .addCallback( + new CallbackDefault() { + public void onSuccess(Object...arguments) { + sessionKey = session.getSessionKey(); + next(sessionKey); + } + } + ); + } + +} diff --git a/java/core/src/core/srp/client/SRPClientAsync.java b/java/core/src/core/srp/client/SRPClientAsync.java new file mode 100644 index 0000000..1aedb98 --- /dev/null +++ b/java/core/src/core/srp/client/SRPClientAsync.java @@ -0,0 +1,71 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.client; + +import com.jordanzimmerman.SRPClientSession; +import com.jordanzimmerman.SRPFactory; + +import core.util.Base64; +import core.callback.Callback; +import core.callback.CallbackDefault; + +public class SRPClientAsync +{ + SRPClientSession key; + + public SRPClientAsync (byte[] password) + { + key = SRPFactory.getInstance().newClientSession(password); + } + + public Callback setSalt_(byte[] bs) + { + return new CallbackDefault(bs) + { + public void onSuccess(Object... arguments) throws Exception { + key.setSalt_s((byte[])V(0)); + callback.invoke(); + } + }; + } + + public Callback setServerPublicKey_(byte[] publicKey) + { + return new CallbackDefault(publicKey) + { + public void onSuccess(Object... arguments) throws Exception { + key.setServerPublicKey_B((byte[])V(0)); + callback.invoke(); + } + }; + } + + public Callback validateServerEvidenceValue_M2_(byte[] evidence) + { + return new CallbackDefault(evidence) + { + public void onSuccess(Object... arguments) throws Exception { + key.validateServerEvidenceValue_M2((byte[])V(0)); + callback.invoke(); + } + }; + } + + public byte[] getSessionKey() + { + return key.getSessionKey_K(); + } + + public byte[] getPublicKey() + { + return key.getPublicKey_A_(); + } + + public byte[] getEvidenceValue() + { + return key.getEvidenceValue_M1_(); + } +} diff --git a/java/core/src/core/srp/client/SRPClientListener.java b/java/core/src/core/srp/client/SRPClientListener.java new file mode 100644 index 0000000..59a4b7f --- /dev/null +++ b/java/core/src/core/srp/client/SRPClientListener.java @@ -0,0 +1,10 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.client; + +public interface SRPClientListener { + public void onSRPStep (String stepName); +} diff --git a/java/core/src/core/srp/client/SRPClientUserSession.java b/java/core/src/core/srp/client/SRPClientUserSession.java new file mode 100644 index 0000000..8bb199a --- /dev/null +++ b/java/core/src/core/srp/client/SRPClientUserSession.java @@ -0,0 +1,93 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.client; + +import core.callbacks.IoOpen; +import core.constants.ConstantsVersion; +import core.crypt.KeyPairFromPassword; +import core.exceptions.CryptoException; +import core.io.IoChain; +import core.srp.SRPPackets; +import core.util.SimpleSerializer; + +public class SRPClientUserSession extends IoChain +{ + String user; + KeyPairFromPassword keyPair; + SRPClient srp; + SRPClientListener listener; + int step; + + public SRPClientUserSession (String user, KeyPairFromPassword keyPair, IoChain sender, SRPClientListener listener) throws CryptoException + { + super(sender); + + this.user = user; + this.keyPair = keyPair; + this.listener = listener; + } + + @Override + public void open () throws Exception + { + if (listener != null) + listener.onSRPStep("H"); + + step = 0; + this.srp = new SRPClient(keyPair.getVerifier()); + sender.send(SimpleSerializer.serialize(new SRPPackets.Packet1_ClientSendsHello(user, ConstantsVersion.LOGIN))); + } + + @Override + protected void onReceive(byte[] bytes) throws Exception + { + switch (step++) + { + case 0: + { + if (listener != null) + listener.onSRPStep("" + step); + + SRPPackets.Packet2_ServerSendSalt in = SimpleSerializer.deserialize(bytes); + srp.step2_generatePublicKey_send_(in, sender).invoke(); + } break; + + case 1: + { + if (listener != null) + listener.onSRPStep("" + step); + + SRPPackets.Packet4_ServerSendPublicKey in = SimpleSerializer.deserialize(bytes); + srp.step3_generateEvidenceUsingServerProvidedInputs_send(in, sender).invoke(); + } break; + case 2: + { + if (listener != null) + listener.onSRPStep("" + step); + + SRPPackets.Packet6_ServerSendEvidenceAndPayload in = SimpleSerializer.deserialize(bytes); + srp.step4_validateServerEvidence(in).addCallback(new IoOpen(receiver)).invoke(); + } break; + + default: + + if (listener != null) + listener.onSRPStep("R"); + + super.onReceive(srp.streamDecrypt(bytes)); + } + } + + @Override + public void send(byte[] packet) throws Exception + { + if (listener != null) + listener.onSRPStep("S"); + + sender.send(srp.streamEncrypt(packet)); + } + +} diff --git a/java/core/src/core/srp/server/SRPServer.java b/java/core/src/core/srp/server/SRPServer.java new file mode 100644 index 0000000..59938bd --- /dev/null +++ b/java/core/src/core/srp/server/SRPServer.java @@ -0,0 +1,77 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.server; + +import java.math.BigInteger; +import java.security.InvalidKeyException; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import org.bc.crypto.params.KeyParameter; + + +import com.jordanzimmerman.SRPFactory; +import com.jordanzimmerman.SRPServerSession; +import com.jordanzimmerman.SRPVerifier; + +import core.exceptions.CryptoException; +import core.srp.SRPPackets; +import core.srp.SRPSession; +import core.srp.SRPPackets.Packet2_ServerSendSalt; +import core.srp.SRPPackets.Packet3_ClientSendPublicKey; +import core.srp.SRPPackets.Packet4_ServerSendPublicKey; +import core.srp.SRPPackets.Packet5_ClientSendEvidence; +import core.srp.SRPPackets.Packet6_ServerSendEvidenceAndPayload; + +public class SRPServer extends SRPSession +{ + static { SRPPackets.register(); } + + SRPFactory factory; + SRPServerSession session; + + public SRPServer () + { + factory = SRPFactory.getInstance(); + } + + public SRPPackets.Packet2_ServerSendSalt step1_getSalt_send (String version, BigInteger verifier, BigInteger salt) + { + session = factory.newServerSession(new SRPVerifier (verifier, salt)); + return new SRPPackets.Packet2_ServerSendSalt(version, session.getVerifier().salt_s.toByteArray()); + } + + public SRPPackets.Packet4_ServerSendPublicKey step2_receivePublicKey_generatePublicKey_send (SRPPackets.Packet3_ClientSendPublicKey packet) throws CryptoException + { + try + { + session.setClientPublicKey_A(packet.getPublicKey()); + return new SRPPackets.Packet4_ServerSendPublicKey(session.getPublicKey_B()); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } + + public SRPPackets.Packet6_ServerSendEvidenceAndPayload step3_validateClientEvidence_generateEvidence_send (SRPPackets.Packet5_ClientSendEvidence packet) throws CryptoException + { + try + { + session.computeCommonValue_S(); + session.validateClientEvidenceValue_M1(packet.getEvidence()); + + sessionKey = session.getSessionKey_K(); + + return new SRPPackets.Packet6_ServerSendEvidenceAndPayload(session.getEvidenceValue_M2()); + } + catch (Exception e) + { + throw new CryptoException(e); + } + } +} diff --git a/java/core/src/core/srp/server/SRPServerUserSession.java b/java/core/src/core/srp/server/SRPServerUserSession.java new file mode 100644 index 0000000..0567e02 --- /dev/null +++ b/java/core/src/core/srp/server/SRPServerUserSession.java @@ -0,0 +1,177 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.server; + +import java.math.BigInteger; + +import core.client.messages.ClientMessagesSerializerJSON; +import core.crypt.CryptorRSAAES; +import core.exceptions.CryptoException; +import core.exceptions.PublicMessageException; +import core.io.IoChain; +import core.srp.SRPPackets; +import core.srp.SRPPackets.PacketInit_ClientTestCreate; +import core.util.LogOut; +import core.util.SimpleSerializer; +import core.util.Strings; +import core.util.Triple; + + +public class SRPServerUserSession extends IoChain +{ + static { SRPPackets.register(); ClientMessagesSerializerJSON.register(); } + + LogOut log = new LogOut(SRPServerUserSession.class); + + CryptorRSAAES cryptorRSA; + boolean noMoreRoomForUsers = false; + String userName; + SRPServer srp = new SRPServer(); + int step = 0; + SRPServerUserSessionDb db; + + public SRPServerUserSession (CryptorRSAAES cryptorRSA, SRPServerUserSessionDb db, IoChain session) + { + super(session); + + this.db = db; + this.cryptorRSA = cryptorRSA; + } + + public String getUserName () + { + return userName; + } + + @Override + public void onReceive (byte[] bytes) throws Exception + { + try + { + if (step == 0) + { + Object object = SimpleSerializer.deserialize(bytes); + if (object instanceof SRPPackets.PacketInit_EncryptedPacket) + { + step = -1; + + SRPPackets.PacketInit_EncryptedPacket + block = (SRPPackets.PacketInit_EncryptedPacket)object; + + Object packet = SimpleSerializer.deserialize(cryptorRSA.decrypt(block.encryptedBlock)); + + if (packet instanceof SRPPackets.PacketInit_ClientPreAutheticationInitialization) + { + step_init((SRPPackets.PacketInit_ClientPreAutheticationInitialization)packet); + return; + } + else + if (packet instanceof SRPPackets.PacketInit_ClientTestCreate) + { + step_testcreate((SRPPackets.PacketInit_ClientTestCreate)packet); + return; + } + } + } + + switch (step++) + { + case 0: + { + SRPPackets.Packet1_ClientSendsHello packet = SimpleSerializer.deserialize(bytes); + this.userName = packet.user; + db.rateLimitFailure(userName); + + Triple vvs = db.getUserVVS(packet.user); + sender.send(SimpleSerializer.serialize( + srp.step1_getSalt_send(vvs.first, vvs.second, vvs.third) + )); + } + break; + + case 1: + { + SRPPackets.Packet3_ClientSendPublicKey packet = SimpleSerializer.deserialize(bytes); + sender.send(SimpleSerializer.serialize(srp.step2_receivePublicKey_generatePublicKey_send(packet))); + } + break; + + case 2: + { + SRPPackets.Packet5_ClientSendEvidence packet = SimpleSerializer.deserialize(bytes); + SRPPackets.Packet6_ServerSendEvidenceAndPayload out = + srp.step3_validateClientEvidence_generateEvidence_send(packet); + + sender.send(SimpleSerializer.serialize(out)); + super.open(); + } + break; + + default: + super.onReceive(srp.streamDecrypt(bytes)); + + } + } + catch (CryptoException e) + { + db.markFailure(userName); + throw e; + } + catch (PublicMessageException e) + { + db.markFailure(userName); + throw e; + } + catch (Exception e) + { + db.markFailure(userName); + throw new CryptoException (e); + } + } + + public void step_testcreate(PacketInit_ClientTestCreate packet) throws Exception + { + try + { + db.testCreate (packet.version, packet.user); + sender.send (SimpleSerializer.serialize(new SRPPackets.PacketInit_ServerResponse(true, null))); + } + catch (PublicMessageException e) + { + sender.send (SimpleSerializer.serialize(new SRPPackets.PacketInit_ServerResponse(false, e.message))); + } + catch (Exception e) + { + sender.send (SimpleSerializer.serialize(new SRPPackets.PacketInit_ServerResponse(false, null))); + } + stop(); + } + + public void step_init (SRPPackets.PacketInit_ClientPreAutheticationInitialization packet) throws Exception + { + try + { + db.createUser (packet.version, packet.user, new BigInteger(packet.v), new BigInteger(packet.s), packet.extra); + sender.send (SimpleSerializer.serialize(new SRPPackets.PacketInit_ServerResponse(true, null))); + } + catch (PublicMessageException e) + { + sender.send (SimpleSerializer.serialize(new SRPPackets.PacketInit_ServerResponse(false, e.message))); + } + catch (Exception e) + { + sender.send (SimpleSerializer.serialize(new SRPPackets.PacketInit_ServerResponse(false, null))); + } + stop(); + } + + @Override + public void send(byte[] packet) throws Exception + { + sender.send(srp.streamEncrypt(packet)); + } + +} diff --git a/java/core/src/core/srp/server/SRPServerUserSessionDb.java b/java/core/src/core/srp/server/SRPServerUserSessionDb.java new file mode 100644 index 0000000..bcf68a4 --- /dev/null +++ b/java/core/src/core/srp/server/SRPServerUserSessionDb.java @@ -0,0 +1,23 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.server; + +import java.math.BigInteger; + +import core.util.Pair; +import core.util.Triple; + + +public interface SRPServerUserSessionDb +{ + public void rateLimitFailure (String userName) throws Exception; + public void markFailure (String userName) throws Exception; + + public Triple getUserVVS (String userName) throws Exception; + + public void testCreate (String version, String userName) throws Exception; + public void createUser (String version, String userName, BigInteger v, BigInteger s, byte[] extra) throws Exception; +} diff --git a/java/core/src/core/srp/test/PBETest.java b/java/core/src/core/srp/test/PBETest.java new file mode 100644 index 0000000..fb1ef77 --- /dev/null +++ b/java/core/src/core/srp/test/PBETest.java @@ -0,0 +1,57 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.srp.test; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Random; + +import core.exceptions.CryptoException; +import core.crypt.KeyPairFromPassword; +import core.crypt.KeyPairFromPasswordCryptor; +import core.crypt.PBE; +import core.util.Streams; + + +public class PBETest +{ + public static void main (String[] args) throws CryptoException, IOException + { + Random random = new Random(); + + String password = "TESTPASSWORD"; + + String s = "I am a string which is not to short not to long. I really should be much much larger. " + + "but what can I do. I wonder, in reality this string would be a very long random number. "; + + for (int i=0; i<50; ++i) + s += BigInteger.valueOf(Math.abs(random.nextLong())).toString(32); + + KeyPairFromPassword keyPair = new KeyPairFromPassword(password); + KeyPairFromPasswordCryptor cryptor = new KeyPairFromPasswordCryptor(keyPair); + byte[] verifier = keyPair.getVerifier(); + + System.out.println(new BigInteger(verifier)); + + byte[] e = cryptor.encrypt(s.getBytes()); + + FileOutputStream fe = new FileOutputStream("PBETest.enc"); + fe.write(e); + fe.flush(); + + FileOutputStream fp = new FileOutputStream("PBETest.plain"); + fp.write(e); + fp.flush(); + + byte[] in = Streams.readFullyBytes(new FileInputStream("PBETest.enc")); + String match = new String(cryptor.decrypt(in)); + + if (match.equals(s)) + System.out.println("decryption succeeded"); + } +} diff --git a/java/core/src/core/swing/CheckListener.java b/java/core/src/core/swing/CheckListener.java new file mode 100644 index 0000000..cab9e83 --- /dev/null +++ b/java/core/src/core/swing/CheckListener.java @@ -0,0 +1,31 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.swing; + +import javax.swing.SwingUtilities; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +public class CheckListener implements DocumentListener +{ + Checker checker; + + public CheckListener (Checker checker) + { + this.checker = checker; + } + + public void onChange () + { + SwingUtilities.invokeLater(new Runnable() { + public void run() { checker.onCheck(); } + }); + } + + public void changedUpdate(DocumentEvent e) { onChange(); } + public void insertUpdate(DocumentEvent e) { onChange(); } + public void removeUpdate(DocumentEvent e) { onChange(); } +} diff --git a/java/core/src/core/swing/Checker.java b/java/core/src/core/swing/Checker.java new file mode 100644 index 0000000..7848852 --- /dev/null +++ b/java/core/src/core/swing/Checker.java @@ -0,0 +1,11 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.swing; + +public interface Checker +{ + public void onCheck (); +} diff --git a/java/core/src/core/swing/DimensionCalculator.java b/java/core/src/core/swing/DimensionCalculator.java new file mode 100644 index 0000000..9052d2d --- /dev/null +++ b/java/core/src/core/swing/DimensionCalculator.java @@ -0,0 +1,16 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.swing; + +import java.awt.Dimension; + +public class DimensionCalculator +{ + public Dimension calculate (Dimension dimension) + { + return dimension; + } +} diff --git a/java/core/src/core/swing/FileFilterStandard.java b/java/core/src/core/swing/FileFilterStandard.java new file mode 100644 index 0000000..6fc9f0b --- /dev/null +++ b/java/core/src/core/swing/FileFilterStandard.java @@ -0,0 +1,38 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.swing; + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +public class FileFilterStandard extends FileFilter +{ + + String name, extension; + + public FileFilterStandard(String name, String extension) + { + this.name = name; + this.extension = extension; + } + + public boolean accept(File f) + { + if (f.isDirectory()) + { + return true; + } + + return (f.getPath().toLowerCase().endsWith(extension)); + } + + // The description of this filter + public String getDescription() + { + return name; + } +} \ No newline at end of file diff --git a/java/core/src/core/swing/RedirectStreams.java b/java/core/src/core/swing/RedirectStreams.java new file mode 100644 index 0000000..2b43013 --- /dev/null +++ b/java/core/src/core/swing/RedirectStreams.java @@ -0,0 +1,81 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.swing; + +import java.awt.TextArea; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; + +public class RedirectStreams +{ + JTextArea textArea; + + PrintStream savedOutput; + PrintStream savedError; + PrintStream out; + + public RedirectStreams(JTextArea textArea) + { + this.textArea = textArea; + OutputStream os = + new OutputStream() { + @Override + public void write(int b) throws IOException + { + updateTextArea(String.valueOf((char) b)); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException + { + updateTextArea(new String(b, off, len)); + } + + @Override + public void write(byte[] b) throws IOException + { + write(b, 0, b.length); + } + }; + + out = new PrintStream(os, true); + } + + private void updateTextArea(final String text) + { + SwingUtilities.invokeLater(new Runnable() { + public void run() + { + textArea.append(text); + } + }); + } + + public void redirectSystem() + { + savedOutput = System.out; + savedError = System.err; + + System.setOut(out); + System.setErr(out); + } + + public PrintStream getOut () + { + return out; + } + + public void restoreSystem () + { + System.setOut(savedOutput); + System.setErr(savedError); + } + +} diff --git a/java/core/src/core/swing/SpringLayoutSize.java b/java/core/src/core/swing/SpringLayoutSize.java new file mode 100644 index 0000000..e83bfc9 --- /dev/null +++ b/java/core/src/core/swing/SpringLayoutSize.java @@ -0,0 +1,72 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.swing; + +import java.awt.Container; +import java.awt.Dimension; + +import javax.swing.SpringLayout; + +public class SpringLayoutSize extends SpringLayout +{ + Dimension dimension; + DimensionCalculator dimensionCalculator; + int depth; + + public SpringLayoutSize (int depth, Dimension dimension, DimensionCalculator dimensionCalculator) + { + this.dimension = dimension; + this.depth = depth; + this.dimensionCalculator = dimensionCalculator; + } + + public SpringLayoutSize (int depth, Dimension dimension) + { + this(depth, dimension, null); + } + + public Container getNestedParent(Container parent) + { + for (int i=1; i 0 ? dimension.width : nestedParent.getSize().width, + dimension.height > 0 ? dimension.height : nestedParent.getSize().height + ); + + if (dimensionCalculator != null) + result = dimensionCalculator.calculate(result); + + return result; + } + + @Override + public Dimension maximumLayoutSize(Container parent) + { + return calculateDimension(parent); + } + + @Override + public Dimension minimumLayoutSize(Container parent) + { + return calculateDimension(parent); + } + + @Override + public Dimension preferredLayoutSize(Container parent) + { + return calculateDimension(parent); + } +} diff --git a/java/core/src/core/util/Arrays.java b/java/core/src/core/util/Arrays.java new file mode 100644 index 0000000..3213698 --- /dev/null +++ b/java/core/src/core/util/Arrays.java @@ -0,0 +1,73 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.ArrayList; +import java.util.List; + +public class Arrays +{ + public static T firstOrNull (T[] t) + { + if (t != null && t.length > 0) + return t[0]; + + return null; + } + + public static byte[] generate (int length, int j) + { + byte[] v = new byte[length]; + for (int i=0; i> 4; + + result += decoder.charAt(nibbleHi); + result += decoder.charAt(nibbleLo); + } + + return result; + } +} diff --git a/java/core/src/core/util/Base64.java b/java/core/src/core/util/Base64.java new file mode 100644 index 0000000..adaa11b --- /dev/null +++ b/java/core/src/core/util/Base64.java @@ -0,0 +1,68 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import core.callback.Callback; +import core.callback.CallbackDefault; + +public class Base64 +{ + public static String encode(byte[] bytes) + { + return Strings.toString(org.bc.util.encoders.Base64.encode(bytes)); + } + + public static byte[] decode(String b64) + { + return org.bc.util.encoders.Base64.decode(Strings.toBytes(b64)); + } + + public static byte[] encodeBytes(byte[] bytes) + { + return org.bc.util.encoders.Base64.encode(bytes); + } + + public static byte[] decodeBytes(byte[] b64) + { + return org.bc.util.encoders.Base64.decode(b64); + } + + public static Callback decode_() + { + return new CallbackDefault() { + public void onSuccess(Object... arguments) throws Exception { + next(Base64.decode((String)arguments[0])); + } + }; + } + + public static Callback encode_() + { + return new CallbackDefault() { + public void onSuccess(Object... arguments) throws Exception { + next(Base64.encode((byte[])arguments[0])); + } + }; + } + + public static Callback decodeBytes_() + { + return new CallbackDefault() { + public void onSuccess(Object... arguments) throws Exception { + next(Base64.decodeBytes((byte[])arguments[0])); + } + }; + } + + public static Callback encodeBytes_() + { + return new CallbackDefault() { + public void onSuccess(Object... arguments) throws Exception { + next(Base64.encodeBytes((byte[])arguments[0])); + } + }; + } +} diff --git a/java/core/src/core/util/Block.java b/java/core/src/core/util/Block.java new file mode 100644 index 0000000..2c3032e --- /dev/null +++ b/java/core/src/core/util/Block.java @@ -0,0 +1,20 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.Serializable; + +public class Block implements Serializable +{ + private static final long serialVersionUID = 1L; + + public byte[] bytes; + + public Block (byte[] bytes) + { + this.bytes = bytes; + } +} diff --git a/java/core/src/core/util/CallSingle.java b/java/core/src/core/util/CallSingle.java new file mode 100644 index 0000000..3daa7eb --- /dev/null +++ b/java/core/src/core/util/CallSingle.java @@ -0,0 +1,15 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import org.timepedia.exporter.client.Export; +import org.timepedia.exporter.client.Exportable; + +@Export +public interface CallSingle extends Exportable +{ + public T invoke(V v) throws Exception; +} diff --git a/java/core/src/core/util/CallSingleWithVariables.java b/java/core/src/core/util/CallSingleWithVariables.java new file mode 100644 index 0000000..dde01bf --- /dev/null +++ b/java/core/src/core/util/CallSingleWithVariables.java @@ -0,0 +1,24 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import core.util.CallSingle; + +abstract class CallSingleWithVariables implements CallSingle +{ + protected Object[] v; + + public CallSingleWithVariables(Object... v) + { + this.v = v; + } + + @SuppressWarnings("unchecked") + public T V(int i) + { + return (T)v[i]; + } +} diff --git a/java/core/src/core/util/Characters.java b/java/core/src/core/util/Characters.java new file mode 100644 index 0000000..1ffafea --- /dev/null +++ b/java/core/src/core/util/Characters.java @@ -0,0 +1,20 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +public class Characters { + + public static boolean isWhitespace(char c) + { + return " \t\n\r".indexOf(c)!=-1; + } + + public static boolean isNumber(char c) + { + return "0123456789".indexOf(c)!=-1; + } + +} diff --git a/java/core/src/core/util/CircularByteBuffer.java b/java/core/src/core/util/CircularByteBuffer.java new file mode 100644 index 0000000..79e72c6 --- /dev/null +++ b/java/core/src/core/util/CircularByteBuffer.java @@ -0,0 +1,809 @@ +/* + * Circular Byte Buffer + * Copyright (C) 2002-2010 Stephen Ostermiller + * http://ostermiller.org/contact.pl?regarding=Java+Utilities + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * See LICENSE.txt for details. + */ +package core.util; + +import java.io.*; + +/** + * Implements the Circular Buffer producer/consumer model for bytes. + * More information about this class is available from ostermiller.org. + *

+ * Using this class is a simpler alternative to using a PipedInputStream + * and a PipedOutputStream. PipedInputStreams and PipedOutputStreams don't support the + * mark operation, don't allow you to control buffer sizes that they use, + * and have a more complicated API that requires instantiating two + * classes and connecting them. + *

+ * This class is thread safe. + * + * @see CircularCharBuffer + * @see CircularObjectBuffer + * + * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities + * @since ostermillerutils 1.00.00 + */ +public class CircularByteBuffer { + + /** + * The default size for a circular byte buffer. + * + * @since ostermillerutils 1.00.00 + */ + private final static int DEFAULT_SIZE = 1024; + + /** + * A buffer that will grow as things are added. + * + * @since ostermillerutils 1.00.00 + */ + public final static int INFINITE_SIZE = -1; + + /** + * The circular buffer. + *

+ * The actual capacity of the buffer is one less than the actual length + * of the buffer so that an empty and a full buffer can be + * distinguished. An empty buffer will have the markPostion and the + * writePosition equal to each other. A full buffer will have + * the writePosition one less than the markPostion. + *

+ * There are three important indexes into the buffer: + * The readPosition, the writePosition, and the markPosition. + * If the InputStream has never been marked, the readPosition and + * the markPosition should always be the same. The bytes + * available to be read go from the readPosition to the writePosition, + * wrapping around the end of the buffer. The space available for writing + * goes from the write position to one less than the markPosition, + * wrapping around the end of the buffer. The bytes that have + * been saved to support a reset() of the InputStream go from markPosition + * to readPosition, wrapping around the end of the buffer. + * + * @since ostermillerutils 1.00.00 + */ + protected byte[] buffer; + /** + * Index of the first byte available to be read. + * + * @since ostermillerutils 1.00.00 + */ + protected volatile int readPosition = 0; + /** + * Index of the first byte available to be written. + * + * @since ostermillerutils 1.00.00 + */ + protected volatile int writePosition = 0; + /** + * Index of the first saved byte. (To support stream marking.) + * + * @since ostermillerutils 1.00.00 + */ + protected volatile int markPosition = 0; + /** + * Number of bytes that have to be saved + * to support mark() and reset() on the InputStream. + * + * @since ostermillerutils 1.00.00 + */ + protected volatile int markSize = 0; + /** + * If this buffer is infinite (should resize itself when full) + * + * @since ostermillerutils 1.00.00 + */ + protected volatile boolean infinite = false; + /** + * True if a write to a full buffer should block until the buffer + * has room, false if the write method should throw an IOException + * + * @since ostermillerutils 1.00.00 + */ + protected boolean blockingWrite = true; + /** + * The InputStream that can empty this buffer. + * + * @since ostermillerutils 1.00.00 + */ + protected InputStream in = new CircularByteBufferInputStream(); + /** + * true if the close() method has been called on the InputStream + * + * @since ostermillerutils 1.00.00 + */ + protected boolean inputStreamClosed = false; + /** + * The OutputStream that can fill this buffer. + * + * @since ostermillerutils 1.00.00 + */ + protected OutputStream out = new CircularByteBufferOutputStream(); + /** + * true if the close() method has been called on the OutputStream + * + * @since ostermillerutils 1.00.00 + */ + protected boolean outputStreamClosed = false; + + /** + * Make this buffer ready for reuse. The contents of the buffer + * will be cleared and the streams associated with this buffer + * will be reopened if they had been closed. + * + * @since ostermillerutils 1.00.00 + */ + public void clear(){ + synchronized (this){ + readPosition = 0; + writePosition = 0; + markPosition = 0; + outputStreamClosed = false; + inputStreamClosed = false; + } + } + + /** + * Retrieve a OutputStream that can be used to fill + * this buffer. + *

+ * Write methods may throw a BufferOverflowException if + * the buffer is not large enough. A large enough buffer + * size must be chosen so that this does not happen or + * the caller must be prepared to catch the exception and + * try again once part of the buffer has been consumed. + * + * + * @return the producer for this buffer. + * + * @since ostermillerutils 1.00.00 + */ + public OutputStream getOutputStream(){ + return out; + } + + /** + * Retrieve a InputStream that can be used to empty + * this buffer. + *

+ * This InputStream supports marks at the expense + * of the buffer size. + * + * @return the consumer for this buffer. + * + * @since ostermillerutils 1.00.00 + */ + public InputStream getInputStream(){ + return in; + } + + /** + * Get number of bytes that are available to be read. + *

+ * Note that the number of bytes available plus + * the number of bytes free may not add up to the + * capacity of this buffer, as the buffer may reserve some + * space for other purposes. + * + * @return the size in bytes of this buffer + * + * @since ostermillerutils 1.00.00 + */ + public int getAvailable(){ + synchronized (this){ + return available(); + } + } + + /** + * Get the number of bytes this buffer has free for + * writing. + *

+ * Note that the number of bytes available plus + * the number of bytes free may not add up to the + * capacity of this buffer, as the buffer may reserve some + * space for other purposes. + * + * @return the available space in bytes of this buffer + * + * @since ostermillerutils 1.00.00 + */ + public int getSpaceLeft(){ + synchronized (this){ + return spaceLeft(); + } + } + + /** + * Get the capacity of this buffer. + *

+ * Note that the number of bytes available plus + * the number of bytes free may not add up to the + * capacity of this buffer, as the buffer may reserve some + * space for other purposes. + * + * @return the size in bytes of this buffer + * + * @since ostermillerutils 1.00.00 + */ + public int getSize(){ + synchronized (this){ + return buffer.length; + } + } + + /** + * double the size of the buffer + * + * @since ostermillerutils 1.00.00 + */ + private void resize(){ + byte[] newBuffer = new byte[buffer.length * 2]; + int marked = marked(); + int available = available(); + if (markPosition <= writePosition){ + // any space between the mark and + // the first write needs to be saved. + // In this case it is all in one piece. + int length = writePosition - markPosition; + System.arraycopy(buffer, markPosition, newBuffer, 0, length); + } else { + int length1 = buffer.length - markPosition; + System.arraycopy(buffer, markPosition, newBuffer, 0, length1); + int length2 = writePosition; + System.arraycopy(buffer, 0, newBuffer, length1, length2); + } + buffer = newBuffer; + markPosition = 0; + readPosition = marked; + writePosition = marked + available; + } + + /** + * Space available in the buffer which can be written. + * + * @since ostermillerutils 1.00.00 + */ + private int spaceLeft(){ + if (writePosition < markPosition){ + // any space between the first write and + // the mark except one byte is available. + // In this case it is all in one piece. + return (markPosition - writePosition - 1); + } + // space at the beginning and end. + return ((buffer.length - 1) - (writePosition - markPosition)); + } + + /** + * Bytes available for reading. + * + * @since ostermillerutils 1.00.00 + */ + private int available(){ + if (readPosition <= writePosition){ + // any space between the first read and + // the first write is available. In this case i + // is all in one piece. + return (writePosition - readPosition); + } + // space at the beginning and end. + return (buffer.length - (readPosition - writePosition)); + } + + /** + * Bytes saved for supporting marks. + * + * @since ostermillerutils 1.00.00 + */ + private int marked(){ + if (markPosition <= readPosition){ + // any space between the markPosition and + // the first write is marked. In this case i + // is all in one piece. + return (readPosition - markPosition); + } + // space at the beginning and end. + return (buffer.length - (markPosition - readPosition)); + } + + /** + * If we have passed the markSize reset the + * mark so that the space can be used. + * + * @since ostermillerutils 1.00.00 + */ + private void ensureMark(){ + if (marked() > markSize){ + markPosition = readPosition; + markSize = 0; + } + } + + /** + * Create a new buffer with a default capacity. + * Writing to a full buffer will block until space + * is available rather than throw an exception. + * + * @since ostermillerutils 1.00.00 + */ + public CircularByteBuffer(){ + this (DEFAULT_SIZE, true); + } + + /** + * Create a new buffer with given capacity. + * Writing to a full buffer will block until space + * is available rather than throw an exception. + *

+ * Note that the buffer may reserve some bytes for + * special purposes and capacity number of bytes may + * not be able to be written to the buffer. + *

+ * Note that if the buffer is of INFINITE_SIZE it will + * neither block or throw exceptions, but rather grow + * without bound. + * + * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE. + * + * @since ostermillerutils 1.00.00 + */ + public CircularByteBuffer(int size){ + this (size, true); + } + + /** + * Create a new buffer with a default capacity and + * given blocking behavior. + * + * @param blockingWrite true writing to a full buffer should block + * until space is available, false if an exception should + * be thrown instead. + * + * @since ostermillerutils 1.00.00 + */ + public CircularByteBuffer(boolean blockingWrite){ + this (DEFAULT_SIZE, blockingWrite); + } + + /** + * Create a new buffer with the given capacity and + * blocking behavior. + *

+ * Note that the buffer may reserve some bytes for + * special purposes and capacity number of bytes may + * not be able to be written to the buffer. + *

+ * Note that if the buffer is of INFINITE_SIZE it will + * neither block or throw exceptions, but rather grow + * without bound. + * + * @param size desired capacity of the buffer in bytes or CircularByteBuffer.INFINITE_SIZE. + * @param blockingWrite true writing to a full buffer should block + * until space is available, false if an exception should + * be thrown instead. + * + * @since ostermillerutils 1.00.00 + */ + public CircularByteBuffer(int size, boolean blockingWrite){ + if (size == INFINITE_SIZE){ + buffer = new byte[DEFAULT_SIZE]; + infinite = true; + } else { + buffer = new byte[size]; + infinite = false; + } + this.blockingWrite = blockingWrite; + } + + /** + * Class for reading from a circular byte buffer. + * + * @since ostermillerutils 1.00.00 + */ + protected class CircularByteBufferInputStream extends InputStream { + + /** + * Returns the number of bytes that can be read (or skipped over) from this + * input stream without blocking by the next caller of a method for this input + * stream. The next caller might be the same thread or or another thread. + * + * @return the number of bytes that can be read from this input stream without blocking. + * @throws IOException if the stream is closed. + * + * @since ostermillerutils 1.00.00 + */ + @Override public int available() throws IOException { + synchronized (CircularByteBuffer.this){ + if (inputStreamClosed) throw new IOException("InputStream has been closed, it is not ready."); + return (CircularByteBuffer.this.available()); + } + } + + /** + * Close the stream. Once a stream has been closed, further read(), available(), + * mark(), or reset() invocations will throw an IOException. Closing a + * previously-closed stream, however, has no effect. + * + * @throws IOException never. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void close() throws IOException { + synchronized (CircularByteBuffer.this){ + inputStreamClosed = true; + } + } + + /** + * Mark the present position in the stream. Subsequent calls to reset() will + * attempt to reposition the stream to this point. + *

+ * The readAheadLimit must be less than the size of circular buffer, otherwise + * this method has no effect. + * + * @param readAheadLimit Limit on the number of bytes that may be read while + * still preserving the mark. After reading this many bytes, attempting to + * reset the stream will fail. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void mark(int readAheadLimit) { + synchronized (CircularByteBuffer.this){ + //if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot mark a closed InputStream."); + if (buffer.length - 1 > readAheadLimit) { + markSize = readAheadLimit; + markPosition = readPosition; + } + } + } + + /** + * Tell whether this stream supports the mark() operation. + * + * @return true, mark is supported. + * + * @since ostermillerutils 1.00.00 + */ + @Override public boolean markSupported() { + return true; + } + + /** + * Read a single byte. + * This method will block until a byte is available, an I/O error occurs, + * or the end of the stream is reached. + * + * @return The byte read, as an integer in the range 0 to 255 (0x00-0xff), + * or -1 if the end of the stream has been reached + * @throws IOException if the stream is closed. + * + * @since ostermillerutils 1.00.00 + */ + @Override public int read() throws IOException { + while (true){ + synchronized (CircularByteBuffer.this){ + if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot read from a closed InputStream."); + int available = CircularByteBuffer.this.available(); + if (available > 0){ + int result = buffer[readPosition] & 0xff; + readPosition++; + if (readPosition == buffer.length){ + readPosition = 0; + } + ensureMark(); + return result; + } else if (outputStreamClosed){ + return -1; + } + } + try { + Thread.sleep(100); + } catch(Exception x){ + throw new IOException("Blocking read operation interrupted."); + } + } + } + + /** + * Read bytes into an array. + * This method will block until some input is available, + * an I/O error occurs, or the end of the stream is reached. + * + * @param cbuf Destination buffer. + * @return The number of bytes read, or -1 if the end of + * the stream has been reached + * @throws IOException if the stream is closed. + * + * @since ostermillerutils 1.00.00 + */ + @Override public int read(byte[] cbuf) throws IOException { + return read(cbuf, 0, cbuf.length); + } + + /** + * Read bytes into a portion of an array. + * This method will block until some input is available, + * an I/O error occurs, or the end of the stream is reached. + * + * @param cbuf Destination buffer. + * @param off Offset at which to start storing bytes. + * @param len Maximum number of bytes to read. + * @return The number of bytes read, or -1 if the end of + * the stream has been reached + * @throws IOException if the stream is closed. + * + * @since ostermillerutils 1.00.00 + */ + @Override public int read(byte[] cbuf, int off, int len) throws IOException { + while (true){ + synchronized (CircularByteBuffer.this){ + if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot read from a closed InputStream."); + int available = CircularByteBuffer.this.available(); + if (available > 0){ + int length = Math.min(len, available); + int firstLen = Math.min(length, buffer.length - readPosition); + int secondLen = length - firstLen; + System.arraycopy(buffer, readPosition, cbuf, off, firstLen); + if (secondLen > 0){ + System.arraycopy(buffer, 0, cbuf, off+firstLen, secondLen); + readPosition = secondLen; + } else { + readPosition += length; + } + if (readPosition == buffer.length) { + readPosition = 0; + } + ensureMark(); + return length; + } else if (outputStreamClosed){ + return -1; + } + } + try { + Thread.sleep(100); + } catch(Exception x){ + throw new IOException("Blocking read operation interrupted."); + } + } + } + + /** + * Reset the stream. + * If the stream has been marked, then attempt to reposition i + * at the mark. If the stream has not been marked, or more bytes + * than the readAheadLimit have been read, this method has no effect. + * + * @throws IOException if the stream is closed. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void reset() throws IOException { + synchronized (CircularByteBuffer.this){ + if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot reset a closed InputStream."); + readPosition = markPosition; + } + } + + /** + * Skip bytes. + * This method will block until some bytes are available, + * an I/O error occurs, or the end of the stream is reached. + * + * @param n The number of bytes to skip + * @return The number of bytes actually skipped + * @throws IllegalArgumentException if n is negative. + * @throws IOException if the stream is closed. + * + * @since ostermillerutils 1.00.00 + */ + @Override public long skip(long n) throws IOException, IllegalArgumentException { + while (true){ + synchronized (CircularByteBuffer.this){ + if (inputStreamClosed) throw new IOException("InputStream has been closed; cannot skip bytes on a closed InputStream."); + int available = CircularByteBuffer.this.available(); + if (available > 0){ + int length = Math.min((int)n, available); + int firstLen = Math.min(length, buffer.length - readPosition); + int secondLen = length - firstLen; + if (secondLen > 0){ + readPosition = secondLen; + } else { + readPosition += length; + } + if (readPosition == buffer.length) { + readPosition = 0; + } + ensureMark(); + return length; + } else if (outputStreamClosed){ + return 0; + } + } + try { + Thread.sleep(100); + } catch(Exception x){ + throw new IOException("Blocking read operation interrupted."); + } + } + } + } + + /** + * Class for writing to a circular byte buffer. + * If the buffer is full, the writes will either block + * until there is some space available or throw an IOException + * based on the CircularByteBuffer's preference. + * + * @since ostermillerutils 1.00.00 + */ + protected class CircularByteBufferOutputStream extends OutputStream { + + /** + * Close the stream, flushing it first. + * This will cause the InputStream associated with this circular buffer + * to read its last bytes once it empties the buffer. + * Once a stream has been closed, further write() or flush() invocations + * will cause an IOException to be thrown. Closing a previously-closed stream, + * however, has no effect. + * + * @throws IOException never. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void close() throws IOException { + synchronized (CircularByteBuffer.this){ + if (!outputStreamClosed){ + flush(); + } + outputStreamClosed = true; + } + } + + /** + * Flush the stream. + * + * @throws IOException if the stream is closed. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void flush() throws IOException { + synchronized (CircularByteBuffer.this){ + if (outputStreamClosed) throw new IOException("OutputStream has been closed; cannot flush a closed OutputStream."); + if (inputStreamClosed) throw new IOException("Buffer closed by inputStream; cannot flush."); + } + // this method needs to do nothing + } + + /** + * Write an array of bytes. + * If the buffer allows blocking writes, this method will block until + * all the data has been written rather than throw an IOException. + * + * @param cbuf Array of bytes to be written + * @throws BufferOverflowException if buffer does not allow blocking writes + * and the buffer is full. If the exception is thrown, no data + * will have been written since the buffer was set to be non-blocking. + * @throws IOException if the stream is closed, or the write is interrupted. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void write(byte[] cbuf) throws IOException { + write(cbuf, 0, cbuf.length); + } + + /** + * Write a portion of an array of bytes. + * If the buffer allows blocking writes, this method will block until + * all the data has been written rather than throw an IOException. + * + * @param cbuf Array of bytes + * @param off Offset from which to start writing bytes + * @param len - Number of bytes to write + * @throws BufferOverflowException if buffer does not allow blocking writes + * and the buffer is full. If the exception is thrown, no data + * will have been written since the buffer was set to be non-blocking. + * @throws IOException if the stream is closed, or the write is interrupted. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void write(byte[] cbuf, int off, int len) throws IOException { + while (len > 0){ + synchronized (CircularByteBuffer.this){ + if (outputStreamClosed) throw new IOException("OutputStream has been closed; cannot write to a closed OutputStream."); + if (inputStreamClosed) throw new IOException("Buffer closed by InputStream; cannot write to a closed buffer."); + int spaceLeft = spaceLeft(); + while (infinite && spaceLeft < len){ + resize(); + spaceLeft = spaceLeft(); + } + if (!blockingWrite && spaceLeft < len) throw new IOException("CircularByteBuffer is full; cannot write " + len + " bytes"); + int realLen = Math.min(len, spaceLeft); + int firstLen = Math.min(realLen, buffer.length - writePosition); + int secondLen = Math.min(realLen - firstLen, buffer.length - markPosition - 1); + int written = firstLen + secondLen; + if (firstLen > 0){ + System.arraycopy(cbuf, off, buffer, writePosition, firstLen); + } + if (secondLen > 0){ + System.arraycopy(cbuf, off+firstLen, buffer, 0, secondLen); + writePosition = secondLen; + } else { + writePosition += written; + } + if (writePosition == buffer.length) { + writePosition = 0; + } + off += written; + len -= written; + } + if (len > 0){ + try { + Thread.sleep(100); + } catch(Exception x){ + throw new IOException("Waiting for available space in buffer interrupted."); + } + } + } + } + + /** + * Write a single byte. + * The byte to be written is contained in the 8 low-order bits of the + * given integer value; the 24 high-order bits are ignored. + * If the buffer allows blocking writes, this method will block until + * all the data has been written rather than throw an IOException. + * + * @param c number of bytes to be written + * @throws BufferOverflowException if buffer does not allow blocking writes + * and the buffer is full. + * @throws IOException if the stream is closed, or the write is interrupted. + * + * @since ostermillerutils 1.00.00 + */ + @Override public void write(int c) throws IOException { + boolean written = false; + while (!written){ + synchronized (CircularByteBuffer.this){ + if (outputStreamClosed) throw new IOException("OutputStream has been closed; cannot write to a closed OutputStream."); + if (inputStreamClosed) throw new IOException("Buffer closed by InputStream; cannot write to a closed buffer."); + int spaceLeft = spaceLeft(); + while (infinite && spaceLeft < 1){ + resize(); + spaceLeft = spaceLeft(); + } + if (!blockingWrite && spaceLeft < 1) throw new IOException("CircularByteBuffer is full; cannot write 1 byte"); + if (spaceLeft > 0){ + buffer[writePosition] = (byte)(c & 0xff); + writePosition++; + if (writePosition == buffer.length) { + writePosition = 0; + } + written = true; + } + } + if (!written){ + try { + Thread.sleep(100); + } catch(Exception x){ + throw new IOException("Waiting for available space in buffer interrupted."); + } + } + } + } + } +} \ No newline at end of file diff --git a/java/core/src/core/util/Collectionz.java b/java/core/src/core/util/Collectionz.java new file mode 100644 index 0000000..2a4bae4 --- /dev/null +++ b/java/core/src/core/util/Collectionz.java @@ -0,0 +1,98 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class Collectionz +{ + public static List toMutableList(T[] a) + { + ArrayList l = new ArrayList(); + for (T t : a) + l.add(t); + + return l; + } + + public static Collection filterNull (Collection items) + { + List l = new ArrayList(); + + for (T i : items) + if (i != null) + l.add(i); + + return l; + } + + public static Collection filterNull (T... items) + { + List l = new ArrayList(); + + for (T i : items) + if (i != null) + l.add(i); + + return l; + } + + static public void removeByFirst (Collection items, Object remove) + { + Object found = null; + for (Pair x : items) + { + if (x.first.equals(remove)) + { + found = x; + break; + } + } + + if (found != null) + items.remove(found); + } + + static public void removeBySecond (Collection items, Object remove) + { + Object found = null; + for (Pair x : items) + { + if (x.second.equals(remove)) + { + found = x; + break; + } + } + + if (found != null) + items.remove(found); + } + + static public boolean containsByFirst (Collection items, Object remove) + { + for (Pair x : items) + { + if (x.first.equals(remove)) + return true; + } + + return false; + } + + static public boolean containsBySecond (Collection items, Object remove) + { + for (Pair x : items) + { + if (x.second.equals(remove)) + return true; + } + + return false; + } +} diff --git a/java/core/src/core/util/Comparators.java b/java/core/src/core/util/Comparators.java new file mode 100644 index 0000000..a01e6ba --- /dev/null +++ b/java/core/src/core/util/Comparators.java @@ -0,0 +1,96 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.Comparator; + +public class Comparators +{ + public static class SortBySecond implements Comparator> + { + Comparator c; + + public SortBySecond(Comparator c) + { + this.c = c; + } + + @Override + public int compare(Pair p1, Pair p2) + { + return c.compare(p1.second, p2.second); + } + } + + public static class SortByFirst implements Comparator> + { + Comparator c; + + public SortByFirst(Comparator c) + { + this.c = c; + } + + @Override + public int compare(Pair p1, Pair p2) + { + return c.compare(p1.first, p2.first); + } + } + + public static class SortByFirstReverse implements Comparator> + { + Comparator c; + + public SortByFirstReverse(Comparator c) + { + this.c = c; + } + + @Override + public int compare(Pair p1, Pair p2) + { + return c.compare(p2.first, p1.first); + } + } + + public static class SortBySecondNatural> implements Comparator> + { + + @Override + public int compare(Pair o1, Pair o2) + { + return o1.second.compareTo(o2.second); + } + } + + public static class SortByFirstNatural> implements Comparator> + { + @Override + public int compare(Pair o1, Pair o2) + { + return o1.first.compareTo(o2.first); + } + } + + public static class SortBySecondNaturalOpposite> implements Comparator> + { + @Override + public int compare(Pair o1, Pair o2) + { + return o2.second.compareTo(o1.second); + } + } + + public static class SortByFirstNaturalOpposite> implements Comparator> + { + @Override + public int compare(Pair o1, Pair o2) + { + return o2.first.compareTo(o1.first); + } + } +} diff --git a/java/core/src/core/util/DateFormat.java b/java/core/src/core/util/DateFormat.java new file mode 100644 index 0000000..7d2d881 --- /dev/null +++ b/java/core/src/core/util/DateFormat.java @@ -0,0 +1,38 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class DateFormat +{ + SimpleDateFormat sdf; + + public DateFormat(String format) + { + sdf = new SimpleDateFormat(format); + } + + public String format(Date date, int i) + { + sdf.setTimeZone(TimeZone.getTimeZone("GMT-"+i+":00")); + return sdf.format(date); + } + + public String format(Date date) + { + return format(date, 0); + } + + public Date parse(String time) throws ParseException + { + return sdf.parse(time); + } + +} diff --git a/java/core/src/core/util/Environment.java b/java/core/src/core/util/Environment.java new file mode 100644 index 0000000..b7f615e --- /dev/null +++ b/java/core/src/core/util/Environment.java @@ -0,0 +1,111 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import core.connector.FileInfo; +//import core.connector.sync.StoreConnector; + + +public class Environment extends HashMap +{ + private static final long serialVersionUID = 1L; + + public Environment () + { + } + +/* + public static Environment fromStore (StoreConnector connector) throws Exception + { + Environment e = new Environment (); + connector.open(); + e.readFromStore(connector); + connector.close(); + return e; + } + + public void readFromStore (StoreConnector connector) throws Exception + { + List files = connector.listDirectory(""); + for (FileInfo file : files) + { + put(file.relativePath, new String(connector.get(file.relativePath), "UTF-8")); + } + } + + public static void toStore (StoreConnector connector, Environment e) throws Exception + { + connector.open(); + e.writeToStore(connector); + connector.close(); + } + + public void writeToStore (StoreConnector connector) throws Exception + { + for (String key : keySet()) + connector.put(key, Strings.toBytes(this.get(key))); + } +*/ + + public String checkGet (String key) + { + // find a better exception + if (!containsKey(key)) + throw new NullPointerException("Unknown key: " + key); + + return get(key); + } + + public Environment childEnvironment (String key) + { + Environment e = new Environment(); + String prefix = key + "/"; + int prefixLength = prefix.length(); + + for (Map.Entry i : entrySet()) + { + String k = i.getKey(); + if (k.startsWith(prefix)) + { + e.put(k.substring(prefixLength), i.getValue()); + } + } + + return e; + } + + public void addChildEnvironment (String key, Environment e) + { + String prefix = key + "/"; + + for (Map.Entry i : e.entrySet()) + { + String k = i.getKey(); + this.put(prefix + k, i.getValue()); + } + } + + public boolean hasChildEnvironment (String key) + { + String prefix = key + "/"; + int prefixLength = prefix.length(); + + for (Map.Entry i : entrySet()) + { + String k = i.getKey(); + if (k.startsWith(prefix)) + { + return true; + } + } + + return false; + } +} diff --git a/java/core/src/core/util/ExceptionHandler.java b/java/core/src/core/util/ExceptionHandler.java new file mode 100644 index 0000000..75a3b25 --- /dev/null +++ b/java/core/src/core/util/ExceptionHandler.java @@ -0,0 +1,11 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +public interface ExceptionHandler +{ + public void exception (Object ... arguments); +} diff --git a/java/core/src/core/util/ExternalResource.java b/java/core/src/core/util/ExternalResource.java new file mode 100644 index 0000000..f5b163c --- /dev/null +++ b/java/core/src/core/util/ExternalResource.java @@ -0,0 +1,39 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.FileInputStream; +import java.io.InputStream; + +public class ExternalResource +{ + static LogOut log = new LogOut(ExternalResource.class); + + static protected String prefix; + + static { + prefix = System.getProperty("user.home") + "/resources/"; + } + + public static byte[] get(String key) throws Exception + { + String path = prefix + key; + log.debug("get", path); + return Streams.readFullyBytes(new FileInputStream(path)); + } + + public static InputStream getResourceAsStream(Class c, String key) throws Exception + { + String path = prefix + c.getPackage().getName() + "/" + key; + log.debug("getResourceAsStream", path); + return new FileInputStream(path); + } + + public static String getTrimmedString(String key) throws Exception + { + return Strings.toString(get(key)).trim(); + } +} diff --git a/java/core/src/core/util/FastRandom.java b/java/core/src/core/util/FastRandom.java new file mode 100644 index 0000000..281a079 --- /dev/null +++ b/java/core/src/core/util/FastRandom.java @@ -0,0 +1,12 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.Random; + +public class FastRandom extends Random { + +} diff --git a/java/core/src/core/util/FileSystem.java b/java/core/src/core/util/FileSystem.java new file mode 100644 index 0000000..64bddf2 --- /dev/null +++ b/java/core/src/core/util/FileSystem.java @@ -0,0 +1,50 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import core.connector.FileInfo; + +public class FileSystem +{ + public static void listRecursiveWorker (File original, File item, List keys) + { + if (item.isDirectory()) + { + String[] children = item.list(); + for (int i=0; i allFilesFor (File dir) + { + List results = new ArrayList(); + listRecursiveWorker(dir, dir, results); + + return results; + } +} diff --git a/java/core/src/core/util/Http.java b/java/core/src/core/util/Http.java new file mode 100644 index 0000000..69ee00e --- /dev/null +++ b/java/core/src/core/util/Http.java @@ -0,0 +1,65 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URLConnection; +import java.net.URLEncoder; + +public class Http +{ + public static String INTERNAL_ERROR = "An internal error occurred."; + + public static void mapParameters (URLConnection c, String... args) + { + for (int i=1; i readFully(HttpURLConnection c) + { + try + { + try + { + return new Pair(Streams.readFullyBytes(c.getInputStream()),null); + } + catch (IOException e) + { + return new Pair(Streams.readFullyBytes(c.getErrorStream()),e); + } + } + catch (IOException e) + { + return new Pair(null, e); + } + } +} diff --git a/java/core/src/core/util/HttpDelegate.java b/java/core/src/core/util/HttpDelegate.java new file mode 100644 index 0000000..08eb046 --- /dev/null +++ b/java/core/src/core/util/HttpDelegate.java @@ -0,0 +1,36 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import core.callback.Callback; +import core.callback.CallbackDefault; + +public abstract class HttpDelegate +{ + public static final String GET = "GET"; + public static final String PUT = "PUT"; + public static final String POST = "POST"; + public static final String DELETE = "DELETE"; + + public abstract void execute (String action, String url, String[][] headers, boolean binaryInput, boolean binaryOutput, byte[] contents, Callback callback); + + public Callback execute_(String action, String url, String[][]headers,boolean binaryInput, boolean binaryOutput) + { + return new CallbackDefault(action, url, headers, binaryInput, binaryOutput) { + public void onSuccess(Object... arguments) throws Exception { + String action = V(0); + String url = V(1); + String[][] headers = V(2); + boolean binaryInput = (Boolean)V(3); + boolean binaryOutput = (Boolean)V(4); + + byte[] content = (byte[])arguments[0]; + + execute(action, url, headers, binaryInput, binaryOutput, content, callback); + } + }; + } +} diff --git a/java/core/src/core/util/HttpDelegateJava.java b/java/core/src/core/util/HttpDelegateJava.java new file mode 100644 index 0000000..74633a6 --- /dev/null +++ b/java/core/src/core/util/HttpDelegateJava.java @@ -0,0 +1,88 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.BufferedOutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import core.callback.Callback; + +public class HttpDelegateJava extends HttpDelegate +{ + @Override + public void execute(String action, String url, String[][] headers, boolean binaryInput, boolean binaryOutput, byte[] contents, Callback callback) + { + try + { + URLConnection urlConnection = new URL(url).openConnection(); + + HttpURLConnection httpCon = (HttpURLConnection) urlConnection; + + httpCon.setRequestMethod(action); + if (headers != null) + { + for (String[] s : headers) + { + httpCon.setRequestProperty(s[0], s[1]); + } + } + + if (contents != null) + { + httpCon.setDoOutput(true); + httpCon.setRequestMethod("PUT"); + httpCon.setRequestProperty("CONTENT-LENGTH", "" + contents.length); + + BufferedOutputStream os = new BufferedOutputStream(urlConnection.getOutputStream()); + os.write(contents); + os.close(); + } + + byte[] bytes = Streams.readFullyBytes(urlConnection.getInputStream()); + + ArrayList responseHeaders = new ArrayList(); + for (Entry> i : httpCon.getHeaderFields().entrySet()) + { + for (String j : i.getValue()) + { + String key = i.getKey(); + String value = j; + + if (key == null) + { + key = value; + value = null; + } + + responseHeaders.add(new String[] { key, value }); + } + } + + String[][] rh = new String[responseHeaders.size()][]; + int j=0; + for (String[] i : responseHeaders) + rh[j++] = i; + + if (binaryOutput) + callback.invoke(bytes, rh); + else + { + String s = new String(bytes); + callback.invoke(s, rh); + } + + } + catch (Exception e) + { + callback.invoke(e); + } + } +} diff --git a/java/core/src/core/util/InternalResource.java b/java/core/src/core/util/InternalResource.java new file mode 100644 index 0000000..ffe9a7c --- /dev/null +++ b/java/core/src/core/util/InternalResource.java @@ -0,0 +1,27 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +public class InternalResource +{ + public static InputStream getResourceAsStream (Class c, String p) + { + return c.getResourceAsStream(p); + } + + public static native String getResource(String r) /*-{ + return $wnd.resource_acquire(r); + }-*/; + + public static InputStream getResourceAsStream_NO (Class c, String p) + { + return new ByteArrayInputStream(Base64.decode(getResource(c.getName() + "." + p))); + } + +} diff --git a/java/core/src/core/util/JSONRegistry.java b/java/core/src/core/util/JSONRegistry.java new file mode 100644 index 0000000..54d5b0b --- /dev/null +++ b/java/core/src/core/util/JSONRegistry.java @@ -0,0 +1,31 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONObject; + + +public class JSONRegistry +{ + static Map> serializers = + new HashMap>(); + + static Map> deserializers = + new HashMap>(); + + public static void register ( + String className, + CallSingle serializer, + CallSingle deserializer + ) + { + serializers.put(className, serializer); + deserializers.put(className, deserializer); + } +} diff --git a/java/core/src/core/util/JSONSerializer.java b/java/core/src/core/util/JSONSerializer.java new file mode 100644 index 0000000..aed501e --- /dev/null +++ b/java/core/src/core/util/JSONSerializer.java @@ -0,0 +1,202 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.json.JSONObject; + + +// http://stackoverflow.com/questions/2213734/using-gson-library-in-gwt-client-code +public class JSONSerializer +{ + static LogNull log = new LogNull(JSONSerializer.class); + + public static byte[] serialize (T o) throws IOException + { + try + { + if (o == null) + return Strings.toBytes("null"); + + String className = o.getClass().getName(); + + Object serialization = null; + if (className.equals("java.lang.String")) + serialization = o; + else + serialization = serialize(className, o); + + byte[] result = Strings.toBytes(className + "!" + serialization); + log.debug("serialize: ", Strings.toString(result)); + return result; + } + catch (Exception e) + { + throw new IOException(e); + } + } + + @SuppressWarnings("unchecked") + public static T deserialize (byte[] bytes) throws IOException + { + String s = Strings.toString(bytes); + log.debug("deserialize: ",s); + + if (s.equals("null")) + return null; + + int notPosition = s.indexOf('!'); + if (notPosition == -1) + return null; + + String className = s.substring(0, notPosition); + String serialization = s.substring(notPosition+1); + + try + { + if (className.equals("java.lang.String")) + return (T)serialization; + + return (T)deserialize(className, new JSONObject(serialization)); + } + catch (Exception e) + { + throw new IOException(e); + } + } + + private static JSONObject serialize (String className, Object o) throws Exception + { + CallSingle serializer = JSONRegistry.serializers.get(className); + if (serializer == null) + throw new ClassNotFoundException("Unknown class " + className); + + return serializer.invoke(o); + } + + private static Object deserialize (String className, JSONObject o) throws Exception + { + CallSingle deserializer = JSONRegistry.deserializers.get(className); + if (deserializer == null) + throw new ClassNotFoundException("Unknown class " + className); + + return deserializer.invoke(o); + } + + + static { + JSONRegistry.register ( + "java.util.HashMap", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + Map p = (Map)v; + for (Entry e : p.entrySet()) + { + log.debug(e.getKey(), e.getValue()); + object.put(e.getKey(), e.getValue()); + } + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + Map m = new HashMap(); + + @SuppressWarnings("unchecked") + Iterator i = (Iterator)v.keys(); + while (i.hasNext()) + { + String key = i.next(); + String value = v.getString(key); + + log.debug(key, value); + m.put(key, value); + } + + return m; + } + } + ); + + JSONRegistry.register ( + "core.util.Block", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + Block p = (Block)v; + if (p.bytes == null) + object.put("bytes", "null"); + else + object.put("bytes", Base64.encode(p.bytes)); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + String bytes = v.get("bytes").toString(); + + if (bytes.equals("null")) + return new Block(null); + + return new Block( + Base64.decode(bytes) + ); + } + } + ); + + JSONRegistry.register ( + "core.util.Environment", + new CallSingle () { + @Override + public JSONObject invoke(Object v) throws Exception + { + JSONObject object = new JSONObject(); + + Environment p = (Environment)v; + for (String k : p.keySet()) + object.put(k, p.get(k)); + + return object; + } + }, + new CallSingle () { + @Override + public Object invoke(JSONObject v) throws Exception + { + Environment e = new Environment(); + Iterator i = v.keys(); + while (i.hasNext()) + { + String k = (String)i.next(); + e.put(k, v.get(k).toString()); + } + + return e; + } + } + ); + } + +} \ No newline at end of file diff --git a/java/core/src/core/util/JSON_.java b/java/core/src/core/util/JSON_.java new file mode 100644 index 0000000..6b2c70b --- /dev/null +++ b/java/core/src/core/util/JSON_.java @@ -0,0 +1,284 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.json.JSONArray; +import org.json.JSONObject; + +public class JSON_ +{ + @SuppressWarnings("serial") + public static class JSONException extends Exception + { + public JSONException(Exception e) + { + super(e); + } + }; + + public static Object newString(String string) + { + return string; + } + + public static Object newArray () + { + return new JSONArray(); + } + + public static Object newObject () + { + return new JSONObject(); + } + + public static Object newNumber (long v) + { + return v; + } + + public static int size(Object a) throws JSONException + { + try + { + return ((JSONArray)a).length(); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static String[] keys(Object _o) throws JSONException + { + try + { + ArrayList a = new ArrayList(); + + JSONObject o = (JSONObject)_o; + Iterator i = o.keys(); + while (i.hasNext()) + { + Object _k = i.next(); + a.add((String)_k); + } + + return a.toArray(new String[0]); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static Object get(Object a, int k) throws JSONException + { + try + { + return ((JSONArray)a).get(k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static void add (Object a, Object v) throws JSONException + { + try + { + JSONArray ja = (JSONArray)a; + ja.put(ja.length(), v); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static Object get(Object o, String k) throws JSONException + { + try + { + return ((JSONObject)o).get(k); + } + catch (Exception e) + { + throw new JSONException(e); + } + + } + + public static void put (Object o, String k, Object v) throws JSONException + { + try + { + ((JSONObject)o).put(k, v); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static boolean has (Object o, String k) throws JSONException + { + try + { + return ((JSONObject)o).has(k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + //---------------------------------------------- + + public static String getString (Object a, int i) throws JSONException + { + try + { + return ((String)get(a,i)); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static String getString (Object o, String k) throws JSONException + { + try + { + return ((String)get(o,k)); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static Object getArray (Object o, String k) throws JSONException + { + try + { + return get(o,k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static Object getObject(Object o, String k) throws JSONException + { + try + { + return get(o,k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static int getInt(Object o, String k) throws JSONException + { + try + { + return ((JSONObject)o).getInt(k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static Object getArray(Object a, int i) throws JSONException + { + try + { + return get(a,i); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static long getLong(Object o, int k) throws JSONException + { + try + { + return ((JSONArray)o).getLong(k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static long getLong(Object o, String k) throws JSONException + { + try + { + return ((JSONObject)o).getLong(k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static boolean getBoolean(Object o, String k) throws JSONException + { + try + { + return ((JSONObject)o).getBoolean(k); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static Object parse (String s) throws JSONException + { + try + { + return new JSONObject(s); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static Object newBoolean(boolean loaded) + { + return loaded; + } + + public static Object getObject(Object a, int i) throws JSONException + { + try + { + return ((JSONArray)a).get(i); + } + catch (Exception e) + { + throw new JSONException(e); + } + } + + public static String asString(Object value) + { + return (String)value; + } +} diff --git a/java/core/src/core/util/JavaSerializer.java b/java/core/src/core/util/JavaSerializer.java new file mode 100644 index 0000000..602802c --- /dev/null +++ b/java/core/src/core/util/JavaSerializer.java @@ -0,0 +1,38 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class JavaSerializer +{ + public static byte[] serialize (T block) throws IOException + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(block); + oos.close(); + bos.close(); + + return bos.toByteArray(); + } + + @SuppressWarnings("unchecked") + public static T deserialize (byte[] bytes) throws IOException, ClassNotFoundException + { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bis); + T block = (T)ois.readObject(); + bis.close(); + ois.close(); + + return block; + } +} diff --git a/java/core/src/core/util/LogNull.java b/java/core/src/core/util/LogNull.java new file mode 100644 index 0000000..f1b96f6 --- /dev/null +++ b/java/core/src/core/util/LogNull.java @@ -0,0 +1,71 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import core.callback.Callback; +import core.callback.CallbackDefault; + +public class LogNull +{ + LogOut out; + + public LogNull (Class clazz) + { + out = new LogOut(clazz); + } + + public LogNull(String string) { + out = new LogOut(string); + } + + public final void debug (Object...arguments) + { +// out.debug(arguments); + } + + public final void debugPart (Object...arguments) + { +// out.debugPart(arguments); + } + + public final void debugFlush () + { +// out.debugFlush(); + } + + public final void error (Object...arguments) + { + out.println(arguments); + } + + public final String format (String format, Object...args) + { + return out.format(format, args); + } + + public final Callback debug_(Object...args) + { + return new CallbackDefault(args) { + + @Override + public void onSuccess(Object... arguments) throws Exception { + debug(v); + debug(arguments); + next(arguments); + } + }; + } + + public final void trace (Object...args) + { + + } + + public void exception (Exception e) + { + out.exception(e); + } +} diff --git a/java/core/src/core/util/LogOut.java b/java/core/src/core/util/LogOut.java new file mode 100644 index 0000000..19c5a4c --- /dev/null +++ b/java/core/src/core/util/LogOut.java @@ -0,0 +1,112 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.Date; + +import core.callback.Callback; +import core.callback.CallbackDefault; + +public class LogOut +{ + String prefix; + String cached = ""; + DateFormat dateFormat = new DateFormat("HH:mm:ss.SSS"); + + public LogOut (Class clazz) + { + prefix = clazz.getName()+":"; + } + + public LogOut(String prefix) + { + this.prefix = prefix; + } + + public final String build (Object...arguments) + { + StringBuilder builder = new StringBuilder(); + builder.append(prefix); + + for (int j=0;j void applyToMapAA(Map m, Object[][] items) + { + for (Object[] p : items) + { + m.put((K)p[0], (V)p[1]); + } + } + + @SuppressWarnings("unchecked") + public static void applyToMapA(Map m, Object ... items) + { + int i; + for (i=1; i Map toMapAA(Object[][] items) + { + Map m = new HashMap (); + applyToMapAA(m, items); + + return m; + } + + @SuppressWarnings("unchecked") + public static Map toMap(Object ... items) + { + Map m = new HashMap (); + + int i; + for (i=1; i Callback put_(Map map, K k, X x) + { + return new CallbackDefault(map, k,x) { + public void onSuccess(Object... arguments) throws Exception { + Map map = V(0); + K k = V(1); + X x = V(2); + map.put(k,x); + + log.debug("put",map,k,x); + next(arguments); + } + }; + } + + public static Callback put_(Map map, K k) + { + return new CallbackDefault(map, k) { + public void onSuccess(Object... arguments) throws Exception { + Map map = V(0); + K k = V(1); + X x = (X)arguments[0]; + map.put(k,x); + log.debug("put",map,k,x); + next(arguments); + } + }; + } +} diff --git a/java/core/src/core/util/Pair.java b/java/core/src/core/util/Pair.java new file mode 100644 index 0000000..e746b67 --- /dev/null +++ b/java/core/src/core/util/Pair.java @@ -0,0 +1,47 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.Serializable; + +public class Pair implements Serializable +{ + private static final long serialVersionUID = 1L; + + public F first; + public S second; + + public Pair(F first, S second) + { + this.first = first; + this.second = second; + } + + public Pair () + { + first = null; + second = null; + } + + public boolean equals (Object other) + { + if (this == other) + return true; + + if (other instanceof Pair) + { + Pair pair = (Pair)other; + return this.first.equals(pair.first) && this.second.equals(pair.second); + } + + return false; + } + + public static Pair create(X x, Y y) + { + return new Pair(x,y); + } +} diff --git a/java/core/src/core/util/Passwords.java b/java/core/src/core/util/Passwords.java new file mode 100644 index 0000000..46c0e34 --- /dev/null +++ b/java/core/src/core/util/Passwords.java @@ -0,0 +1,23 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.IOException; + +public class Passwords +{ + public static String getPasswordFor (String key) throws IOException + { + try + { + return ExternalResource.getTrimmedString("passwords/" + key); + } + catch (Exception e) + { + throw new IOException("Internal error. #9203 " + key); + } + } +} diff --git a/java/core/src/core/util/ProxySelectorRegex.java b/java/core/src/core/util/ProxySelectorRegex.java new file mode 100644 index 0000000..8327416 --- /dev/null +++ b/java/core/src/core/util/ProxySelectorRegex.java @@ -0,0 +1,101 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +public class ProxySelectorRegex extends ProxySelector +{ + static public String[] DEFAULT_EXCLUDED_HOSTS = { + "localhost", + "127\\.0.*", + "192\\.168\\.1\\..*", + ".*piratemail\\.se", + "blue", + ".*mailiverse\\.com", + ".*\\.dropbox\\.com", + "s3\\.amazonaws\\.com", + "s3\\.amazonaws\\.com\\.", + ".*\\.s3\\.amazonaws\\.com", + ".*\\.s3\\.amazonaws\\.com\\.", + ".*\\.push\\.apple\\.com" + } ; + + List NoProxy = Arrays.asList(new Proxy[] { Proxy.NO_PROXY }); + Set excludedHosts = new HashSet(); + Map > schemeToProxyList = new HashMap >(); + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) + { + } + + public void excludeHost (String host) + { + excludedHosts.add(Pattern.compile(host.toLowerCase())); + } + + public void addProxy (String scheme, Proxy proxy) + { + if (!schemeToProxyList.containsKey(scheme)) + schemeToProxyList.put(scheme, new ArrayList(1)); + + schemeToProxyList.get(scheme).add(proxy); + } + + @Override + public List select(URI uri) + { + String host = uri.getHost().toLowerCase(); +// System.err.println("ProxySelectorRegex.select host " + host); + for (Pattern excluded : excludedHosts) + { + if (excluded.matcher(host).matches()) + { +// System.err.println("ProxySelectorRegex.select host matched " + excluded.toString()); + return NoProxy; + } + } + + String scheme = uri.getScheme(); +// System.err.println("ProxySelectorRegex.select scheme " + scheme); + + List proxyList = schemeToProxyList.get(scheme); + if (proxyList == null) + { +// System.err.println("ProxySelectorRegex.select did not find a proxy for " + scheme); + return NoProxy; + } + + System.err.println("ProxySelectorRegex.select found a proxy for " + scheme + " " + host); + return proxyList; + } + + public void install () + { + setDefault(this); + } + + public void excludeHosts(String[] hosts) + { + for (String host : hosts) + { + excludeHost(host); + } + } +} diff --git a/java/core/src/core/util/RunnableWithVariables.java b/java/core/src/core/util/RunnableWithVariables.java new file mode 100644 index 0000000..9f98cf8 --- /dev/null +++ b/java/core/src/core/util/RunnableWithVariables.java @@ -0,0 +1,22 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +public abstract class RunnableWithVariables implements Runnable +{ + protected Object[] v; + + public RunnableWithVariables(Object... v) + { + this.v = v; + } + + @SuppressWarnings("unchecked") + public T V(int i) + { + return (T)v[i]; + } +} diff --git a/java/core/src/core/util/SecureRandom.java b/java/core/src/core/util/SecureRandom.java new file mode 100644 index 0000000..b96e77f --- /dev/null +++ b/java/core/src/core/util/SecureRandom.java @@ -0,0 +1,11 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +public class SecureRandom extends java.security.SecureRandom +{ + +} diff --git a/java/core/src/core/util/SimpleSerializer.java b/java/core/src/core/util/SimpleSerializer.java new file mode 100644 index 0000000..54a91e0 --- /dev/null +++ b/java/core/src/core/util/SimpleSerializer.java @@ -0,0 +1,21 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.IOException; + +public class SimpleSerializer +{ + public static byte[] serialize (T block) throws IOException + { + return JSONSerializer.serialize(block); + } + + public static T deserialize (byte[] bytes) throws IOException, ClassNotFoundException + { + return JSONSerializer.deserialize(bytes); + } +} diff --git a/java/core/src/core/util/SqlCatalog.java b/java/core/src/core/util/SqlCatalog.java new file mode 100644 index 0000000..1b74321 --- /dev/null +++ b/java/core/src/core/util/SqlCatalog.java @@ -0,0 +1,29 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.IOException; +import java.util.ArrayList; + +public class SqlCatalog +{ + public String getSingle (String name) throws IOException + { + return Streams.readFullyString(getClass().getResourceAsStream(name), "UTF-8"); + } + + public String[] getMulti (String name) throws IOException + { + String[] sqls = getSingle(name).split(";"); + ArrayList valid = new ArrayList(); + for (String sql : sqls) + if (!sql.trim().isEmpty()) + valid.add(sql); + + return valid.toArray(new String[0]); + } + +} diff --git a/java/core/src/core/util/Streams.java b/java/core/src/core/util/Streams.java new file mode 100644 index 0000000..f3426e3 --- /dev/null +++ b/java/core/src/core/util/Streams.java @@ -0,0 +1,140 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +import core.callback.Callback; +import core.callback.CallbackDefault; + +public class Streams { + + public static String readFullyString (InputStream inputStream, String encoding) throws IOException + { + return Strings.toString(readFullyBytes(inputStream), encoding); + } + + public static byte[] readFullyBytes (InputStream inputStream) throws IOException + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = 0; + while ((length = inputStream.read(buffer)) != -1) + baos.write(buffer, 0, length); + + return baos.toByteArray(); + } + + public static void writeBoundedArray(OutputStream dos, byte[] bytes) throws IOException + { + dos.write(intToByteArray(bytes.length)); + dos.write(bytes); + } + + public static Callback writeBytes_(OutputStream os) + { + return new CallbackDefault(os) { + + @Override + public void onSuccess(Object... arguments) throws Exception + { + OutputStream os = V(0); + os.write((byte[])arguments[0]); + next(arguments); + } + }; + } + + public static Callback toByteArray_ (ByteArrayOutputStream bos) + { + return new CallbackDefault(bos) { + + @Override + public void onSuccess(Object... arguments) throws Exception + { + ByteArrayOutputStream bos = V(0); + next(bos.toByteArray()); + } + }; + } + + public static byte[] readBoundedArray(InputStream dis) throws IOException + { + int size = readInt(dis); + if (size > dis.available()) + throw new IOException("read size greater than max read size"); + + byte[] bytes = new byte[size]; + dis.read(bytes, 0, size); + return bytes; + } + + public static byte[] readBoundedArray3(InputStream dis) throws IOException + { + int size = readInt3(dis); + if (size > dis.available()) + throw new IOException("read size greater than max read size"); + + byte[] bytes = new byte[size]; + dis.read(bytes, 0, size); + return bytes; + } + + public static int readInt(byte[] block, int offset) + { + int b1 = block[offset++] & 0xFF; + int b2 = block[offset++] & 0xFF; + int b3 = block[offset++] & 0xFF; + int b4 = block[offset++] & 0xFF; + return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); + } + + public static int readInt3(byte[] block, int offset) + { + int b2 = block[offset++] & 0xFF; + int b3 = block[offset++] & 0xFF; + int b4 = block[offset++] & 0xFF; + return ((b2 << 16) | (b3 << 8) | b4); + } + + public static int readInt(InputStream bis) throws IOException + { + byte[] i = new byte[4]; + bis.read(i); + return readInt(i,0); + } + + public static int readInt3(InputStream bis) throws IOException + { + byte[] i = new byte[3]; + bis.read(i); + return readInt3(i,0); + } + + public static byte[] intToByteArray(int val) + { + return new byte[] { + (byte)(val >> 24), + (byte)(val >> 16), + (byte)(val >> 8), + (byte)(val) + }; + } + + public static void writeInt(OutputStream bos, int val) throws IOException + { + bos.write(intToByteArray(val)); + } +} diff --git a/java/core/src/core/util/Strings.java b/java/core/src/core/util/Strings.java new file mode 100644 index 0000000..fd8ee0e --- /dev/null +++ b/java/core/src/core/util/Strings.java @@ -0,0 +1,118 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.UnsupportedEncodingException; +import java.util.Collection; +import java.util.Iterator; + +public class Strings +{ + static public String concat (Collection c, String delimiter) + { + return concat(c.iterator(), delimiter); + } + + static public String toString (byte[] bytes, String encoding) throws UnsupportedEncodingException + { + if ("UTF-8".equals(encoding)) + return toString(bytes); + + return new String(bytes, encoding); + } + + static public String toString (byte[] bytes) + { + return StringsPlatform.toString(bytes); + } + + static public String toString (char[] chars) + { + return new String(chars); + } + + static public byte[] toBytes (String s) + { + return StringsPlatform.toBytes(s); + } + + @SuppressWarnings("rawtypes") + static public String concat (Iterator i, String delimiter) + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + while (i.hasNext()) + { + String string = i.next().toString(); + if (!first) + sb.append(delimiter); + + sb.append(string); + + first = false; + } + + return sb.toString(); + } + + static public String concat (Object[] args, String delimiter) + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Object string : args) + { + if (!first) + sb.append(delimiter); + + sb.append(string.toString()); + + first = false; + } + + return sb.toString(); + } + + static public String concatExcudingNull (Object[] objects, String delimiter) + { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Object i : objects) + { + if (i == null) + continue; + + String string = i.toString(); + if (!first) + sb.append(delimiter); + + sb.append(string); + + first = false; + } + + return sb.toString(); + } + + static public String[] splitLines (String lines) + { + return lines.split("\\r?\\n"); + } + + public static String trimQuotes( String value ) + { + if (value == null) + return value; + + value = value.trim(); + boolean b = value.startsWith("\"") || value.startsWith("\'"); + boolean e = value.endsWith("\"") || value.endsWith("\'"); + + if (b || e) + return value.substring(0 + (b ? 1 : 0), value.length() - (e ? 1 : 0)); + + return value; + } +} diff --git a/java/core/src/core/util/StringsPlatform.java b/java/core/src/core/util/StringsPlatform.java new file mode 100644 index 0000000..4ef0640 --- /dev/null +++ b/java/core/src/core/util/StringsPlatform.java @@ -0,0 +1,36 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.UnsupportedEncodingException; + +public class StringsPlatform +{ + static public String toString(byte[] bytes) + { + try + { + return new String(bytes, "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + + static public byte[] toBytes(String string) + { + try + { + return string.getBytes("UTF-8"); + } + catch (UnsupportedEncodingException e) + { + throw new RuntimeException(e); + } + } + +} diff --git a/java/core/src/core/util/Triple.java b/java/core/src/core/util/Triple.java new file mode 100644 index 0000000..a183b85 --- /dev/null +++ b/java/core/src/core/util/Triple.java @@ -0,0 +1,31 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.Serializable; + +public class Triple implements Serializable +{ + private static final long serialVersionUID = 1L; + + public F first; + public S second; + public T third; + + public Triple (F first, S second, T third) + { + this.first = first; + this.second = second; + this.third = third; + } + + public Triple () + { + first = null; + second = null; + third = null; + } +} diff --git a/java/core/src/core/util/WorkerThread.java b/java/core/src/core/util/WorkerThread.java new file mode 100644 index 0000000..da9712e --- /dev/null +++ b/java/core/src/core/util/WorkerThread.java @@ -0,0 +1,69 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.util.LinkedList; + +public class WorkerThread extends Thread +{ + public LinkedList queue; + boolean running; + + public WorkerThread () + { + queue = new LinkedList(); + running = true; + } + + public synchronized void queue (Runnable r) + { + synchronized (queue) + { + queue.addLast(r); + queue.notify(); + } + } + + public Runnable dequeue () + { + synchronized (queue) + { + return queue.removeFirst(); + } + } + + public boolean waitFor () + { + try + { + synchronized(queue) + { + while (queue.isEmpty() && running) + queue.wait(); + } + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + return running; + } + + public void shutdown () + { + running = false; + queue.notify(); + } + + public void run () + { + while (waitFor()) + { + Runnable r = dequeue(); + r.run(); + } + } +} diff --git a/java/core/src/core/util/XML.java b/java/core/src/core/util/XML.java new file mode 100644 index 0000000..29a7372 --- /dev/null +++ b/java/core/src/core/util/XML.java @@ -0,0 +1,52 @@ +/** + * Author: Timothy Prepscius + * License: GPLv3 Affero + keep my name in the code! + */ + +package core.util; + +import java.io.ByteArrayInputStream; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class XML +{ + public static int ELEMENT_NODE = Node.ELEMENT_NODE; + + public static Object parse(String xml) throws Exception + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(new ByteArrayInputStream(xml.getBytes("UTF-8"))); + + return doc; + } + + public static Object[] getElementsByTagName(Object doc, String tag) throws Exception + { + NodeList contents = ((Element) doc).getElementsByTagName(tag); + Object[] result = new Object[contents.getLength()]; + + for (int i=0; i