mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Extract SignalAccount from Manager
This commit is contained in:
parent
701328b8c2
commit
35c72f692f
30 changed files with 793 additions and 549 deletions
8
.idea/codeStyles/Project.xml
generated
8
.idea/codeStyles/Project.xml
generated
|
@ -2,10 +2,18 @@
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<option name="LINE_SEPARATOR" value=" " />
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
<JavaCodeStyleSettings>
|
<JavaCodeStyleSettings>
|
||||||
|
<option name="GENERATE_FINAL_LOCALS" value="true" />
|
||||||
|
<option name="GENERATE_FINAL_PARAMETERS" value="true" />
|
||||||
<option name="JD_P_AT_EMPTY_LINES" value="false" />
|
<option name="JD_P_AT_EMPTY_LINES" value="false" />
|
||||||
</JavaCodeStyleSettings>
|
</JavaCodeStyleSettings>
|
||||||
<XML>
|
<XML>
|
||||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||||
</XML>
|
</XML>
|
||||||
|
<codeStyleSettings language="JAVA">
|
||||||
|
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||||
|
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||||
|
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||||
|
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
||||||
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
|
@ -20,7 +20,7 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.github.turasa:signal-service-java:2.12.2_unofficial_1'
|
compile 'com.github.turasa:signal-service-java:2.12.2_unofficial_2'
|
||||||
compile 'org.bouncycastle:bcprov-jdk15on:1.60'
|
compile 'org.bouncycastle:bcprov-jdk15on:1.60'
|
||||||
compile 'net.sourceforge.argparse4j:argparse4j:0.8.1'
|
compile 'net.sourceforge.argparse4j:argparse4j:0.8.1'
|
||||||
compile 'org.freedesktop.dbus:dbus-java:2.7.0'
|
compile 'org.freedesktop.dbus:dbus-java:2.7.0'
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface Signal extends DBusInterface {
|
public interface Signal extends DBusInterface {
|
||||||
|
|
||||||
void sendMessage(String message, List<String> attachments, String recipient) throws EncapsulatedExceptions, AttachmentInvalidException, IOException;
|
void sendMessage(String message, List<String> attachments, String recipient) throws EncapsulatedExceptions, AttachmentInvalidException, IOException;
|
||||||
|
|
||||||
void sendMessage(String message, List<String> attachments, List<String> recipients) throws EncapsulatedExceptions, AttachmentInvalidException, IOException;
|
void sendMessage(String message, List<String> attachments, List<String> recipients) throws EncapsulatedExceptions, AttachmentInvalidException, IOException;
|
||||||
|
@ -32,6 +33,7 @@ public interface Signal extends DBusInterface {
|
||||||
byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException;
|
byte[] updateGroup(byte[] groupId, String name, List<String> members, String avatar) throws IOException, EncapsulatedExceptions, GroupNotFoundException, AttachmentInvalidException;
|
||||||
|
|
||||||
class MessageReceived extends DBusSignal {
|
class MessageReceived extends DBusSignal {
|
||||||
|
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
private String sender;
|
private String sender;
|
||||||
private byte[] groupId;
|
private byte[] groupId;
|
||||||
|
@ -69,6 +71,7 @@ public interface Signal extends DBusInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReceiptReceived extends DBusSignal {
|
class ReceiptReceived extends DBusSignal {
|
||||||
|
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
private String sender;
|
private String sender;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.asamk.signal;
|
||||||
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
import org.freedesktop.dbus.exceptions.DBusExecutionException;
|
||||||
|
|
||||||
public class AttachmentInvalidException extends DBusExecutionException {
|
public class AttachmentInvalidException extends DBusExecutionException {
|
||||||
|
|
||||||
public AttachmentInvalidException(String message) {
|
public AttachmentInvalidException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
|
||||||
|
|
||||||
class JsonAttachment {
|
class JsonAttachment {
|
||||||
|
|
||||||
String contentType;
|
String contentType;
|
||||||
long id;
|
long id;
|
||||||
int size;
|
int size;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.whispersystems.signalservice.api.messages.calls.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class JsonCallMessage {
|
class JsonCallMessage {
|
||||||
|
|
||||||
OfferMessage offerMessage;
|
OfferMessage offerMessage;
|
||||||
AnswerMessage answerMessage;
|
AnswerMessage answerMessage;
|
||||||
BusyMessage busyMessage;
|
BusyMessage busyMessage;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class JsonDataMessage {
|
class JsonDataMessage {
|
||||||
|
|
||||||
long timestamp;
|
long timestamp;
|
||||||
String message;
|
String message;
|
||||||
int expiresInSeconds;
|
int expiresInSeconds;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.asamk.signal;
|
package org.asamk.signal;
|
||||||
|
|
||||||
class JsonError {
|
class JsonError {
|
||||||
|
|
||||||
String message;
|
String message;
|
||||||
|
|
||||||
JsonError(Throwable exception) {
|
JsonError(Throwable exception) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.whispersystems.signalservice.internal.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class JsonGroupInfo {
|
class JsonGroupInfo {
|
||||||
|
|
||||||
String groupId;
|
String groupId;
|
||||||
List<String> members;
|
List<String> members;
|
||||||
String name;
|
String name;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
|
||||||
class JsonMessageEnvelope {
|
class JsonMessageEnvelope {
|
||||||
|
|
||||||
String source;
|
String source;
|
||||||
int sourceDevice;
|
int sourceDevice;
|
||||||
String relay;
|
String relay;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSy
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class JsonSyncMessage {
|
class JsonSyncMessage {
|
||||||
|
|
||||||
JsonDataMessage sentMessage;
|
JsonDataMessage sentMessage;
|
||||||
List<String> blockedNumbers;
|
List<String> blockedNumbers;
|
||||||
List<ReadMessage> readMessages;
|
List<ReadMessage> readMessages;
|
||||||
|
|
|
@ -129,15 +129,13 @@ public class Main {
|
||||||
|
|
||||||
m = new Manager(username, settingsPath);
|
m = new Manager(username, settingsPath);
|
||||||
ts = m;
|
ts = m;
|
||||||
if (m.userExists()) {
|
|
||||||
try {
|
try {
|
||||||
m.init();
|
m.init();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("Error loading state file \"" + m.getFileName() + "\": " + e.getMessage());
|
System.err.println("Error loading state file: " + e.getMessage());
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
switch (ns.getString("command")) {
|
switch (ns.getString("command")) {
|
||||||
case "register":
|
case "register":
|
||||||
|
@ -145,9 +143,6 @@ public class Main {
|
||||||
System.err.println("register is not yet implemented via dbus");
|
System.err.println("register is not yet implemented via dbus");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!m.userHasKeys()) {
|
|
||||||
m.createNewIdentity();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
m.register(ns.getBoolean("voice"));
|
m.register(ns.getBoolean("voice"));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -252,9 +247,6 @@ public class Main {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When linking, username is null and we always have to create keys
|
|
||||||
m.createNewIdentity();
|
|
||||||
|
|
||||||
String deviceName = ns.getString("name");
|
String deviceName = ns.getString("name");
|
||||||
if (deviceName == null) {
|
if (deviceName == null) {
|
||||||
deviceName = "cli";
|
deviceName = "cli";
|
||||||
|
@ -736,7 +728,6 @@ public class Main {
|
||||||
System.err.println("Aborting sending.");
|
System.err.println("Aborting sending.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void handleDBusExecutionException(DBusExecutionException e) {
|
private static void handleDBusExecutionException(DBusExecutionException e) {
|
||||||
System.err.println("Cannot connect to dbus: " + e.getMessage());
|
System.err.println("Cannot connect to dbus: " + e.getMessage());
|
||||||
System.err.println("Aborting.");
|
System.err.println("Aborting.");
|
||||||
|
@ -949,6 +940,7 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
private static class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||||
|
|
||||||
final Manager m;
|
final Manager m;
|
||||||
|
|
||||||
public ReceiveMessageHandler(Manager m) {
|
public ReceiveMessageHandler(Manager m) {
|
||||||
|
@ -1212,6 +1204,7 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DbusReceiveMessageHandler extends ReceiveMessageHandler {
|
private static class DbusReceiveMessageHandler extends ReceiveMessageHandler {
|
||||||
|
|
||||||
final DBusConnection conn;
|
final DBusConnection conn;
|
||||||
|
|
||||||
public DbusReceiveMessageHandler(Manager m, DBusConnection conn) {
|
public DbusReceiveMessageHandler(Manager m, DBusConnection conn) {
|
||||||
|
@ -1228,6 +1221,7 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
private static class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
||||||
|
|
||||||
final Manager m;
|
final Manager m;
|
||||||
final ObjectMapper jsonProcessor;
|
final ObjectMapper jsonProcessor;
|
||||||
|
|
||||||
|
@ -1259,6 +1253,7 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler {
|
private static class JsonDbusReceiveMessageHandler extends JsonReceiveMessageHandler {
|
||||||
|
|
||||||
final DBusConnection conn;
|
final DBusConnection conn;
|
||||||
|
|
||||||
public JsonDbusReceiveMessageHandler(Manager m, DBusConnection conn) {
|
public JsonDbusReceiveMessageHandler(Manager m, DBusConnection conn) {
|
||||||
|
@ -1266,13 +1261,6 @@ public class Main {
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
|
|
||||||
super.handleMessage(envelope, content, exception);
|
|
||||||
|
|
||||||
sendReceivedMessageToDbus(envelope, content, conn, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendReceivedMessageToDbus(SignalServiceEnvelope envelope, SignalServiceContent content, DBusConnection conn, Manager m) {
|
private static void sendReceivedMessageToDbus(SignalServiceEnvelope envelope, SignalServiceContent content, DBusConnection conn, Manager m) {
|
||||||
if (envelope.isReceipt()) {
|
if (envelope.isReceipt()) {
|
||||||
try {
|
try {
|
||||||
|
@ -1313,5 +1301,12 @@ public class Main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
|
||||||
|
super.handleMessage(envelope, content, exception);
|
||||||
|
|
||||||
|
sendReceivedMessageToDbus(envelope, content, conn, m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.asamk.signal;
|
package org.asamk.signal;
|
||||||
|
|
||||||
public class UserAlreadyExists extends Exception {
|
public class UserAlreadyExists extends Exception {
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private String fileName;
|
private String fileName;
|
||||||
|
|
||||||
|
|
|
@ -8,26 +8,25 @@ import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl;
|
||||||
|
|
||||||
public class BaseConfig {
|
public class BaseConfig {
|
||||||
|
|
||||||
private BaseConfig() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public final static String PROJECT_NAME = Manager.class.getPackage().getImplementationTitle();
|
public final static String PROJECT_NAME = Manager.class.getPackage().getImplementationTitle();
|
||||||
public final static String PROJECT_VERSION = Manager.class.getPackage().getImplementationVersion();
|
public final static String PROJECT_VERSION = Manager.class.getPackage().getImplementationVersion();
|
||||||
|
|
||||||
final static String USER_AGENT = PROJECT_NAME == null ? null : PROJECT_NAME + " " + PROJECT_VERSION;
|
final static String USER_AGENT = PROJECT_NAME == null ? null : PROJECT_NAME + " " + PROJECT_VERSION;
|
||||||
|
final static String UNIDENTIFIED_SENDER_TRUST_ROOT = "BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF";
|
||||||
|
final static int PREKEY_MINIMUM_COUNT = 20;
|
||||||
|
final static int PREKEY_BATCH_SIZE = 100;
|
||||||
|
final static int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024;
|
||||||
|
|
||||||
private final static String URL = "https://textsecure-service.whispersystems.org";
|
private final static String URL = "https://textsecure-service.whispersystems.org";
|
||||||
private final static String CDN_URL = "https://cdn.signal.org";
|
private final static String CDN_URL = "https://cdn.signal.org";
|
||||||
|
|
||||||
private final static TrustStore TRUST_STORE = new WhisperTrustStore();
|
private final static TrustStore TRUST_STORE = new WhisperTrustStore();
|
||||||
|
|
||||||
final static SignalServiceConfiguration serviceConfiguration = new SignalServiceConfiguration(
|
final static SignalServiceConfiguration serviceConfiguration = new SignalServiceConfiguration(
|
||||||
new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)},
|
new SignalServiceUrl[]{new SignalServiceUrl(URL, TRUST_STORE)},
|
||||||
new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)},
|
new SignalCdnUrl[]{new SignalCdnUrl(CDN_URL, TRUST_STORE)},
|
||||||
new SignalContactDiscoveryUrl[0]
|
new SignalContactDiscoveryUrl[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
final static String UNIDENTIFIED_SENDER_TRUST_ROOT = "BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF";
|
private BaseConfig() {
|
||||||
|
}
|
||||||
final static int PREKEY_MINIMUM_COUNT = 20;
|
|
||||||
static final int PREKEY_BATCH_SIZE = 100;
|
|
||||||
static final int MAX_ATTACHMENT_SIZE = 150 * 1024 * 1024;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
package org.asamk.signal.util;
|
package org.asamk.signal.manager;
|
||||||
|
|
||||||
import org.whispersystems.signalservice.internal.util.Base64;
|
import org.whispersystems.signalservice.internal.util.Base64;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
public class KeyUtils {
|
class KeyUtils {
|
||||||
|
|
||||||
private KeyUtils() {
|
private KeyUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String createSignalingKey() {
|
static String createSignalingKey() {
|
||||||
return getSecret(52);
|
return getSecret(52);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] createProfileKey() {
|
static byte[] createProfileKey() {
|
||||||
return getSecretBytes(32);
|
return getSecretBytes(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String createPassword() {
|
static String createPassword() {
|
||||||
return getSecret(18);
|
return getSecret(18);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] createGroupId() {
|
static byte[] createGroupId() {
|
||||||
return getSecretBytes(16);
|
return getSecretBytes(16);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
321
src/main/java/org/asamk/signal/storage/SignalAccount.java
Normal file
321
src/main/java/org/asamk/signal/storage/SignalAccount.java
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
package org.asamk.signal.storage;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import org.asamk.signal.storage.contacts.JsonContactsStore;
|
||||||
|
import org.asamk.signal.storage.groups.JsonGroupStore;
|
||||||
|
import org.asamk.signal.storage.protocol.JsonSignalProtocolStore;
|
||||||
|
import org.asamk.signal.storage.threads.JsonThreadStore;
|
||||||
|
import org.asamk.signal.util.IOUtils;
|
||||||
|
import org.asamk.signal.util.Util;
|
||||||
|
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||||
|
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||||
|
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||||
|
import org.whispersystems.libsignal.util.Medium;
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.internal.util.Base64;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.FileLock;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class SignalAccount {
|
||||||
|
|
||||||
|
private final ObjectMapper jsonProcessor = new ObjectMapper();
|
||||||
|
private FileChannel fileChannel;
|
||||||
|
private FileLock lock;
|
||||||
|
private String username;
|
||||||
|
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
|
||||||
|
private boolean isMultiDevice = false;
|
||||||
|
private String password;
|
||||||
|
private String registrationLockPin;
|
||||||
|
private String signalingKey;
|
||||||
|
private byte[] profileKey;
|
||||||
|
private int preKeyIdOffset;
|
||||||
|
private int nextSignedPreKeyId;
|
||||||
|
|
||||||
|
private boolean registered = false;
|
||||||
|
|
||||||
|
private JsonSignalProtocolStore signalProtocolStore;
|
||||||
|
private JsonGroupStore groupStore;
|
||||||
|
private JsonContactsStore contactStore;
|
||||||
|
private JsonThreadStore threadStore;
|
||||||
|
|
||||||
|
private SignalAccount() {
|
||||||
|
jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); // disable autodetect
|
||||||
|
jsonProcessor.enable(SerializationFeature.INDENT_OUTPUT); // for pretty print, you can disable it.
|
||||||
|
jsonProcessor.enable(SerializationFeature.WRITE_NULL_MAP_VALUES);
|
||||||
|
jsonProcessor.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||||
|
jsonProcessor.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
|
||||||
|
jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignalAccount load(String dataPath, String username) throws IOException {
|
||||||
|
SignalAccount account = new SignalAccount();
|
||||||
|
IOUtils.createPrivateDirectories(dataPath);
|
||||||
|
account.openFileChannel(getFileName(dataPath, username));
|
||||||
|
account.load();
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignalAccount create(String dataPath, String username, IdentityKeyPair identityKey, int registrationId, byte[] profileKey) throws IOException {
|
||||||
|
IOUtils.createPrivateDirectories(dataPath);
|
||||||
|
|
||||||
|
SignalAccount account = new SignalAccount();
|
||||||
|
account.openFileChannel(getFileName(dataPath, username));
|
||||||
|
|
||||||
|
account.username = username;
|
||||||
|
account.profileKey = profileKey;
|
||||||
|
account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId);
|
||||||
|
account.groupStore = new JsonGroupStore();
|
||||||
|
account.threadStore = new JsonThreadStore();
|
||||||
|
account.contactStore = new JsonContactsStore();
|
||||||
|
account.registered = false;
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignalAccount createLinkedAccount(String dataPath, String username, String password, int deviceId, IdentityKeyPair identityKey, int registrationId, String signalingKey, byte[] profileKey) throws IOException {
|
||||||
|
IOUtils.createPrivateDirectories(dataPath);
|
||||||
|
|
||||||
|
SignalAccount account = new SignalAccount();
|
||||||
|
account.openFileChannel(getFileName(dataPath, username));
|
||||||
|
|
||||||
|
account.username = username;
|
||||||
|
account.password = password;
|
||||||
|
account.profileKey = profileKey;
|
||||||
|
account.deviceId = deviceId;
|
||||||
|
account.signalingKey = signalingKey;
|
||||||
|
account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId);
|
||||||
|
account.groupStore = new JsonGroupStore();
|
||||||
|
account.threadStore = new JsonThreadStore();
|
||||||
|
account.contactStore = new JsonContactsStore();
|
||||||
|
account.registered = true;
|
||||||
|
account.isMultiDevice = true;
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignalAccount createTemporaryAccount(IdentityKeyPair identityKey, int registrationId) {
|
||||||
|
SignalAccount account = new SignalAccount();
|
||||||
|
|
||||||
|
account.signalProtocolStore = new JsonSignalProtocolStore(identityKey, registrationId);
|
||||||
|
account.registered = false;
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileName(String dataPath, String username) {
|
||||||
|
return dataPath + "/" + username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean userExists(String dataPath, String username) {
|
||||||
|
if (username == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File f = new File(getFileName(dataPath, username));
|
||||||
|
return !(!f.exists() || f.isDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load() throws IOException {
|
||||||
|
JsonNode rootNode = jsonProcessor.readTree(Channels.newInputStream(fileChannel));
|
||||||
|
|
||||||
|
JsonNode node = rootNode.get("deviceId");
|
||||||
|
if (node != null) {
|
||||||
|
deviceId = node.asInt();
|
||||||
|
}
|
||||||
|
username = Util.getNotNullNode(rootNode, "username").asText();
|
||||||
|
password = Util.getNotNullNode(rootNode, "password").asText();
|
||||||
|
JsonNode pinNode = rootNode.get("registrationLockPin");
|
||||||
|
registrationLockPin = pinNode == null ? null : pinNode.asText();
|
||||||
|
if (rootNode.has("signalingKey")) {
|
||||||
|
signalingKey = Util.getNotNullNode(rootNode, "signalingKey").asText();
|
||||||
|
}
|
||||||
|
if (rootNode.has("preKeyIdOffset")) {
|
||||||
|
preKeyIdOffset = Util.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0);
|
||||||
|
} else {
|
||||||
|
preKeyIdOffset = 0;
|
||||||
|
}
|
||||||
|
if (rootNode.has("nextSignedPreKeyId")) {
|
||||||
|
nextSignedPreKeyId = Util.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt();
|
||||||
|
} else {
|
||||||
|
nextSignedPreKeyId = 0;
|
||||||
|
}
|
||||||
|
if (rootNode.has("profileKey")) {
|
||||||
|
profileKey = Base64.decode(Util.getNotNullNode(rootNode, "profileKey").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
signalProtocolStore = jsonProcessor.convertValue(Util.getNotNullNode(rootNode, "axolotlStore"), JsonSignalProtocolStore.class);
|
||||||
|
registered = Util.getNotNullNode(rootNode, "registered").asBoolean();
|
||||||
|
JsonNode groupStoreNode = rootNode.get("groupStore");
|
||||||
|
if (groupStoreNode != null) {
|
||||||
|
groupStore = jsonProcessor.convertValue(groupStoreNode, JsonGroupStore.class);
|
||||||
|
}
|
||||||
|
if (groupStore == null) {
|
||||||
|
groupStore = new JsonGroupStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode contactStoreNode = rootNode.get("contactStore");
|
||||||
|
if (contactStoreNode != null) {
|
||||||
|
contactStore = jsonProcessor.convertValue(contactStoreNode, JsonContactsStore.class);
|
||||||
|
}
|
||||||
|
if (contactStore == null) {
|
||||||
|
contactStore = new JsonContactsStore();
|
||||||
|
}
|
||||||
|
JsonNode threadStoreNode = rootNode.get("threadStore");
|
||||||
|
if (threadStoreNode != null) {
|
||||||
|
threadStore = jsonProcessor.convertValue(threadStoreNode, JsonThreadStore.class);
|
||||||
|
}
|
||||||
|
if (threadStore == null) {
|
||||||
|
threadStore = new JsonThreadStore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
if (fileChannel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ObjectNode rootNode = jsonProcessor.createObjectNode();
|
||||||
|
rootNode.put("username", username)
|
||||||
|
.put("deviceId", deviceId)
|
||||||
|
.put("password", password)
|
||||||
|
.put("registrationLockPin", registrationLockPin)
|
||||||
|
.put("signalingKey", signalingKey)
|
||||||
|
.put("preKeyIdOffset", preKeyIdOffset)
|
||||||
|
.put("nextSignedPreKeyId", nextSignedPreKeyId)
|
||||||
|
.put("registered", registered)
|
||||||
|
.putPOJO("axolotlStore", signalProtocolStore)
|
||||||
|
.putPOJO("groupStore", groupStore)
|
||||||
|
.putPOJO("contactStore", contactStore)
|
||||||
|
.putPOJO("threadStore", threadStore)
|
||||||
|
;
|
||||||
|
try {
|
||||||
|
fileChannel.position(0);
|
||||||
|
jsonProcessor.writeValue(Channels.newOutputStream(fileChannel), rootNode);
|
||||||
|
fileChannel.truncate(fileChannel.position());
|
||||||
|
fileChannel.force(false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(String.format("Error saving file: %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openFileChannel(String fileName) throws IOException {
|
||||||
|
if (fileChannel != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new File(fileName).exists()) {
|
||||||
|
IOUtils.createPrivateFile(fileName);
|
||||||
|
}
|
||||||
|
fileChannel = new RandomAccessFile(new File(fileName), "rw").getChannel();
|
||||||
|
lock = fileChannel.tryLock();
|
||||||
|
if (lock == null) {
|
||||||
|
System.err.println("Config file is in use by another instance, waiting…");
|
||||||
|
lock = fileChannel.lock();
|
||||||
|
System.err.println("Config file lock acquired.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPreKeys(Collection<PreKeyRecord> records) {
|
||||||
|
for (PreKeyRecord record : records) {
|
||||||
|
signalProtocolStore.storePreKey(record.getId(), record);
|
||||||
|
}
|
||||||
|
preKeyIdOffset = (preKeyIdOffset + records.size()) % Medium.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSignedPreKey(SignedPreKeyRecord record) {
|
||||||
|
signalProtocolStore.storeSignedPreKey(record.getId(), record);
|
||||||
|
nextSignedPreKeyId = (nextSignedPreKeyId + 1) % Medium.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonSignalProtocolStore getSignalProtocolStore() {
|
||||||
|
return signalProtocolStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonGroupStore getGroupStore() {
|
||||||
|
return groupStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonContactsStore getContactStore() {
|
||||||
|
return contactStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonThreadStore getThreadStore() {
|
||||||
|
return threadStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(final String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegistrationLockPin() {
|
||||||
|
return registrationLockPin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistrationLockPin(final String registrationLockPin) {
|
||||||
|
this.registrationLockPin = registrationLockPin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignalingKey() {
|
||||||
|
return signalingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignalingKey(final String signalingKey) {
|
||||||
|
this.signalingKey = signalingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getProfileKey() {
|
||||||
|
return profileKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProfileKey(final byte[] profileKey) {
|
||||||
|
this.profileKey = profileKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPreKeyIdOffset() {
|
||||||
|
return preKeyIdOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNextSignedPreKeyId() {
|
||||||
|
return nextSignedPreKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRegistered() {
|
||||||
|
return registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistered(final boolean registered) {
|
||||||
|
this.registered = registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiDevice() {
|
||||||
|
return isMultiDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMultiDevice(final boolean multiDevice) {
|
||||||
|
isMultiDevice = multiDevice;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package org.asamk.signal.storage.contacts;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
public class ContactInfo {
|
public class ContactInfo {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public String name;
|
public String name;
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,13 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class JsonContactsStore {
|
public class JsonContactsStore {
|
||||||
|
|
||||||
|
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
||||||
@JsonProperty("contacts")
|
@JsonProperty("contacts")
|
||||||
@JsonSerialize(using = JsonContactsStore.MapToListSerializer.class)
|
@JsonSerialize(using = JsonContactsStore.MapToListSerializer.class)
|
||||||
@JsonDeserialize(using = ContactsDeserializer.class)
|
@JsonDeserialize(using = ContactsDeserializer.class)
|
||||||
private Map<String, ContactInfo> contacts = new HashMap<>();
|
private Map<String, ContactInfo> contacts = new HashMap<>();
|
||||||
|
|
||||||
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
|
||||||
|
|
||||||
public void updateContact(ContactInfo contact) {
|
public void updateContact(ContactInfo contact) {
|
||||||
contacts.put(contact.number, contact);
|
contacts.put(contact.number, contact);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ public class JsonContactsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MapToListSerializer extends JsonSerializer<Map<?, ?>> {
|
public static class MapToListSerializer extends JsonSerializer<Map<?, ?>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Map<?, ?> value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
|
public void serialize(final Map<?, ?> value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
|
||||||
jgen.writeObject(value.values());
|
jgen.writeObject(value.values());
|
||||||
|
@ -48,6 +49,7 @@ public class JsonContactsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ContactsDeserializer extends JsonDeserializer<Map<String, ContactInfo>> {
|
public static class ContactsDeserializer extends JsonDeserializer<Map<String, ContactInfo>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ContactInfo> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
public Map<String, ContactInfo> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||||
Map<String, ContactInfo> contacts = new HashMap<>();
|
Map<String, ContactInfo> contacts = new HashMap<>();
|
||||||
|
|
|
@ -8,6 +8,7 @@ import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class GroupInfo {
|
public class GroupInfo {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public final byte[] groupId;
|
public final byte[] groupId;
|
||||||
|
|
||||||
|
@ -16,20 +17,13 @@ public class GroupInfo {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public Set<String> members = new HashSet<>();
|
public Set<String> members = new HashSet<>();
|
||||||
|
|
||||||
private long avatarId;
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
public long getAvatarId() {
|
|
||||||
return avatarId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public boolean active;
|
public boolean active;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public String color;
|
public String color;
|
||||||
|
|
||||||
|
private long avatarId;
|
||||||
|
|
||||||
public GroupInfo(byte[] groupId) {
|
public GroupInfo(byte[] groupId) {
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
@ -41,4 +35,9 @@ public class GroupInfo {
|
||||||
this.avatarId = avatarId;
|
this.avatarId = avatarId;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public long getAvatarId() {
|
||||||
|
return avatarId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,16 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class JsonGroupStore {
|
public class JsonGroupStore {
|
||||||
|
|
||||||
|
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
||||||
|
|
||||||
|
public static List<GroupInfo> groupsWithLegacyAvatarId = new ArrayList<>();
|
||||||
|
|
||||||
@JsonProperty("groups")
|
@JsonProperty("groups")
|
||||||
@JsonSerialize(using = JsonGroupStore.MapToListSerializer.class)
|
@JsonSerialize(using = JsonGroupStore.MapToListSerializer.class)
|
||||||
@JsonDeserialize(using = JsonGroupStore.GroupsDeserializer.class)
|
@JsonDeserialize(using = JsonGroupStore.GroupsDeserializer.class)
|
||||||
private Map<String, GroupInfo> groups = new HashMap<>();
|
private Map<String, GroupInfo> groups = new HashMap<>();
|
||||||
|
|
||||||
public static List<GroupInfo> groupsWithLegacyAvatarId = new ArrayList<>();
|
|
||||||
|
|
||||||
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
|
||||||
|
|
||||||
public void updateGroup(GroupInfo group) {
|
public void updateGroup(GroupInfo group) {
|
||||||
groups.put(Base64.encodeBytes(group.groupId), group);
|
groups.put(Base64.encodeBytes(group.groupId), group);
|
||||||
}
|
}
|
||||||
|
@ -38,6 +39,7 @@ public class JsonGroupStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MapToListSerializer extends JsonSerializer<Map<?, ?>> {
|
public static class MapToListSerializer extends JsonSerializer<Map<?, ?>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Map<?, ?> value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
|
public void serialize(final Map<?, ?> value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
|
||||||
jgen.writeObject(value.values());
|
jgen.writeObject(value.values());
|
||||||
|
@ -45,6 +47,7 @@ public class JsonGroupStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GroupsDeserializer extends JsonDeserializer<Map<String, GroupInfo>> {
|
public static class GroupsDeserializer extends JsonDeserializer<Map<String, GroupInfo>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, GroupInfo> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
public Map<String, GroupInfo> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||||
Map<String, GroupInfo> groups = new HashMap<>();
|
Map<String, GroupInfo> groups = new HashMap<>();
|
||||||
|
|
|
@ -22,7 +22,6 @@ public class JsonIdentityKeyStore implements IdentityKeyStore {
|
||||||
private final IdentityKeyPair identityKeyPair;
|
private final IdentityKeyPair identityKeyPair;
|
||||||
private final int localRegistrationId;
|
private final int localRegistrationId;
|
||||||
|
|
||||||
|
|
||||||
public JsonIdentityKeyStore(IdentityKeyPair identityKeyPair, int localRegistrationId) {
|
public JsonIdentityKeyStore(IdentityKeyPair identityKeyPair, int localRegistrationId) {
|
||||||
this.identityKeyPair = identityKeyPair;
|
this.identityKeyPair = identityKeyPair;
|
||||||
this.localRegistrationId = localRegistrationId;
|
this.localRegistrationId = localRegistrationId;
|
||||||
|
@ -131,7 +130,6 @@ public class JsonIdentityKeyStore implements IdentityKeyStore {
|
||||||
int localRegistrationId = node.get("registrationId").asInt();
|
int localRegistrationId = node.get("registrationId").asInt();
|
||||||
IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.decode(node.get("identityKey").asText()));
|
IdentityKeyPair identityKeyPair = new IdentityKeyPair(Base64.decode(node.get("identityKey").asText()));
|
||||||
|
|
||||||
|
|
||||||
JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId);
|
JsonIdentityKeyStore keyStore = new JsonIdentityKeyStore(identityKeyPair, localRegistrationId);
|
||||||
|
|
||||||
JsonNode trustedKeysNode = node.get("trustedKeys");
|
JsonNode trustedKeysNode = node.get("trustedKeys");
|
||||||
|
@ -180,6 +178,7 @@ public class JsonIdentityKeyStore implements IdentityKeyStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Identity {
|
public class Identity {
|
||||||
|
|
||||||
IdentityKey identityKey;
|
IdentityKey identityKey;
|
||||||
TrustLevel trustLevel;
|
TrustLevel trustLevel;
|
||||||
Date added;
|
Date added;
|
||||||
|
|
|
@ -17,7 +17,6 @@ class JsonPreKeyStore implements PreKeyStore {
|
||||||
|
|
||||||
private final Map<Integer, byte[]> store = new HashMap<>();
|
private final Map<Integer, byte[]> store = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
public JsonPreKeyStore() {
|
public JsonPreKeyStore() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,7 +59,6 @@ class JsonPreKeyStore implements PreKeyStore {
|
||||||
public JsonPreKeyStore deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
|
public JsonPreKeyStore deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
|
||||||
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
|
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
|
||||||
|
|
||||||
|
|
||||||
Map<Integer, byte[]> preKeyMap = new HashMap<>();
|
Map<Integer, byte[]> preKeyMap = new HashMap<>();
|
||||||
if (node.isArray()) {
|
if (node.isArray()) {
|
||||||
for (JsonNode preKey : node) {
|
for (JsonNode preKey : node) {
|
||||||
|
|
|
@ -24,7 +24,6 @@ class JsonSessionStore implements SessionStore {
|
||||||
this.sessions.putAll(sessions);
|
this.sessions.putAll(sessions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized SessionRecord loadSession(SignalProtocolAddress remoteAddress) {
|
public synchronized SessionRecord loadSession(SignalProtocolAddress remoteAddress) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -23,7 +23,6 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addSignedPreKeys(Map<Integer, byte[]> preKeys) {
|
public void addSignedPreKeys(Map<Integer, byte[]> preKeys) {
|
||||||
store.putAll(preKeys);
|
store.putAll(preKeys);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +76,6 @@ class JsonSignedPreKeyStore implements SignedPreKeyStore {
|
||||||
public JsonSignedPreKeyStore deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
|
public JsonSignedPreKeyStore deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
|
||||||
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
|
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
|
||||||
|
|
||||||
|
|
||||||
Map<Integer, byte[]> preKeyMap = new HashMap<>();
|
Map<Integer, byte[]> preKeyMap = new HashMap<>();
|
||||||
if (node.isArray()) {
|
if (node.isArray()) {
|
||||||
for (JsonNode preKey : node) {
|
for (JsonNode preKey : node) {
|
||||||
|
|
|
@ -14,13 +14,14 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class JsonThreadStore {
|
public class JsonThreadStore {
|
||||||
|
|
||||||
|
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
||||||
|
|
||||||
@JsonProperty("threads")
|
@JsonProperty("threads")
|
||||||
@JsonSerialize(using = JsonThreadStore.MapToListSerializer.class)
|
@JsonSerialize(using = JsonThreadStore.MapToListSerializer.class)
|
||||||
@JsonDeserialize(using = ThreadsDeserializer.class)
|
@JsonDeserialize(using = ThreadsDeserializer.class)
|
||||||
private Map<String, ThreadInfo> threads = new HashMap<>();
|
private Map<String, ThreadInfo> threads = new HashMap<>();
|
||||||
|
|
||||||
private static final ObjectMapper jsonProcessor = new ObjectMapper();
|
|
||||||
|
|
||||||
public void updateThread(ThreadInfo thread) {
|
public void updateThread(ThreadInfo thread) {
|
||||||
threads.put(thread.id, thread);
|
threads.put(thread.id, thread);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +35,7 @@ public class JsonThreadStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MapToListSerializer extends JsonSerializer<Map<?, ?>> {
|
public static class MapToListSerializer extends JsonSerializer<Map<?, ?>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(final Map<?, ?> value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
|
public void serialize(final Map<?, ?> value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException {
|
||||||
jgen.writeObject(value.values());
|
jgen.writeObject(value.values());
|
||||||
|
@ -41,6 +43,7 @@ public class JsonThreadStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ThreadsDeserializer extends JsonDeserializer<Map<String, ThreadInfo>> {
|
public static class ThreadsDeserializer extends JsonDeserializer<Map<String, ThreadInfo>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ThreadInfo> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
public Map<String, ThreadInfo> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
|
||||||
Map<String, ThreadInfo> threads = new HashMap<>();
|
Map<String, ThreadInfo> threads = new HashMap<>();
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.asamk.signal.storage.threads;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
public class ThreadInfo {
|
public class ThreadInfo {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
public String id;
|
public String id;
|
||||||
|
|
||||||
|
|
|
@ -33,13 +33,18 @@ public class IOUtils {
|
||||||
return output.toString();
|
return output.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void createPrivateDirectories(String path) throws IOException {
|
public static void createPrivateDirectories(String directoryPath) throws IOException {
|
||||||
final Path file = new File(path).toPath();
|
final File file = new File(directoryPath);
|
||||||
|
if (file.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Path path = file.toPath();
|
||||||
try {
|
try {
|
||||||
Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE);
|
Set<PosixFilePermission> perms = EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE);
|
||||||
Files.createDirectories(file, PosixFilePermissions.asFileAttribute(perms));
|
Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms));
|
||||||
} catch (UnsupportedOperationException e) {
|
} catch (UnsupportedOperationException e) {
|
||||||
Files.createDirectories(file);
|
Files.createDirectories(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package org.asamk.signal.util;
|
package org.asamk.signal.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
import java.io.InvalidObjectException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -53,4 +56,13 @@ public class Util {
|
||||||
|
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JsonNode getNotNullNode(JsonNode parent, String name) throws InvalidObjectException {
|
||||||
|
JsonNode node = parent.get(name);
|
||||||
|
if (node == null) {
|
||||||
|
throw new InvalidObjectException(String.format("Incorrect file format: expected parameter %s not found ", name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue