]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/protocol/JsonSessionStore.java
de84c8958d619086942e8b0e7eeba8494d70dff0
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / protocol / JsonSessionStore.java
1 package org.asamk.signal.manager.storage.protocol;
2
3 import com.fasterxml.jackson.core.JsonGenerator;
4 import com.fasterxml.jackson.core.JsonParser;
5 import com.fasterxml.jackson.databind.DeserializationContext;
6 import com.fasterxml.jackson.databind.JsonDeserializer;
7 import com.fasterxml.jackson.databind.JsonNode;
8 import com.fasterxml.jackson.databind.JsonSerializer;
9 import com.fasterxml.jackson.databind.SerializerProvider;
10
11 import org.asamk.signal.manager.util.Utils;
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14 import org.whispersystems.libsignal.SignalProtocolAddress;
15 import org.whispersystems.libsignal.state.SessionRecord;
16 import org.whispersystems.libsignal.state.SessionStore;
17 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
18 import org.whispersystems.signalservice.api.util.UuidUtil;
19
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Base64;
23 import java.util.LinkedList;
24 import java.util.List;
25 import java.util.UUID;
26
27 class JsonSessionStore implements SessionStore {
28
29 private final static Logger logger = LoggerFactory.getLogger(JsonSessionStore.class);
30
31 private final List<SessionInfo> sessions = new ArrayList<>();
32
33 private SignalServiceAddressResolver resolver;
34
35 public JsonSessionStore() {
36 }
37
38 public void setResolver(final SignalServiceAddressResolver resolver) {
39 this.resolver = resolver;
40 }
41
42 private SignalServiceAddress resolveSignalServiceAddress(String identifier) {
43 if (resolver != null) {
44 return resolver.resolveSignalServiceAddress(identifier);
45 } else {
46 return Utils.getSignalServiceAddressFromIdentifier(identifier);
47 }
48 }
49
50 @Override
51 public synchronized SessionRecord loadSession(SignalProtocolAddress address) {
52 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
53 for (SessionInfo info : sessions) {
54 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
55 try {
56 return new SessionRecord(info.sessionRecord);
57 } catch (IOException e) {
58 logger.warn("Failed to load session, resetting session: {}", e.getMessage());
59 final SessionRecord sessionRecord = new SessionRecord();
60 info.sessionRecord = sessionRecord.serialize();
61 return sessionRecord;
62 }
63 }
64 }
65
66 return new SessionRecord();
67 }
68
69 public synchronized List<SessionInfo> getSessions() {
70 return sessions;
71 }
72
73 @Override
74 public synchronized List<Integer> getSubDeviceSessions(String name) {
75 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name);
76
77 List<Integer> deviceIds = new LinkedList<>();
78 for (SessionInfo info : sessions) {
79 if (info.address.matches(serviceAddress) && info.deviceId != 1) {
80 deviceIds.add(info.deviceId);
81 }
82 }
83
84 return deviceIds;
85 }
86
87 @Override
88 public synchronized void storeSession(SignalProtocolAddress address, SessionRecord record) {
89 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
90 for (SessionInfo info : sessions) {
91 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
92 if (!info.address.getUuid().isPresent() || !info.address.getNumber().isPresent()) {
93 info.address = serviceAddress;
94 }
95 info.sessionRecord = record.serialize();
96 return;
97 }
98 }
99
100 sessions.add(new SessionInfo(serviceAddress, address.getDeviceId(), record.serialize()));
101 }
102
103 @Override
104 public synchronized boolean containsSession(SignalProtocolAddress address) {
105 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
106 for (SessionInfo info : sessions) {
107 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
108 return true;
109 }
110 }
111 return false;
112 }
113
114 @Override
115 public synchronized void deleteSession(SignalProtocolAddress address) {
116 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
117 sessions.removeIf(info -> info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId());
118 }
119
120 @Override
121 public synchronized void deleteAllSessions(String name) {
122 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name);
123 deleteAllSessions(serviceAddress);
124 }
125
126 public synchronized void deleteAllSessions(SignalServiceAddress serviceAddress) {
127 sessions.removeIf(info -> info.address.matches(serviceAddress));
128 }
129
130 public static class JsonSessionStoreDeserializer extends JsonDeserializer<JsonSessionStore> {
131
132 @Override
133 public JsonSessionStore deserialize(
134 JsonParser jsonParser, DeserializationContext deserializationContext
135 ) throws IOException {
136 JsonNode node = jsonParser.getCodec().readTree(jsonParser);
137
138 JsonSessionStore sessionStore = new JsonSessionStore();
139
140 if (node.isArray()) {
141 for (JsonNode session : node) {
142 String sessionName = session.hasNonNull("name") ? session.get("name").asText() : null;
143 if (UuidUtil.isUuid(sessionName)) {
144 // Ignore sessions that were incorrectly created with UUIDs as name
145 continue;
146 }
147
148 UUID uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null;
149 final SignalServiceAddress serviceAddress = uuid == null
150 ? Utils.getSignalServiceAddressFromIdentifier(sessionName)
151 : new SignalServiceAddress(uuid, sessionName);
152 final int deviceId = session.get("deviceId").asInt();
153 final byte[] record = Base64.getDecoder().decode(session.get("record").asText());
154 SessionInfo sessionInfo = new SessionInfo(serviceAddress, deviceId, record);
155 sessionStore.sessions.add(sessionInfo);
156 }
157 }
158
159 return sessionStore;
160 }
161 }
162
163 public static class JsonSessionStoreSerializer extends JsonSerializer<JsonSessionStore> {
164
165 @Override
166 public void serialize(
167 JsonSessionStore jsonSessionStore, JsonGenerator json, SerializerProvider serializerProvider
168 ) throws IOException {
169 json.writeStartArray();
170 for (SessionInfo sessionInfo : jsonSessionStore.sessions) {
171 json.writeStartObject();
172 if (sessionInfo.address.getNumber().isPresent()) {
173 json.writeStringField("name", sessionInfo.address.getNumber().get());
174 }
175 if (sessionInfo.address.getUuid().isPresent()) {
176 json.writeStringField("uuid", sessionInfo.address.getUuid().get().toString());
177 }
178 json.writeNumberField("deviceId", sessionInfo.deviceId);
179 json.writeStringField("record", Base64.getEncoder().encodeToString(sessionInfo.sessionRecord));
180 json.writeEndObject();
181 }
182 json.writeEndArray();
183 }
184 }
185
186 }