]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/sessions/LegacySessionStore.java
71cd92310c0801eb5372ff18f91aa245dcf6726a
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / sessions / LegacySessionStore.java
1 package org.asamk.signal.manager.storage.sessions;
2
3 import org.asamk.signal.manager.api.Pair;
4 import org.asamk.signal.manager.helper.RecipientAddressResolver;
5 import org.asamk.signal.manager.storage.recipients.RecipientId;
6 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
7 import org.asamk.signal.manager.util.IOUtils;
8 import org.signal.libsignal.protocol.state.SessionRecord;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.IOException;
15 import java.nio.file.Files;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.List;
19 import java.util.Objects;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22
23 public class LegacySessionStore {
24
25 private final static Logger logger = LoggerFactory.getLogger(LegacySessionStore.class);
26
27 public static void migrate(
28 final File sessionsPath,
29 final RecipientResolver resolver,
30 final RecipientAddressResolver addressResolver,
31 final SessionStore sessionStore
32 ) {
33 final var keys = getKeysLocked(sessionsPath, resolver);
34 final var sessions = keys.stream().map(key -> {
35 final var record = loadSessionLocked(key, sessionsPath);
36 final var serviceId = addressResolver.resolveRecipientAddress(key.recipientId).serviceId();
37 if (record == null || serviceId.isEmpty()) {
38 return null;
39 }
40 return new Pair<>(new SessionStore.Key(serviceId.get(), key.deviceId()), record);
41 }).filter(Objects::nonNull).toList();
42 sessionStore.addLegacySessions(sessions);
43 deleteAllSessions(sessionsPath);
44 }
45
46 private static void deleteAllSessions(File sessionsPath) {
47 final var files = sessionsPath.listFiles();
48 if (files == null) {
49 return;
50 }
51
52 for (var file : files) {
53 try {
54 Files.delete(file.toPath());
55 } catch (IOException e) {
56 logger.error("Failed to delete session file {}: {}", file, e.getMessage());
57 }
58 }
59 try {
60 Files.delete(sessionsPath.toPath());
61 } catch (IOException e) {
62 logger.error("Failed to delete session directory {}: {}", sessionsPath, e.getMessage());
63 }
64 }
65
66 private static Collection<Key> getKeysLocked(File sessionsPath, final RecipientResolver resolver) {
67 final var files = sessionsPath.listFiles();
68 if (files == null) {
69 return List.of();
70 }
71 return parseFileNames(files, resolver);
72 }
73
74 static final Pattern sessionFileNamePattern = Pattern.compile("(\\d+)_(\\d+)");
75
76 private static List<Key> parseFileNames(final File[] files, final RecipientResolver resolver) {
77 return Arrays.stream(files)
78 .map(f -> sessionFileNamePattern.matcher(f.getName()))
79 .filter(Matcher::matches)
80 .map(matcher -> {
81 final var recipientId = resolver.resolveRecipient(Long.parseLong(matcher.group(1)));
82 if (recipientId == null) {
83 return null;
84 }
85 return new Key(recipientId, Integer.parseInt(matcher.group(2)));
86 })
87 .filter(Objects::nonNull)
88 .toList();
89 }
90
91 private static File getSessionFile(Key key, final File sessionsPath) {
92 try {
93 IOUtils.createPrivateDirectories(sessionsPath);
94 } catch (IOException e) {
95 throw new AssertionError("Failed to create sessions path", e);
96 }
97 return new File(sessionsPath, key.recipientId().id() + "_" + key.deviceId());
98 }
99
100 private static SessionRecord loadSessionLocked(final Key key, final File sessionsPath) {
101 final var file = getSessionFile(key, sessionsPath);
102 if (!file.exists()) {
103 return null;
104 }
105 try (var inputStream = new FileInputStream(file)) {
106 return new SessionRecord(inputStream.readAllBytes());
107 } catch (Exception e) {
108 logger.warn("Failed to load session, resetting session: {}", e.getMessage());
109 return null;
110 }
111 }
112
113 record Key(RecipientId recipientId, int deviceId) {}
114 }