mirror of
https://github.com/moparisthebest/mailiverse
synced 2024-11-23 01:02:14 -05:00
adds a set of files
This commit is contained in:
parent
b0c64f9d08
commit
10028cd6e8
10
License.txt
Normal file
10
License.txt
Normal file
@ -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.
|
40
java/core/src/core/crypt/Cryptor.java
Normal file
40
java/core/src/core/crypt/Cryptor.java
Normal file
@ -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]));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
105
java/core/src/core/crypt/CryptorAES.java
Normal file
105
java/core/src/core/crypt/CryptorAES.java
Normal file
@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
93
java/core/src/core/crypt/CryptorAESIV.java
Normal file
93
java/core/src/core/crypt/CryptorAESIV.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
53
java/core/src/core/crypt/CryptorAESJCE.java
Normal file
53
java/core/src/core/crypt/CryptorAESJCE.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
25
java/core/src/core/crypt/CryptorNone.java
Normal file
25
java/core/src/core/crypt/CryptorNone.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
27
java/core/src/core/crypt/CryptorRSA.java
Normal file
27
java/core/src/core/crypt/CryptorRSA.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
216
java/core/src/core/crypt/CryptorRSAAES.java
Normal file
216
java/core/src/core/crypt/CryptorRSAAES.java
Normal file
@ -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<byte[]>(encrypt_()).invoke(bytes).export();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] decrypt (byte[] bytes) throws CryptoException
|
||||
{
|
||||
try
|
||||
{
|
||||
return new CallbackSync<byte[]>(decrypt_()).invoke(bytes).export();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
}
|
172
java/core/src/core/crypt/CryptorRSABC.java
Normal file
172
java/core/src/core/crypt/CryptorRSABC.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
78
java/core/src/core/crypt/CryptorRSAFactory.java
Normal file
78
java/core/src/core/crypt/CryptorRSAFactory.java
Normal file
@ -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<byte[], byte[]> 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<byte[], byte[]>(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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
83
java/core/src/core/crypt/CryptorRSAFactoryEnvironment.java
Normal file
83
java/core/src/core/crypt/CryptorRSAFactoryEnvironment.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
91
java/core/src/core/crypt/CryptorRSAJCE.java
Normal file
91
java/core/src/core/crypt/CryptorRSAJCE.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
16
java/core/src/core/crypt/CryptorSeed.java
Normal file
16
java/core/src/core/crypt/CryptorSeed.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
26
java/core/src/core/crypt/HashMd5.java
Normal file
26
java/core/src/core/crypt/HashMd5.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
26
java/core/src/core/crypt/HashSha256.java
Normal file
26
java/core/src/core/crypt/HashSha256.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
32
java/core/src/core/crypt/HmacSha1.java
Normal file
32
java/core/src/core/crypt/HmacSha1.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
75
java/core/src/core/crypt/KeyPairFromPassword.java
Normal file
75
java/core/src/core/crypt/KeyPairFromPassword.java
Normal file
@ -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];
|
||||
}
|
||||
}
|
41
java/core/src/core/crypt/KeyPairFromPasswordCryptor.java
Normal file
41
java/core/src/core/crypt/KeyPairFromPasswordCryptor.java
Normal file
@ -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_();
|
||||
}
|
||||
}
|
105
java/core/src/core/crypt/PBE.java
Normal file
105
java/core/src/core/crypt/PBE.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
16
java/core/src/core/crypt/PBEPlatform.java
Normal file
16
java/core/src/core/crypt/PBEPlatform.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
29
java/core/src/core/crypt/PBEPlatformBC.java
Normal file
29
java/core/src/core/crypt/PBEPlatformBC.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
13
java/core/src/core/crypt/PBEPlatformNative.java
Normal file
13
java/core/src/core/crypt/PBEPlatformNative.java
Normal file
@ -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);
|
||||
}
|
30
java/core/src/core/crypt/PasswordValidator.java
Normal file
30
java/core/src/core/crypt/PasswordValidator.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
19
java/core/src/core/exceptions/CryptoException.java
Normal file
19
java/core/src/core/exceptions/CryptoException.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
23
java/core/src/core/exceptions/InternalException.java
Normal file
23
java/core/src/core/exceptions/InternalException.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
32
java/core/src/core/exceptions/PublicMessageException.java
Normal file
32
java/core/src/core/exceptions/PublicMessageException.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
16
java/core/src/core/exceptions/UserExistsException.java
Normal file
16
java/core/src/core/exceptions/UserExistsException.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
98
java/core/src/core/io/IoChain.java
Normal file
98
java/core/src/core/io/IoChain.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
83
java/core/src/core/io/IoChainAccumulator.java
Normal file
83
java/core/src/core/io/IoChainAccumulator.java
Normal file
@ -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<byte[]> out;
|
||||
boolean opened, closed;
|
||||
Exception exception;
|
||||
|
||||
public IoChainAccumulator()
|
||||
{
|
||||
super(null);
|
||||
out = new ArrayList<byte[]>();
|
||||
}
|
||||
|
||||
@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<byte[]> getAndClearPackets()
|
||||
{
|
||||
List<byte[]> result = out;
|
||||
out = new ArrayList<byte[]>();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Exception getAndClearException ()
|
||||
{
|
||||
Exception e = this.exception;
|
||||
this.exception = null;
|
||||
return e;
|
||||
}
|
||||
|
||||
public boolean isOpened ()
|
||||
{
|
||||
return opened;
|
||||
}
|
||||
|
||||
public boolean isClosed ()
|
||||
{
|
||||
return closed;
|
||||
}
|
||||
}
|
41
java/core/src/core/io/IoChainBase64.java
Normal file
41
java/core/src/core/io/IoChainBase64.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
10
java/core/src/core/io/IoChainFinishedException.java
Normal file
10
java/core/src/core/io/IoChainFinishedException.java
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Author: Timothy Prepscius
|
||||
* License: GPLv3 Affero + keep my name in the code!
|
||||
*/
|
||||
|
||||
package core.io;
|
||||
|
||||
public class IoChainFinishedException extends Exception {
|
||||
|
||||
}
|
65
java/core/src/core/io/IoChainNewLinePackets.java
Normal file
65
java/core/src/core/io/IoChainNewLinePackets.java
Normal file
@ -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<byte[]> packets = new ArrayList<byte[]>();
|
||||
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());
|
||||
}
|
||||
}
|
32
java/core/src/core/io/IoChainSSLSocket.java
Normal file
32
java/core/src/core/io/IoChainSSLSocket.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
87
java/core/src/core/io/IoChainSizedPackets.java
Normal file
87
java/core/src/core/io/IoChainSizedPackets.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
111
java/core/src/core/io/IoChainSocket.java
Normal file
111
java/core/src/core/io/IoChainSocket.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
29
java/core/src/core/io/IoChainThread.java
Normal file
29
java/core/src/core/io/IoChainThread.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
47
java/core/src/core/io/SSLContextGenerator.java
Normal file
47
java/core/src/core/io/SSLContextGenerator.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
146
java/core/src/core/server/captcha/Captcha.java
Normal file
146
java/core/src/core/server/captcha/Captcha.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
22
java/core/src/core/server/captcha/sql/Catalog.java
Normal file
22
java/core/src/core/server/captcha/sql/Catalog.java
Normal file
@ -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";
|
||||
}
|
1
java/core/src/core/server/captcha/sql/add_token
Normal file
1
java/core/src/core/server/captcha/sql/add_token
Normal file
@ -0,0 +1 @@
|
||||
INSERT INTO captcha (token) values (?)
|
1
java/core/src/core/server/captcha/sql/check_token
Normal file
1
java/core/src/core/server/captcha/sql/check_token
Normal file
@ -0,0 +1 @@
|
||||
SELECT true FROM captcha WHERE token=?
|
5
java/core/src/core/server/captcha/sql/create_tables
Normal file
5
java/core/src/core/server/captcha/sql/create_tables
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS captcha (
|
||||
token VARCHAR(255),
|
||||
mark TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (token)
|
||||
)
|
1
java/core/src/core/server/captcha/sql/prune_tokens
Normal file
1
java/core/src/core/server/captcha/sql/prune_tokens
Normal file
@ -0,0 +1 @@
|
||||
DELETE FROM captcha WHERE mark < DATE_SUB(now(), INTERVAL 2 HOUR);
|
1
java/core/src/core/server/captcha/sql/use_token
Normal file
1
java/core/src/core/server/captcha/sql/use_token
Normal file
@ -0,0 +1 @@
|
||||
DELETE FROM captcha WHERE token=?
|
479
java/core/src/core/server/db/UserDb.java
Normal file
479
java/core/src/core/server/db/UserDb.java
Normal file
@ -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<Integer, Triple<String, byte[], byte[]> > getUser (String userName) throws IOException, SQLException
|
||||
{
|
||||
Connection connection = openConnection();
|
||||
|
||||
Pair<Integer, Triple<String, byte[], byte[]>> 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<Integer, Triple<String, byte[], byte[] > >(
|
||||
results.getInt("id"),
|
||||
new Triple<String, byte[], byte[]> (
|
||||
results.getString("version"),
|
||||
Base64.decode(results.getString("v")),
|
||||
Base64.decode(results.getString("s"))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
closeConnection(connection);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Triple<String, BigInteger, BigInteger> getVVS (String userName) throws IOException, SQLException
|
||||
{
|
||||
Triple<String, byte[], byte[]> vvs = getUser(userName).second;
|
||||
return
|
||||
new Triple<String, BigInteger, BigInteger>(
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
51
java/core/src/core/server/db/sql/Catalog.java
Normal file
51
java/core/src/core/server/db/sql/Catalog.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
55
java/core/src/core/server/db/sql/create_tables
Normal file
55
java/core/src/core/server/db/sql/create_tables
Normal file
@ -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;
|
||||
|
1
java/core/src/core/server/db/sql/create_user
Normal file
1
java/core/src/core/server/db/sql/create_user
Normal file
@ -0,0 +1 @@
|
||||
INSERT INTO user (version, name, v, s) VALUES (?, ?, ?, ?)
|
24
java/core/src/core/server/db/sql/delete_user.sql
Normal file
24
java/core/src/core/server/db/sql/delete_user.sql
Normal file
@ -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=?
|
@ -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=?
|
7
java/core/src/core/server/db/sql/get_deleted_user.sql
Normal file
7
java/core/src/core/server/db/sql/get_deleted_user.sql
Normal file
@ -0,0 +1,7 @@
|
||||
select
|
||||
*
|
||||
from
|
||||
deleted_user
|
||||
order by
|
||||
deleted
|
||||
limit 1
|
@ -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 = ?
|
8
java/core/src/core/server/db/sql/get_last_failure
Normal file
8
java/core/src/core/server/db/sql/get_last_failure
Normal file
@ -0,0 +1,8 @@
|
||||
SELECT
|
||||
failure.mark
|
||||
FROM
|
||||
user,
|
||||
failure
|
||||
WHERE
|
||||
user.name = ? AND
|
||||
failure.user_id = user.id
|
6
java/core/src/core/server/db/sql/get_user
Normal file
6
java/core/src/core/server/db/sql/get_user
Normal file
@ -0,0 +1,6 @@
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
user
|
||||
WHERE
|
||||
user.name = ?
|
8
java/core/src/core/server/db/sql/get_user_key_block
Normal file
8
java/core/src/core/server/db/sql/get_user_key_block
Normal file
@ -0,0 +1,8 @@
|
||||
SELECT
|
||||
key_block.*
|
||||
FROM
|
||||
user,
|
||||
key_block
|
||||
WHERE
|
||||
key_block.user_id = user.id AND
|
||||
user.name = ?
|
8
java/core/src/core/server/db/sql/get_user_mail_block
Normal file
8
java/core/src/core/server/db/sql/get_user_mail_block
Normal file
@ -0,0 +1,8 @@
|
||||
SELECT
|
||||
mail_block.*
|
||||
FROM
|
||||
user,
|
||||
mail_block
|
||||
WHERE
|
||||
mail_block.user_id = user.id AND
|
||||
user.name = ?
|
8
java/core/src/core/server/db/sql/mark_failure
Normal file
8
java/core/src/core/server/db/sql/mark_failure
Normal file
@ -0,0 +1,8 @@
|
||||
REPLACE INTO failure (user_id, mark)
|
||||
SELECT
|
||||
id as user_id,
|
||||
now() as mark
|
||||
FROM
|
||||
user
|
||||
WHERE
|
||||
name = ?
|
4
java/core/src/core/server/db/sql/room_for_new_user
Normal file
4
java/core/src/core/server/db/sql/room_for_new_user
Normal file
@ -0,0 +1,4 @@
|
||||
select
|
||||
count(*) < (select convert(v, unsigned integer) from registry where k="max_users") as room
|
||||
from
|
||||
user
|
1
java/core/src/core/server/db/sql/set_user_key_block
Normal file
1
java/core/src/core/server/db/sql/set_user_key_block
Normal file
@ -0,0 +1 @@
|
||||
REPLACE INTO key_block (user_id, block) VALUES (?, ?)
|
1
java/core/src/core/server/db/sql/set_user_mail_block
Normal file
1
java/core/src/core/server/db/sql/set_user_mail_block
Normal file
@ -0,0 +1 @@
|
||||
REPLACE INTO mail_block (user_id, block) VALUES (?, ?)
|
51
java/core/src/core/server/log/Tail.java
Normal file
51
java/core/src/core/server/log/Tail.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
220
java/core/src/core/server/mailextra/MailExtraDb.java
Normal file
220
java/core/src/core/server/mailextra/MailExtraDb.java
Normal file
@ -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<String,String,String> getDeviceFor (String email) throws SQLException, IOException
|
||||
{
|
||||
return getDeviceForHash(getUserHash(email));
|
||||
}
|
||||
|
||||
public Triple<String,String,String> getDeviceForHash (String hash) throws SQLException, IOException
|
||||
{
|
||||
|
||||
Connection connection = openConnection();
|
||||
Triple<String,String,String> 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<String,String,String>(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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
25
java/core/src/core/server/mailextra/sql/Catalog.java
Normal file
25
java/core/src/core/server/mailextra/sql/Catalog.java
Normal file
@ -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";
|
||||
}
|
10
java/core/src/core/server/mailextra/sql/add_days_to
Normal file
10
java/core/src/core/server/mailextra/sql/add_days_to
Normal file
@ -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)
|
||||
)
|
15
java/core/src/core/server/mailextra/sql/create_tables
Normal file
15
java/core/src/core/server/mailextra/sql/create_tables
Normal file
@ -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`)
|
||||
);
|
6
java/core/src/core/server/mailextra/sql/get_days_left
Normal file
6
java/core/src/core/server/mailextra/sql/get_days_left
Normal file
@ -0,0 +1,6 @@
|
||||
SELECT
|
||||
DATEDIFF(expiration, CURDATE()) as days
|
||||
FROM
|
||||
expirations
|
||||
WHERE
|
||||
expirations.email = ?
|
@ -0,0 +1,10 @@
|
||||
SELECT
|
||||
device.notification_type,
|
||||
device.device_type,
|
||||
device.device_id,
|
||||
device.mark
|
||||
FROM
|
||||
device
|
||||
WHERE
|
||||
device.email = ?
|
||||
|
@ -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())
|
301
java/core/src/core/srp/SRPPacketSerializerJSON.java
Normal file
301
java/core/src/core/srp/SRPPacketSerializerJSON.java
Normal file
@ -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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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<JSONObject,Object> () {
|
||||
@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<Object,JSONObject> () {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
182
java/core/src/core/srp/SRPPackets.java
Normal file
182
java/core/src/core/srp/SRPPackets.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
43
java/core/src/core/srp/SRPSession.java
Normal file
43
java/core/src/core/srp/SRPSession.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
91
java/core/src/core/srp/client/SRPClient.java
Normal file
91
java/core/src/core/srp/client/SRPClient.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
71
java/core/src/core/srp/client/SRPClientAsync.java
Normal file
71
java/core/src/core/srp/client/SRPClientAsync.java
Normal file
@ -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_();
|
||||
}
|
||||
}
|
10
java/core/src/core/srp/client/SRPClientListener.java
Normal file
10
java/core/src/core/srp/client/SRPClientListener.java
Normal file
@ -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);
|
||||
}
|
93
java/core/src/core/srp/client/SRPClientUserSession.java
Normal file
93
java/core/src/core/srp/client/SRPClientUserSession.java
Normal file
@ -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));
|
||||
}
|
||||
|
||||
}
|
77
java/core/src/core/srp/server/SRPServer.java
Normal file
77
java/core/src/core/srp/server/SRPServer.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
177
java/core/src/core/srp/server/SRPServerUserSession.java
Normal file
177
java/core/src/core/srp/server/SRPServerUserSession.java
Normal file
@ -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<String, BigInteger, BigInteger> 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));
|
||||
}
|
||||
|
||||
}
|
23
java/core/src/core/srp/server/SRPServerUserSessionDb.java
Normal file
23
java/core/src/core/srp/server/SRPServerUserSessionDb.java
Normal file
@ -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<String, BigInteger, BigInteger> 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;
|
||||
}
|
57
java/core/src/core/srp/test/PBETest.java
Normal file
57
java/core/src/core/srp/test/PBETest.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
31
java/core/src/core/swing/CheckListener.java
Normal file
31
java/core/src/core/swing/CheckListener.java
Normal file
@ -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(); }
|
||||
}
|
11
java/core/src/core/swing/Checker.java
Normal file
11
java/core/src/core/swing/Checker.java
Normal file
@ -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 ();
|
||||
}
|
16
java/core/src/core/swing/DimensionCalculator.java
Normal file
16
java/core/src/core/swing/DimensionCalculator.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
38
java/core/src/core/swing/FileFilterStandard.java
Normal file
38
java/core/src/core/swing/FileFilterStandard.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
81
java/core/src/core/swing/RedirectStreams.java
Normal file
81
java/core/src/core/swing/RedirectStreams.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
72
java/core/src/core/swing/SpringLayoutSize.java
Normal file
72
java/core/src/core/swing/SpringLayoutSize.java
Normal file
@ -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<depth; ++i)
|
||||
parent = parent.getParent();
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
public Dimension calculateDimension (Container parent)
|
||||
{
|
||||
Container nestedParent = getNestedParent (parent);
|
||||
|
||||
Dimension result =
|
||||
new Dimension (
|
||||
dimension.width > 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);
|
||||
}
|
||||
}
|
73
java/core/src/core/util/Arrays.java
Normal file
73
java/core/src/core/util/Arrays.java
Normal file
@ -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> 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<length; ++i)
|
||||
v[i]=(byte)j;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static byte[] concat (byte[]... arrays)
|
||||
{
|
||||
int length = 0;
|
||||
for (byte[] array : arrays)
|
||||
length += array.length;
|
||||
|
||||
byte[] result = new byte[length];
|
||||
|
||||
int i=0;
|
||||
for (byte[] array : arrays)
|
||||
{
|
||||
for (byte b : array)
|
||||
result[i++] = b;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] copyOf(byte[] src, int l)
|
||||
{
|
||||
return copyOf(src, 0, l);
|
||||
}
|
||||
|
||||
public static void copyFromTo(byte[] src, int srcOff, byte[] dst, int dstOff, int l)
|
||||
{
|
||||
for (int i=0; i<l; ++i)
|
||||
dst[i+dstOff] = src[i+srcOff];
|
||||
}
|
||||
|
||||
public static void copyFromTo(byte[] src, byte[] dst, int l)
|
||||
{
|
||||
copyFromTo(src,0, dst,0, l);
|
||||
}
|
||||
|
||||
public static byte[] copyOf(byte[] src, int offset, int l)
|
||||
{
|
||||
byte[] result = new byte[l];
|
||||
|
||||
for (int i=0; i<l; ++i)
|
||||
result[i] = src[offset + i];
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
58
java/core/src/core/util/Base16.java
Normal file
58
java/core/src/core/util/Base16.java
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Author: Timothy Prepscius
|
||||
* License: GPLv3 Affero + keep my name in the code!
|
||||
*/
|
||||
|
||||
package core.util;
|
||||
|
||||
public class Base16
|
||||
{
|
||||
static String decoder = "0123456789abcdef";
|
||||
static byte[] encoder = null;
|
||||
|
||||
static void initialize ()
|
||||
{
|
||||
if (encoder != null)
|
||||
return;
|
||||
|
||||
encoder = new byte[0xFF];
|
||||
for (int i=0; i<decoder.length(); ++i)
|
||||
encoder[decoder.charAt(i)] = (byte)i;
|
||||
}
|
||||
|
||||
public static byte[] decode (String value)
|
||||
{
|
||||
initialize();
|
||||
|
||||
byte[] bytes = new byte[value.length() / 2];
|
||||
|
||||
int i=0, j=0;
|
||||
while (i<value.length())
|
||||
{
|
||||
int nibbleHi = (int)encoder[(int)value.charAt(i++)];
|
||||
int nibbleLo = (int)encoder[(int)value.charAt(i++)];
|
||||
|
||||
bytes[j++] = (byte) ((nibbleHi << 4) | (nibbleLo));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static String encode (byte[] bytes)
|
||||
{
|
||||
String result = "";
|
||||
|
||||
int i=0;
|
||||
while (i<bytes.length)
|
||||
{
|
||||
int b = bytes[i++] & 0xFF;
|
||||
int nibbleLo = b & 0x0F;
|
||||
int nibbleHi = b >> 4;
|
||||
|
||||
result += decoder.charAt(nibbleHi);
|
||||
result += decoder.charAt(nibbleLo);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
68
java/core/src/core/util/Base64.java
Normal file
68
java/core/src/core/util/Base64.java
Normal file
@ -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]));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
20
java/core/src/core/util/Block.java
Normal file
20
java/core/src/core/util/Block.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
15
java/core/src/core/util/CallSingle.java
Normal file
15
java/core/src/core/util/CallSingle.java
Normal file
@ -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<T, V> extends Exportable
|
||||
{
|
||||
public T invoke(V v) throws Exception;
|
||||
}
|
24
java/core/src/core/util/CallSingleWithVariables.java
Normal file
24
java/core/src/core/util/CallSingleWithVariables.java
Normal file
@ -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<T,V> implements CallSingle<T,V>
|
||||
{
|
||||
protected Object[] v;
|
||||
|
||||
public CallSingleWithVariables(Object... v)
|
||||
{
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T V(int i)
|
||||
{
|
||||
return (T)v[i];
|
||||
}
|
||||
}
|
20
java/core/src/core/util/Characters.java
Normal file
20
java/core/src/core/util/Characters.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
809
java/core/src/core/util/CircularByteBuffer.java
Normal file
809
java/core/src/core/util/CircularByteBuffer.java
Normal file
@ -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 <a target="_top" href=
|
||||
* "http://ostermiller.org/utils/CircularByteBuffer.html">ostermiller.org</a>.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
98
java/core/src/core/util/Collectionz.java
Normal file
98
java/core/src/core/util/Collectionz.java
Normal file
@ -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 <T> List<T> toMutableList(T[] a)
|
||||
{
|
||||
ArrayList<T> l = new ArrayList<T>();
|
||||
for (T t : a)
|
||||
l.add(t);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
public static <T> Collection<T> filterNull (Collection<T> items)
|
||||
{
|
||||
List<T> l = new ArrayList<T>();
|
||||
|
||||
for (T i : items)
|
||||
if (i != null)
|
||||
l.add(i);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
public static <T> Collection<T> filterNull (T... items)
|
||||
{
|
||||
List<T> l = new ArrayList<T>();
|
||||
|
||||
for (T i : items)
|
||||
if (i != null)
|
||||
l.add(i);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static public void removeByFirst (Collection<? extends Pair> 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<? extends Pair> 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<? extends Pair> items, Object remove)
|
||||
{
|
||||
for (Pair x : items)
|
||||
{
|
||||
if (x.first.equals(remove))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static public boolean containsBySecond (Collection<? extends Pair> items, Object remove)
|
||||
{
|
||||
for (Pair x : items)
|
||||
{
|
||||
if (x.second.equals(remove))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
96
java/core/src/core/util/Comparators.java
Normal file
96
java/core/src/core/util/Comparators.java
Normal file
@ -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<S> implements Comparator<Pair<?,S>>
|
||||
{
|
||||
Comparator<S> c;
|
||||
|
||||
public SortBySecond(Comparator<S> c)
|
||||
{
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Pair<?,S> p1, Pair<?,S> p2)
|
||||
{
|
||||
return c.compare(p1.second, p2.second);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortByFirst<S> implements Comparator<Pair<S,?>>
|
||||
{
|
||||
Comparator<S> c;
|
||||
|
||||
public SortByFirst(Comparator<S> c)
|
||||
{
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Pair<S,?> p1, Pair<S,?> p2)
|
||||
{
|
||||
return c.compare(p1.first, p2.first);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortByFirstReverse<S> implements Comparator<Pair<S,?>>
|
||||
{
|
||||
Comparator<S> c;
|
||||
|
||||
public SortByFirstReverse(Comparator<S> c)
|
||||
{
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Pair<S,?> p1, Pair<S,?> p2)
|
||||
{
|
||||
return c.compare(p2.first, p1.first);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortBySecondNatural<S extends Comparable<S>> implements Comparator<Pair<?,S>>
|
||||
{
|
||||
|
||||
@Override
|
||||
public int compare(Pair<?, S> o1, Pair<?, S> o2)
|
||||
{
|
||||
return o1.second.compareTo(o2.second);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortByFirstNatural<S extends Comparable<S>> implements Comparator<Pair<S,?>>
|
||||
{
|
||||
@Override
|
||||
public int compare(Pair<S,?> o1, Pair<S,?> o2)
|
||||
{
|
||||
return o1.first.compareTo(o2.first);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortBySecondNaturalOpposite<S extends Comparable<S>> implements Comparator<Pair<?,S>>
|
||||
{
|
||||
@Override
|
||||
public int compare(Pair<?, S> o1, Pair<?, S> o2)
|
||||
{
|
||||
return o2.second.compareTo(o1.second);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SortByFirstNaturalOpposite<S extends Comparable<S>> implements Comparator<Pair<S,?>>
|
||||
{
|
||||
@Override
|
||||
public int compare(Pair<S,?> o1, Pair<S,?> o2)
|
||||
{
|
||||
return o2.first.compareTo(o1.first);
|
||||
}
|
||||
}
|
||||
}
|
38
java/core/src/core/util/DateFormat.java
Normal file
38
java/core/src/core/util/DateFormat.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
111
java/core/src/core/util/Environment.java
Normal file
111
java/core/src/core/util/Environment.java
Normal file
@ -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<String,String>
|
||||
{
|
||||
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<FileInfo> 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<String, String> 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<String, String> 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<String, String> i : entrySet())
|
||||
{
|
||||
String k = i.getKey();
|
||||
if (k.startsWith(prefix))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
11
java/core/src/core/util/ExceptionHandler.java
Normal file
11
java/core/src/core/util/ExceptionHandler.java
Normal file
@ -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);
|
||||
}
|
39
java/core/src/core/util/ExternalResource.java
Normal file
39
java/core/src/core/util/ExternalResource.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user