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