diff --git a/pom.xml b/pom.xml
index c00d129..47d37bc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,5 +29,11 @@
RELEASE
test
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.60
+
\ No newline at end of file
diff --git a/src/main/java/com/github/manevolent/ts3j/ClientConnectionState.java b/src/main/java/com/github/manevolent/ts3j/ClientConnectionState.java
deleted file mode 100644
index a355cb9..0000000
--- a/src/main/java/com/github/manevolent/ts3j/ClientConnectionState.java
+++ /dev/null
@@ -1,28 +0,0 @@
-
-package com.github.manevolent.ts3j;
-
-import com.github.manevolent.ts3j.handler.*;
-
-public enum ClientConnectionState {
- CONNECTING(TeamspeakClientHandlerConnecting.class),
- CONNECTED(TeamspeakClientHandlerConnected.class),
- DISCONNECTED(TeamspeakClientHandlerDisconnected.class);
-
- private final Class extends TeamspeakClientHandler> clazz;
-
- ClientConnectionState(Class extends TeamspeakClientHandler> clazz) {
- this.clazz = clazz;
- }
-
- public Class extends TeamspeakClientHandler> getHandlerClass() {
- return clazz;
- }
-
- public TeamspeakClientHandler createHandler(Teamspeak3Client client) {
- try {
- return clazz.getConstructor(Teamspeak3Client.class).newInstance(client);
- } catch (ReflectiveOperationException ex) {
- throw new RuntimeException(ex);
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/github/manevolent/ts3j/command/Command.java b/src/main/java/com/github/manevolent/ts3j/command/Command.java
index 9ef8974..ae9436e 100644
--- a/src/main/java/com/github/manevolent/ts3j/command/Command.java
+++ b/src/main/java/com/github/manevolent/ts3j/command/Command.java
@@ -1,8 +1,48 @@
package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.command.part.CommandParameter;
+import com.github.manevolent.ts3j.protocol.ProtocolRole;
public interface Command {
+ /**
+ * Gets the name, or label, of this command.
+ * @return Command label.
+ */
+ String getName();
+
+ /**
+ * Gets the intended use role of this command (source).
+ * @return Command role.
+ */
+ ProtocolRole getRole();
+
+ /**
+ * Gets the parameter list of this command.
+ * @return Parameter list.
+ */
Iterable getParameters();
- void appendParameter(CommandParameter parameter);
+
+ /**
+ * Finds if the command is expected to be responded to with a specific return_code.
+ * This is handled by supporting protocol handlers, not by the command code itself.
+ * @return true if the command is expected to support a return_code.
+ */
+ default boolean willExpectResponse() { return false; }
+
+ /**
+ * Builds the command to a netowrk-issueable string.
+ * The implementation currently defaults to the basic command format for TS3:
+ * label -flag parameter=value
+ * @return Baked command string.
+ */
+ default String build() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getName());
+
+ for (CommandParameter parameter : getParameters()) {
+ builder.append(" " + (parameter.toString()).trim());
+ }
+
+ return builder.toString();
+ }
}
diff --git a/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java b/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java
index ebe3751..54156d3 100644
--- a/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java
+++ b/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java
@@ -1,39 +1,34 @@
package com.github.manevolent.ts3j.command;
import com.github.manevolent.ts3j.command.part.CommandParameter;
+import com.github.manevolent.ts3j.protocol.ProtocolRole;
+import java.util.Collection;
import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
public class SimpleCommand implements Command {
private final String name;
- private final List parameterList = new LinkedList<>();
+ private final ProtocolRole role;
+ private final Collection params;
- public SimpleCommand(String name) {
+ public SimpleCommand(String name, ProtocolRole role, Collection params) {
this.name = name;
+ this.role = role;
+ this.params = params;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public ProtocolRole getRole() {
+ return role;
}
@Override
public Iterable getParameters() {
- return Collections.unmodifiableList(parameterList);
- }
-
- @Override
- public void appendParameter(CommandParameter parameter) {
- parameterList.add(parameter);
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append(name);
-
- for (CommandParameter parameter : getParameters()) {
- builder.append(" ");
- builder.append(parameter.toString());
- }
-
- return builder.toString();
+ return Collections.unmodifiableCollection(params);
}
}
diff --git a/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java b/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java
new file mode 100644
index 0000000..4dc2b5f
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java
@@ -0,0 +1,50 @@
+package com.github.manevolent.ts3j.command;
+
+import com.github.manevolent.ts3j.command.part.CommandParameter;
+import com.github.manevolent.ts3j.protocol.ProtocolRole;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public abstract class TypedCommand implements Command {
+ private final ProtocolRole role;
+ private final String name;
+ private final Map parameters = new LinkedHashMap<>();
+
+ protected TypedCommand(ProtocolRole role, String name) {
+ this.role = role;
+
+ this.name = name;
+ }
+
+ protected CommandParameter set(CommandParameter commandParameter) {
+ this.parameters.put(commandParameter.getName(), commandParameter);
+
+ return commandParameter;
+ }
+
+ public CommandParameter get(String key) {
+ return this.parameters.get(key);
+ }
+
+ @Override
+ public Iterable getParameters() {
+ return Collections.unmodifiableCollection(parameters.values());
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public ProtocolRole getRole() {
+ return role;
+ }
+
+ @Override
+ public String toString() {
+ return build();
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java b/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java
index 7dd10b5..00f9ccc 100644
--- a/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java
+++ b/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java
@@ -1,17 +1,68 @@
package com.github.manevolent.ts3j.command.part;
-import com.github.manevolent.ts3j.enums.CommandPartType;
-
public class CommandOption implements CommandParameter {
- private final String value;
+ private final String key;
+ private boolean enabled = false;
- public CommandOption(String value) {
- this.value = " -" + value;
+ public CommandOption(String key) {
+ this.key = key;
+ }
+
+
+ @Override
+ public void set(boolean value) {
+ this.enabled = value;
}
@Override
- public CommandPartType getType() {
- return CommandPartType.OPTION;
+ public String getName() {
+ return key;
}
+ @Override
+ public void set(char value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(String value) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public void set(byte value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(short value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(int value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(long value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(float value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void set(double value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ if (enabled)
+ return "-" + key;
+ else
+ return "";
+ }
}
diff --git a/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java b/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java
index 1af70fc..3c22f16 100644
--- a/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java
+++ b/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java
@@ -1,9 +1,23 @@
package com.github.manevolent.ts3j.command.part;
-import com.github.manevolent.ts3j.enums.CommandPartType;
-
public interface CommandParameter {
- CommandPartType getType();
+ String getName();
+
+ void set(char value);
+ void set(String value);
+ void set(boolean value);
+ void set(byte value);
+ void set(short value);
+ void set(int value);
+ void set(long value);
+ void set(float value);
+ void set(double value);
+
+ /**
+ * Converts the command part to a usable command string element.
+ * @return Command string element.
+ */
+ String toString();
}
diff --git a/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java b/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java
index ef57d87..907c047 100644
--- a/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java
+++ b/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java
@@ -1,81 +1,70 @@
package com.github.manevolent.ts3j.command.part;
-import com.github.manevolent.ts3j.enums.CommandPartType;
+import com.github.manevolent.ts3j.util.Ts3String;
public class CommandSingleParameter implements CommandParameter {
- private final String key, value;
+ private final String key;
+ private String value;
- public CommandSingleParameter(String key, String value) {
+ public CommandSingleParameter(String key) {
this.key = key;
- this.value = value;
- }
-
- public CommandSingleParameter(String key, boolean value) {
- this.key = key;
- this.value = serialize(value);
- }
-
- public CommandSingleParameter(String key, byte value) {
- this.key = key;
- this.value = serialize(value);
- }
-
- public CommandSingleParameter(String key, short value) {
- this.key = key;
- this.value = serialize(value);
- }
-
- public CommandSingleParameter(String key, int value) {
- this.key = key;
- this.value = serialize(value);
- }
-
- public CommandSingleParameter(String key, long value) {
- this.key = key;
- this.value = serialize(value);
- }
-
- public CommandSingleParameter(String key, float value) {
- this.key = key;
- this.value = serialize(value);
- }
-
- public CommandSingleParameter(String key, double value) {
- this.key = key;
- this.value = serialize(value);
}
@Override
- public CommandPartType getType() {
- return CommandPartType.SINGLE_PARAMETER;
+ public String toString() {
+ if (value == null)
+ return "";
+ else
+ return (key + "=" + Ts3String.escape(value.trim()));
}
- public static String serialize(boolean value) {
- return value ? "1": "0";
+ @Override
+ public String getName() {
+ return key;
}
- public static String serialize(byte value) {
- return Byte.toString(value);
+ @Override
+ public void set(char value) {
+ this.value = Character.toString(value);
}
- public static String serialize(short value) {
- return Short.toString(value);
+ @Override
+ public void set(String value) {
+ this.value = value;
}
- public static String serialize(int value) {
- return Integer.toString(value);
+ @Override
+ public void set(boolean value) {
+ this.value = (value ? "1" : "0");
}
- public static String serialize(long value) {
- return Long.toString(value);
+ @Override
+ public void set(byte value) {
+ this.value = Byte.toString(value);
}
- public static String serialize(float value) {
- return Float.toString(value);
+ @Override
+ public void set(short value) {
+ this.value = Short.toString(value);
}
- public static String serialize(double value) {
- return Double.toString(value);
+ @Override
+ public void set(int value) {
+ this.value = Integer.toString(value);
}
+ @Override
+ public void set(long value) {
+ this.value = Long.toString(value);
+ }
+
+ @Override
+ public void set(float value) {
+ this.value = Float.toString(value);
+ }
+
+ @Override
+ public void set(double value) {
+ this.value = Double.toString(value);
+ }
}
diff --git a/src/main/java/com/github/manevolent/ts3j/command/type/client/ClientInitIV.java b/src/main/java/com/github/manevolent/ts3j/command/type/client/ClientInitIV.java
new file mode 100644
index 0000000..73f29ae
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/command/type/client/ClientInitIV.java
@@ -0,0 +1,16 @@
+package com.github.manevolent.ts3j.command.type.client;
+
+import com.github.manevolent.ts3j.command.TypedCommand;
+import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
+import com.github.manevolent.ts3j.protocol.ProtocolRole;
+
+public class ClientInitIV extends TypedCommand {
+ public ClientInitIV() {
+ super(ProtocolRole.CLIENT, "clientinitiv");
+
+ set(new CommandSingleParameter("ot"));
+ set(new CommandSingleParameter("alpha"));
+ set(new CommandSingleParameter("omega"));
+ set(new CommandSingleParameter("ip"));
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandler.java b/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandler.java
deleted file mode 100644
index e6829c0..0000000
--- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.github.manevolent.ts3j.handler;
-
-import com.github.manevolent.ts3j.Teamspeak3Client;
-import com.github.manevolent.ts3j.ClientConnectionState;
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
-
-import java.io.IOException;
-
-public abstract class TeamspeakClientHandler {
- private final Teamspeak3Client client;
-
- protected TeamspeakClientHandler(Teamspeak3Client client) {
- this.client = client;
- }
-
- protected final Teamspeak3Client getClient() {
- return client;
- }
-
- public void handleConnectionStateChanging(ClientConnectionState clientConnectionState) {
-
- }
-
- public void onAssigned() throws IOException {
-
- }
-
- public void handlePacket(NetworkPacket packet) throws IOException {
-
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnected.java b/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnected.java
deleted file mode 100644
index 7b4fa1c..0000000
--- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnected.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.github.manevolent.ts3j.handler;
-
-import com.github.manevolent.ts3j.Teamspeak3Client;
-
-public class TeamspeakClientHandlerConnected extends TeamspeakClientHandler {
- public TeamspeakClientHandlerConnected(Teamspeak3Client client) {
- super(client);
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerDisconnected.java b/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerDisconnected.java
deleted file mode 100644
index 021f7e7..0000000
--- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerDisconnected.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.github.manevolent.ts3j.handler;
-
-import com.github.manevolent.ts3j.Teamspeak3Client;
-
-public class TeamspeakClientHandlerDisconnected extends TeamspeakClientHandler {
- public TeamspeakClientHandlerDisconnected(Teamspeak3Client client) {
- super(client);
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/identity/Identity.java b/src/main/java/com/github/manevolent/ts3j/identity/Identity.java
new file mode 100644
index 0000000..7ee5825
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/identity/Identity.java
@@ -0,0 +1,159 @@
+package com.github.manevolent.ts3j.identity;
+
+import com.github.manevolent.ts3j.util.Ts3Logging;
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERInteger;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+
+public class Identity {
+ private static MessageDigest sha1;
+
+ static {
+ try {
+ sha1 = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private final ECPoint publicKey;
+ private final String fingerprint;
+ private final String publicKeyString;
+
+ private long keyOffset = 0L, lastCheckedKeyOffset = 0L;
+
+ public Identity(ECPoint publicKey) {
+ this.publicKey = publicKey;
+
+ this.publicKeyString = Base64.getEncoder().encodeToString(encodePublicKey());
+ this.fingerprint = generateFingerprint();
+ }
+
+ public ECPoint getPublicKey() {
+ return publicKey;
+ }
+
+ public String getPublicKeyString() {
+ return publicKeyString;
+ }
+
+ public String getFingerprint() {
+ return fingerprint;
+ }
+
+ public long getLastCheckedKeyOffset() {
+ return lastCheckedKeyOffset;
+ }
+
+ public void setLastCheckedKeyOffset(long lastCheckedKeyOffset) {
+ this.lastCheckedKeyOffset = lastCheckedKeyOffset;
+ }
+
+ public long getKeyOffset() {
+ return keyOffset;
+ }
+
+ public void setKeyOffset(long keyOffset) {
+ this.keyOffset = keyOffset;
+ }
+
+ public void improveSecurity(int target) {
+ byte[] hashBuffer = new byte[getPublicKeyString().length() + 20];
+ byte[] pubKeyBytes = getPublicKeyString().getBytes(Charset.forName("ASCII"));
+ System.arraycopy(pubKeyBytes, 0, hashBuffer, 0, pubKeyBytes.length);
+
+
+ setLastCheckedKeyOffset(Math.max(getKeyOffset(), getLastCheckedKeyOffset()));
+ int best = getSecurityLevel(hashBuffer, pubKeyBytes.length, getKeyOffset());
+ while (true)
+ {
+ if (best >= target) return;
+
+ int curr = getSecurityLevel(hashBuffer, pubKeyBytes.length, lastCheckedKeyOffset);
+
+ if (curr > best)
+ {
+ Ts3Logging.debug("Improved identity security level: from " + best + " to " + curr);
+ keyOffset = lastCheckedKeyOffset;
+ best = curr;
+ }
+ lastCheckedKeyOffset ++;
+ }
+ }
+
+ public int getSecurityLevel() {
+ byte[] hashBuffer = new byte[getPublicKeyString().length() + 20];
+ byte[] pubKeyBytes = getPublicKeyString().getBytes(Charset.forName("ASCII"));
+ System.arraycopy(pubKeyBytes, 0, hashBuffer, 0, pubKeyBytes.length);
+
+ return getSecurityLevel(hashBuffer, pubKeyBytes.length, getKeyOffset());
+ }
+
+ private static int getSecurityLevel(byte[] hashBuffer, int pubKeyLen, long offset) {
+ byte[] numBuffer = new byte[20];
+ int numLen = 0;
+ do
+ {
+ numBuffer[numLen] = (byte)((offset % 10) & 0xFF);
+ offset /= 10;
+ numLen ++;
+ } while (offset > 0);
+
+ for (int i = 0; i < numLen; i ++)
+ hashBuffer[pubKeyLen + i] = numBuffer[numLen - (i + 1)];
+
+ byte[] outHash;
+ synchronized (sha1) {
+ sha1.update(hashBuffer, 0, pubKeyLen + numLen);
+ outHash = sha1.digest();
+ }
+
+ return getLeadingZeroBits(outHash);
+ }
+
+ // GOOD
+ private static int getLeadingZeroBits(byte[] data) {
+ int curr = 0;
+ int i ;
+ for (i = 0; i < data.length; i ++) {
+ if (data[i] == 0) curr += 8;
+ else break;
+ }
+ if (i < data.length)
+ for (int bit = 0; bit < 8; bit ++) {
+ if ((data[i] & (1 << bit)) == 0) curr++;
+ else break;
+ }
+ return curr;
+ }
+
+ private byte[] encodePublicKey() {
+ try {
+ byte[] dataArray = new DERSequence(new ASN1Encodable[]{
+ new DERBitString(new byte[]{0b0000_0000}, 7),
+ new DERInteger(32),
+ new DERInteger(publicKey.getAffineXCoord().toBigInteger()),
+ new DERInteger(publicKey.getAffineYCoord().toBigInteger())
+ }).getEncoded();
+
+ return dataArray;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String generateFingerprint() {
+ synchronized (sha1) {
+ return Base64.getEncoder().encodeToString(sha1.digest(encodePublicKey()));
+ }
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/identity/LocalIdentity.java b/src/main/java/com/github/manevolent/ts3j/identity/LocalIdentity.java
new file mode 100644
index 0000000..fd9fabf
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/identity/LocalIdentity.java
@@ -0,0 +1,59 @@
+package com.github.manevolent.ts3j.identity;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+import java.security.*;
+
+public class LocalIdentity extends Identity {
+ static {
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+ }
+
+ private final BigInteger privateKey;
+
+ public LocalIdentity(ECPoint publicKey, BigInteger privateKey) {
+ super(publicKey);
+
+ this.privateKey = privateKey;
+ }
+
+ public static LocalIdentity load(ECPoint publicKey,
+ BigInteger privateKey,
+ long keyOffset,
+ long lastCheckedKeyOffset) {
+ LocalIdentity localIdentity = new LocalIdentity(publicKey, privateKey);
+
+ localIdentity.setKeyOffset(keyOffset);
+ localIdentity.setLastCheckedKeyOffset(lastCheckedKeyOffset);
+
+ return localIdentity;
+ }
+
+ public static LocalIdentity generateNew(int securityLevel) throws GeneralSecurityException {
+ ECNamedCurveParameterSpec ecp = ECNamedCurveTable.getParameterSpec("prime256v1");
+ ECDomainParameters domainParams =
+ new ECDomainParameters(ecp.getCurve(), ecp.getG(), ecp.getN(), ecp.getH(), ecp.getSeed());
+ ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, new SecureRandom());
+
+ ECKeyPairGenerator generator = new ECKeyPairGenerator();
+ generator.init(keyGenParams);
+
+ AsymmetricCipherKeyPair keyPair = generator.generateKeyPair();
+ ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate();
+ ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();
+
+ LocalIdentity localIdentity = load(publicKey.getQ().normalize(), privateKey.getD(), 0, 0);
+ localIdentity.improveSecurity(securityLevel);
+
+ return localIdentity;
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java
new file mode 100644
index 0000000..ed71802
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java
@@ -0,0 +1,13 @@
+package com.github.manevolent.ts3j.protocol;
+
+import com.github.manevolent.ts3j.protocol.packet.Packet;
+
+import java.io.IOException;
+
+public interface Endpoint {
+
+ SocketRole getRole();
+
+ void send(Packet packet) throws IOException;
+
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java
new file mode 100644
index 0000000..4d9a299
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java
@@ -0,0 +1,109 @@
+package com.github.manevolent.ts3j.protocol;
+
+import com.github.manevolent.ts3j.identity.LocalIdentity;
+import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
+import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
+import com.github.manevolent.ts3j.util.Ts3Logging;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+
+public abstract class LocalEndpoint implements Endpoint {
+ private final LocalTeamspeakSocket socket;
+
+
+ private PacketHandler handler = null;
+ private LocalIdentity localIdentity = null;
+
+ private final Object runLock = new Object();
+ private Thread receiverThread;
+ private boolean running = false;
+
+ protected LocalEndpoint(LocalTeamspeakSocket socket) {
+ this.socket = socket;
+ }
+
+ protected LocalEndpoint(SocketRole socketRole) throws SocketException {
+ this.socket = new LocalTeamspeakSocket(socketRole);
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return getSocket().getLocalAddress();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return getSocket().getLocalAddress();
+ }
+
+ protected final LocalTeamspeakSocket getSocket() {
+ return socket;
+ }
+
+ public void setRunning(boolean running) {
+ if (this.running != running) {
+ this.running = running;
+
+ if (running) {
+ Ts3Logging.debug("Starting network receiver thread...");
+
+ receiverThread = new Thread(new PacketReceiver());
+ receiverThread.setDaemon(true);
+ receiverThread.start();
+ } else {
+ if (receiverThread != null) {
+ receiverThread.interrupt();
+ receiverThread = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public SocketRole getRole() {
+ return socket.getSocketRole();
+ }
+
+ private NetworkPacket receive() throws IOException {
+ return socket.receive();
+ }
+
+ public void setHandler(PacketHandler handler) throws IOException {
+ if (this.handler != handler) {
+ if (this.handler != null) this.handler.onUnassigning();
+ this.handler = handler;
+ if (handler != null) handler.onAssigned();
+ }
+ }
+
+ protected void handlePacket(NetworkPacket packet) throws IOException {
+ if (this.handler != null) this.handler.handlePacket(packet);
+ }
+
+ public LocalIdentity getLocalIdentity() {
+ return localIdentity;
+ }
+
+ public void setLocalIdentity(LocalIdentity localIdentity) {
+ this.localIdentity = localIdentity;
+ }
+
+ private class PacketReceiver implements Runnable {
+ @Override
+ public void run() {
+ Ts3Logging.debug("Entering packet receiver");
+
+ while (running) {
+ try {
+ handlePacket(receive());
+ } catch (Exception e) {
+ if (e instanceof InterruptedException) continue;
+
+ Ts3Logging.debug("Problem handling packet", e);
+ }
+ }
+
+ Ts3Logging.debug("Leaving packet receiver");
+ }
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java
new file mode 100644
index 0000000..3899f4c
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java
@@ -0,0 +1,7 @@
+package com.github.manevolent.ts3j.protocol;
+
+import java.net.InetSocketAddress;
+
+public abstract class RemoteEndpoint implements Endpoint {
+ public abstract InetSocketAddress getRemoteAddress();
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java
new file mode 100644
index 0000000..2796847
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java
@@ -0,0 +1,12 @@
+package com.github.manevolent.ts3j.protocol;
+
+import com.github.manevolent.ts3j.protocol.header.PacketHeader;
+import com.github.manevolent.ts3j.protocol.packet.Packet;
+
+import java.io.IOException;
+
+public interface TeamspeakSocket {
+ SocketRole getSocketRole();
+
+ void send(PacketHeader header, Packet packet) throws IOException;
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java
new file mode 100644
index 0000000..6979b0d
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java
@@ -0,0 +1,31 @@
+
+package com.github.manevolent.ts3j.protocol.client;
+
+import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandler;
+import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerConnected;
+import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerConnecting;
+import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerDisconnected;
+
+public enum ClientConnectionState {
+ CONNECTING(LocalClientHandlerConnecting.class),
+ CONNECTED(LocalClientHandlerConnected.class),
+ DISCONNECTED(LocalClientHandlerDisconnected.class);
+
+ private final Class extends LocalClientHandler> clazz;
+
+ ClientConnectionState(Class extends LocalClientHandler> clazz) {
+ this.clazz = clazz;
+ }
+
+ public Class extends LocalClientHandler> getHandlerClass() {
+ return clazz;
+ }
+
+ public LocalClientHandler createHandler(LocalTeamspeakClient client) {
+ try {
+ return clazz.getConstructor(LocalTeamspeakClient.class).newInstance(client);
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/manevolent/ts3j/Teamspeak3Client.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java
similarity index 63%
rename from src/main/java/com/github/manevolent/ts3j/Teamspeak3Client.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java
index 4126d13..3b0a502 100644
--- a/src/main/java/com/github/manevolent/ts3j/Teamspeak3Client.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java
@@ -1,50 +1,71 @@
-package com.github.manevolent.ts3j;
+package com.github.manevolent.ts3j.protocol.client;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.command.response.CommandResponse;
-import com.github.manevolent.ts3j.handler.TeamspeakClientHandler;
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.identity.LocalIdentity;
+import com.github.manevolent.ts3j.protocol.LocalEndpoint;
import com.github.manevolent.ts3j.protocol.SocketRole;
-import com.github.manevolent.ts3j.protocol.Teamspeak3Socket;
import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader;
+import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandler;
+import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
import com.github.manevolent.ts3j.protocol.packet.Packet;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.io.IOException;
-import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
-public class Teamspeak3Client {
- private final PacketReceiver packetReceiver = new PacketReceiver();
-
- private final Teamspeak3Socket socket;
+public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient {
private final Object connectionStateLock = new Object();
private final Map clientOptions = new LinkedHashMap<>();
- private TeamspeakClientHandler handler;
-
- private InetSocketAddress remote = null;
+ private LocalClientHandler handler;
private ClientConnectionState clientConnectionState = null;
+
private int returnCodeIndex = 0;
- public Teamspeak3Client(DatagramSocket datagramSocket) throws SocketException {
- this.socket = new Teamspeak3Socket(SocketRole.CLIENT, datagramSocket);
+ public LocalTeamspeakClient(LocalTeamspeakSocket socket) {
+ super(socket);
+
+ if (socket.getSocketRole() != SocketRole.CLIENT)
+ throw new IllegalArgumentException("invalid socket role: " + socket.getSocketRole().name());
setClientConnectionState(ClientConnectionState.DISCONNECTED);
}
- public Teamspeak3Client() throws SocketException {
- this(new DatagramSocket());
+ public CommandResponse sendCommand(Command command) throws IOException {
+ if (command.willExpectResponse() && !isConnected())
+ throw new IOException("not connected");
+
+ CommandResponse response;
+
+ if (command.willExpectResponse()) {
+ int returnCodeIndex = ++ this.returnCodeIndex;
+ //command.appendParameter(new CommandSingleParameter("return_code", returnCodeIndex));
+ response = new CommandResponse(command, returnCodeIndex);
+ } else {
+ response = new CommandResponse(command, -1);
+ }
+
+ String commandString = command.toString();
+
+ response.setDispatchedTime(System.currentTimeMillis());
+
+ return response;
}
- public InetSocketAddress getRemote() {
- return remote;
+ public LocalTeamspeakClient() throws SocketException {
+ this(new LocalTeamspeakSocket(SocketRole.CLIENT));
+ }
+
+ @Override
+ public SocketRole getRole() {
+ return SocketRole.CLIENT;
}
public void send(Packet packet) throws IOException {
@@ -53,7 +74,7 @@ public class Teamspeak3Client {
packet.setHeaderValues(header);
- socket.send(header, packet);
+ getSocket().send(header, packet);
}
protected void setClientConnectionState(ClientConnectionState state) {
@@ -65,13 +86,8 @@ public class Teamspeak3Client {
this.clientConnectionState = state;
- if (wasDisconnected) {
- Ts3Logging.debug("Starting network receiver thread...");
-
- Thread receiverThread = new Thread(new PacketReceiver());
- receiverThread.setDaemon(true);
- receiverThread.start();
- }
+ if (wasDisconnected)
+ setRunning(true);
if (handler != null)
handler.handleConnectionStateChanging(state);
@@ -82,7 +98,7 @@ public class Teamspeak3Client {
handler = state.createHandler(this);
try {
- handler.onAssigned();
+ setHandler(handler);
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -102,6 +118,11 @@ public class Teamspeak3Client {
return clientConnectionState == ClientConnectionState.CONNECTED;
}
+ @Override
+ public Map getOptions() {
+ return clientOptions;
+ }
+
public ClientConnectionState getClientConnectionState() {
return clientConnectionState;
}
@@ -120,46 +141,6 @@ public class Teamspeak3Client {
}
}
- public T getOption(String key, Class clazz) {
- Object value = clientOptions.get(key);
-
- if (value == null) return (T) null;
-
- else if (!value.getClass().isAssignableFrom(clazz)) {
- throw new ClassCastException(
- clazz.getName()
- + " is not assignable to option type " +
- value.getClass().getName()
- );
- } else
- return (T) value;
- }
-
- public Object setOption(String key, Object value) {
- return clientOptions.put(key, value);
- }
-
- public CommandResponse sendCommand(Command command) throws IOException {
- if (command.willExpectResponse() && !isConnected())
- throw new IOException("not connected");
-
- CommandResponse response;
-
- if (command.willExpectResponse()) {
- int returnCodeIndex = ++ this.returnCodeIndex;
- command.appendParameter(new CommandSingleParameter("return_code", returnCodeIndex));
- response = new CommandResponse(command, returnCodeIndex);
- } else {
- response = new CommandResponse(command, -1);
- }
-
- String commandString = command.toString();
-
- response.setDispatchedTime(System.currentTimeMillis());
-
- return response;
- }
-
public void connect(InetSocketAddress remote, String password, long timeout) throws IOException {
try {
if (clientConnectionState != ClientConnectionState.DISCONNECTED)
@@ -168,7 +149,7 @@ public class Teamspeak3Client {
setOption("client.hostname", remote.getHostString());
setOption("client.password", password);
- socket.connect(remote);
+ getSocket().connect(remote);
setClientConnectionState(ClientConnectionState.CONNECTING);
joinConnectionState(ClientConnectionState.CONNECTED, timeout);
@@ -178,25 +159,4 @@ public class Teamspeak3Client {
throw new IOException(e);
}
}
-
- private void handlePacket(NetworkPacket packet) throws Exception {
- if (handler != null) handler.handlePacket(packet);
- }
-
- private class PacketReceiver implements Runnable {
- @Override
- public void run() {
- Ts3Logging.debug("Entering packet receiver");
-
- while (clientConnectionState != ClientConnectionState.DISCONNECTED) {
- try {
- handlePacket(socket.receive());
- } catch (Exception e) {
- Ts3Logging.debug("Problem handling packet", e);
- }
- }
-
- Ts3Logging.debug("Leaving packet receiver");
- }
- }
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java
new file mode 100644
index 0000000..b90ef8d
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java
@@ -0,0 +1,37 @@
+package com.github.manevolent.ts3j.protocol.client;
+
+import com.github.manevolent.ts3j.protocol.Endpoint;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+
+public interface TeamspeakClient extends Endpoint {
+ void connect(InetSocketAddress remote, String password, long timeout) throws IOException;
+
+ SocketAddress getRemoteAddress();
+
+ boolean isConnected();
+
+ Map getOptions();
+
+ default T getOption(String key, Class clazz) {
+ Object value = getOptions().get(key);
+
+ if (value == null) return (T) null;
+
+ else if (!value.getClass().isAssignableFrom(clazz)) {
+ throw new ClassCastException(
+ clazz.getName()
+ + " is not assignable to option type " +
+ value.getClass().getName()
+ );
+ } else
+ return (T) value;
+ }
+
+ default Object setOption(String key, Object value) {
+ return getOptions().put(key, value);
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java
index 2a18fe3..480477d 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java
@@ -90,6 +90,9 @@ public class Packet8Init1 extends Packet {
case 3:
step = new Step3();
break;
+ case 4:
+ step = new Step4();
+ break;
default:
throw new IllegalArgumentException("invalid Init1 step: " + stepNumber);
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java
new file mode 100644
index 0000000..871796a
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java
@@ -0,0 +1,30 @@
+package com.github.manevolent.ts3j.protocol.packet.handler;
+
+import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.protocol.LocalEndpoint;
+
+import java.io.IOException;
+
+public abstract class PacketHandler {
+ private final LocalEndpoint endpoint;
+
+ protected PacketHandler(LocalEndpoint endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ public void onUnassigning() throws IOException {
+
+ }
+
+ public void onAssigned() throws IOException {
+
+ }
+
+ public void handlePacket(NetworkPacket packet) throws IOException {
+
+ }
+
+ public LocalEndpoint getEndpoint() {
+ return endpoint;
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java
new file mode 100644
index 0000000..c98c40f
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java
@@ -0,0 +1,26 @@
+package com.github.manevolent.ts3j.protocol.packet.handler.client;
+
+import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
+
+import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
+import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
+
+import java.io.IOException;
+
+public abstract class LocalClientHandler extends PacketHandler {
+ private final LocalTeamspeakClient client;
+
+ protected LocalClientHandler(LocalTeamspeakClient client) {
+ super(client);
+
+ this.client = client;
+ }
+
+ public void handleConnectionStateChanging(ClientConnectionState clientConnectionState) {
+
+ }
+
+ protected LocalTeamspeakClient getClient() {
+ return client;
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java
new file mode 100644
index 0000000..d33da6b
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java
@@ -0,0 +1,9 @@
+package com.github.manevolent.ts3j.protocol.packet.handler.client;
+
+import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
+
+public class LocalClientHandlerConnected extends LocalClientHandler {
+ public LocalClientHandlerConnected(LocalTeamspeakClient client) {
+ super(client);
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnecting.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java
similarity index 77%
rename from src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnecting.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java
index 6c3e3c4..153c825 100644
--- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnecting.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java
@@ -1,20 +1,23 @@
-package com.github.manevolent.ts3j.handler;
+package com.github.manevolent.ts3j.protocol.packet.handler.client;
-import com.github.manevolent.ts3j.Teamspeak3Client;
-import com.github.manevolent.ts3j.command.Command;
+import com.github.manevolent.ts3j.command.type.client.ClientInitIV;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
+import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
import com.github.manevolent.ts3j.protocol.packet.Packet8Init1;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.io.IOException;
import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.Base64;
import java.util.Random;
-public class TeamspeakClientHandlerConnecting extends TeamspeakClientHandler {
+public class LocalClientHandlerConnecting extends LocalClientHandler {
private byte[] randomBytes;
+ private byte[] alphaBytes;
- public TeamspeakClientHandlerConnecting(Teamspeak3Client client) {
+ public LocalClientHandlerConnecting(LocalTeamspeakClient client) {
super(client);
}
@@ -90,10 +93,12 @@ public class TeamspeakClientHandlerConnecting extends TeamspeakClientHandler {
byte[] solution =
x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n).toByteArray();
+ Ts3Logging.debug(Ts3Logging.getHex(solution));
+
System.arraycopy(
- solution, Math.max(0, solution.length - 64),
- y, 64 - solution.length,
- solution.length
+ solution, Math.abs(solution.length - 64),
+ y, solution.length < 64 ? 64 - solution.length : 0,
+ solution.length < 64 ? solution.length : 64
);
// Build response
@@ -107,8 +112,17 @@ public class TeamspeakClientHandlerConnecting extends TeamspeakClientHandler {
step4.setY(y);
step4.setServerStuff(serverReplyStep3.getServerStuff());
+ alphaBytes = new byte[10];
+ new Random().nextBytes(alphaBytes);
- step4.setClientIVcommand(new byte[4]); // TODO
+ ClientInitIV command = new ClientInitIV();
+
+ command.get("alpha").set(Base64.getEncoder().encodeToString(alphaBytes));
+ command.get("omega").set(getClient().getLocalIdentity().getPublicKeyString());
+ command.get("ot").set(1); // ?
+ command.get("ip").set(null);
+
+ step4.setClientIVcommand(command.build().getBytes(Charset.forName("UTF8")));
response4.setStep(step4);
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java
new file mode 100644
index 0000000..e57aa51
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java
@@ -0,0 +1,9 @@
+package com.github.manevolent.ts3j.protocol.packet.handler.client;
+
+import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
+
+public class LocalClientHandlerDisconnected extends LocalClientHandler {
+ public LocalClientHandlerDisconnected(LocalTeamspeakClient client) {
+ super(client);
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java b/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java
new file mode 100644
index 0000000..4ca98b9
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java
@@ -0,0 +1,36 @@
+package com.github.manevolent.ts3j.protocol.server;
+
+import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.protocol.RemoteEndpoint;
+import com.github.manevolent.ts3j.protocol.SocketRole;
+import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
+import com.github.manevolent.ts3j.protocol.packet.Packet;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+public class RemoteTeamspeakServer extends RemoteEndpoint implements TeamspeakServer {
+ private final LocalTeamspeakClient client;
+ private final InetSocketAddress address;
+
+ public RemoteTeamspeakServer(LocalTeamspeakClient client,
+ InetSocketAddress address) {
+ this.client = client;
+ this.address = address;
+ }
+
+ @Override
+ public SocketRole getRole() {
+ return SocketRole.SERVER;
+ }
+
+ @Override
+ public void send(Packet packet) throws IOException {
+ client.send(packet);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress() {
+ return address;
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java b/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java
new file mode 100644
index 0000000..2687855
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java
@@ -0,0 +1,11 @@
+package com.github.manevolent.ts3j.protocol.server;
+
+import com.github.manevolent.ts3j.protocol.Endpoint;
+
+import java.net.InetSocketAddress;
+
+public interface TeamspeakServer extends Endpoint {
+
+
+
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Teamspeak3Socket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java
similarity index 82%
rename from src/main/java/com/github/manevolent/ts3j/protocol/Teamspeak3Socket.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java
index 194f458..b0ff10d 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/Teamspeak3Socket.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java
@@ -1,14 +1,14 @@
-package com.github.manevolent.ts3j.protocol;
+package com.github.manevolent.ts3j.protocol.socket;
+import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.protocol.SocketRole;
+import com.github.manevolent.ts3j.protocol.TeamspeakSocket;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.protocol.packet.Packet;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.SocketAddress;
-import java.net.SocketException;
+import java.net.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -17,18 +17,18 @@ import java.nio.ByteOrder;
*
* Used simply to transcribe packets on the network
*/
-public final class Teamspeak3Socket {
+public class LocalTeamspeakSocket implements TeamspeakSocket {
private final SocketRole socketRole;
private final DatagramSocket datagramSocket;
private final DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500);
- public Teamspeak3Socket(SocketRole socketRole, DatagramSocket datagramSocket) {
+ public LocalTeamspeakSocket(SocketRole socketRole, DatagramSocket datagramSocket) {
this.socketRole = socketRole;
this.datagramSocket = datagramSocket;
}
- public Teamspeak3Socket(SocketRole socketRole) throws SocketException {
+ public LocalTeamspeakSocket(SocketRole socketRole) throws SocketException {
this(socketRole, new DatagramSocket());
}
@@ -36,6 +36,14 @@ public final class Teamspeak3Socket {
return socketRole;
}
+ public InetSocketAddress getLocalAddress() {
+ return new InetSocketAddress(datagramSocket.getLocalAddress(), datagramSocket.getLocalPort());
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return new InetSocketAddress(datagramSocket.getInetAddress(), datagramSocket.getPort());
+ }
+
public void connect(SocketAddress remote) throws SocketException {
datagramSocket.connect(remote);
}
@@ -116,4 +124,5 @@ public final class Teamspeak3Socket {
return networkPacket;
}
+
}
diff --git a/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java b/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java
index 7e7fe38..76dbba6 100644
--- a/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java
+++ b/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java
@@ -18,8 +18,8 @@ public final class Ts3Logging {
System.err.println("[INFO] " + message);
}
- public static void debug(String message) {
- System.err.println("[DEBUG] " + message);
+ public static void debug(Object message) {
+ System.err.println("[DEBUG] " + message.toString());
}
public static void debug(String message, Throwable ex) {
diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java
index 9f1cbf4..459c535 100644
--- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java
+++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java
@@ -1,5 +1,8 @@
package com.github.manevolent.ts3j;
+import com.github.manevolent.ts3j.identity.LocalIdentity;
+import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
+import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
import junit.framework.TestCase;
import java.net.InetSocketAddress;
@@ -10,15 +13,23 @@ public class ServerConnectionTest extends TestCase {
}
public void testParser() throws Exception {
- Teamspeak3Client client = new Teamspeak3Client();
+ try {
+ LocalIdentity localIdentity = LocalIdentity.generateNew(8);
- client.connect(new InetSocketAddress(
- "voice.teamspeak.com",
- 9987),
- null,
- 10000L
- );
+ LocalTeamspeakClient client = new LocalTeamspeakClient();
- assertEquals(client.getClientConnectionState(), ClientConnectionState.CONNECTED);
+ client.setLocalIdentity(localIdentity);
+
+ client.connect(new InetSocketAddress(
+ "voice.teamspeak.com",
+ 9987),
+ null,
+ 10000L
+ );
+
+ assertEquals(client.getClientConnectionState(), ClientConnectionState.CONNECTED);
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
}
}