]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/storage/SignalAccount.java
Cache profiles for 24h before retrieving them again
[signal-cli] / src / main / java / org / asamk / signal / storage / SignalAccount.java
index 16ad21220a9a3ee0f35bba5283d9178ae14e9026..d9b37825b108fd114baa90139ddd44ca83bffd6a 100644 (file)
@@ -14,6 +14,7 @@ import org.asamk.signal.storage.contacts.ContactInfo;
 import org.asamk.signal.storage.contacts.JsonContactsStore;
 import org.asamk.signal.storage.groups.GroupInfo;
 import org.asamk.signal.storage.groups.JsonGroupStore;
+import org.asamk.signal.storage.profiles.ProfileStore;
 import org.asamk.signal.storage.protocol.JsonIdentityKeyStore;
 import org.asamk.signal.storage.protocol.JsonSignalProtocolStore;
 import org.asamk.signal.storage.protocol.RecipientStore;
@@ -29,24 +30,27 @@ 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.libsignal.util.Pair;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.util.Base64;
 
+import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.nio.channels.Channels;
+import java.nio.channels.ClosedChannelException;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.util.Collection;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
-public class SignalAccount {
+public class SignalAccount implements Closeable {
 
     private final ObjectMapper jsonProcessor = new ObjectMapper();
-    private FileChannel fileChannel;
-    private FileLock lock;
+    private final FileChannel fileChannel;
+    private final FileLock lock;
     private String username;
     private UUID uuid;
     private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
@@ -64,8 +68,11 @@ public class SignalAccount {
     private JsonGroupStore groupStore;
     private JsonContactsStore contactStore;
     private RecipientStore recipientStore;
+    private ProfileStore profileStore;
 
-    private SignalAccount() {
+    private SignalAccount(final FileChannel fileChannel, final FileLock lock) {
+        this.fileChannel = fileChannel;
+        this.lock = lock;
         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);
@@ -75,18 +82,28 @@ public class SignalAccount {
     }
 
     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;
+        final String fileName = getFileName(dataPath, username);
+        final Pair<FileChannel, FileLock> pair = openFileChannel(fileName);
+        try {
+            SignalAccount account = new SignalAccount(pair.first(), pair.second());
+            account.load();
+            return account;
+        } catch (Throwable e) {
+            pair.second().close();
+            pair.first().close();
+            throw e;
+        }
     }
 
     public static SignalAccount create(String dataPath, String username, IdentityKeyPair identityKey, int registrationId, ProfileKey profileKey) throws IOException {
         IOUtils.createPrivateDirectories(dataPath);
+        String fileName = getFileName(dataPath, username);
+        if (!new File(fileName).exists()) {
+            IOUtils.createPrivateFile(fileName);
+        }
 
-        SignalAccount account = new SignalAccount();
-        account.openFileChannel(getFileName(dataPath, username));
+        final Pair<FileChannel, FileLock> pair = openFileChannel(fileName);
+        SignalAccount account = new SignalAccount(pair.first(), pair.second());
 
         account.username = username;
         account.profileKey = profileKey;
@@ -94,6 +111,7 @@ public class SignalAccount {
         account.groupStore = new JsonGroupStore();
         account.contactStore = new JsonContactsStore();
         account.recipientStore = new RecipientStore();
+        account.profileStore = new ProfileStore();
         account.registered = false;
 
         return account;
@@ -101,9 +119,13 @@ public class SignalAccount {
 
     public static SignalAccount createLinkedAccount(String dataPath, String username, UUID uuid, String password, int deviceId, IdentityKeyPair identityKey, int registrationId, String signalingKey, ProfileKey profileKey) throws IOException {
         IOUtils.createPrivateDirectories(dataPath);
+        String fileName = getFileName(dataPath, username);
+        if (!new File(fileName).exists()) {
+            IOUtils.createPrivateFile(fileName);
+        }
 
-        SignalAccount account = new SignalAccount();
-        account.openFileChannel(getFileName(dataPath, username));
+        final Pair<FileChannel, FileLock> pair = openFileChannel(fileName);
+        SignalAccount account = new SignalAccount(pair.first(), pair.second());
 
         account.username = username;
         account.uuid = uuid;
@@ -115,6 +137,7 @@ public class SignalAccount {
         account.groupStore = new JsonGroupStore();
         account.contactStore = new JsonContactsStore();
         account.recipientStore = new RecipientStore();
+        account.profileStore = new ProfileStore();
         account.registered = true;
         account.isMultiDevice = true;
 
@@ -226,6 +249,14 @@ public class SignalAccount {
             }
         }
 
+        JsonNode profileStoreNode = rootNode.get("profileStore");
+        if (profileStoreNode != null) {
+            profileStore = jsonProcessor.convertValue(profileStoreNode, ProfileStore.class);
+        }
+        if (profileStore == null) {
+            profileStore = new ProfileStore();
+        }
+
         JsonNode threadStoreNode = rootNode.get("threadStore");
         if (threadStoreNode != null) {
             LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class);
@@ -272,6 +303,7 @@ public class SignalAccount {
                 .putPOJO("groupStore", groupStore)
                 .putPOJO("contactStore", contactStore)
                 .putPOJO("recipientStore", recipientStore)
+                .putPOJO("profileStore", profileStore)
         ;
         try {
             synchronized (fileChannel) {
@@ -285,21 +317,15 @@ public class SignalAccount {
         }
     }
 
-    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();
+    private static Pair<FileChannel, FileLock> openFileChannel(String fileName) throws IOException {
+        FileChannel fileChannel = new RandomAccessFile(new File(fileName), "rw").getChannel();
+        FileLock 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.");
         }
+        return new Pair<>(fileChannel, lock);
     }
 
     public void setResolver(final SignalServiceAddressResolver resolver) {
@@ -334,6 +360,10 @@ public class SignalAccount {
         return recipientStore;
     }
 
+    public ProfileStore getProfileStore() {
+        return profileStore;
+    }
+
     public String getUsername() {
         return username;
     }
@@ -413,4 +443,15 @@ public class SignalAccount {
     public void setMultiDevice(final boolean multiDevice) {
         isMultiDevice = multiDevice;
     }
+
+    @Override
+    public void close() throws IOException {
+        synchronized (fileChannel) {
+            try {
+                lock.close();
+            } catch (ClosedChannelException ignored) {
+            }
+            fileChannel.close();
+        }
+    }
 }