]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java
fae72bae768355ee0a49f3a283a2f2d14e0a5ba8
[signal-cli] / src / main / java / org / asamk / signal / storage / protocol / JsonSessionStore.java
1 package org.asamk.signal.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.util.Util;
12 import org.whispersystems.libsignal.SignalProtocolAddress;
13 import org.whispersystems.libsignal.state.SessionRecord;
14 import org.whispersystems.libsignal.state.SessionStore;
15 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
16 import org.whispersystems.signalservice.api.util.UuidUtil;
17 import org.whispersystems.util.Base64;
18
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.UUID;
24
25 class JsonSessionStore implements SessionStore {
26
27 private final List<SessionInfo> sessions = new ArrayList<>();
28
29 private SignalServiceAddressResolver resolver;
30
31 public JsonSessionStore() {
32 }
33
34 public void setResolver(final SignalServiceAddressResolver resolver) {
35 this.resolver = resolver;
36 }
37
38 private SignalServiceAddress resolveSignalServiceAddress(String identifier) {
39 if (resolver != null) {
40 return resolver.resolveSignalServiceAddress(identifier);
41 } else {
42 return Util.getSignalServiceAddressFromIdentifier(identifier);
43 }
44 }
45
46 @Override
47 public synchronized SessionRecord loadSession(SignalProtocolAddress address) {
48 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
49 for (SessionInfo info : sessions) {
50 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
51 try {
52 return new SessionRecord(info.sessionRecord);
53 } catch (IOException e) {
54 System.err.println("Failed to load session, resetting session: " + e);
55 final SessionRecord sessionRecord = new SessionRecord();
56 info.sessionRecord = sessionRecord.serialize();
57 return sessionRecord;
58 }
59 }
60 }
61
62 return new SessionRecord();
63 }
64
65 public synchronized List<SessionInfo> getSessions() {
66 return sessions;
67 }
68
69 @Override
70 public synchronized List<Integer> getSubDeviceSessions(String name) {
71 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name);
72
73 List<Integer> deviceIds = new LinkedList<>();
74 for (SessionInfo info : sessions) {
75 if (info.address.matches(serviceAddress) && info.deviceId != 1) {
76 deviceIds.add(info.deviceId);
77 }
78 }
79
80 return deviceIds;
81 }
82
83 @Override
84 public synchronized void storeSession(SignalProtocolAddress address, SessionRecord record) {
85 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
86 for (SessionInfo info : sessions) {
87 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
88 if (!info.address.getUuid().isPresent() || !info.address.getNumber().isPresent()) {
89 info.address = serviceAddress;
90 }
91 info.sessionRecord = record.serialize();
92 return;
93 }
94 }
95
96 sessions.add(new SessionInfo(serviceAddress, address.getDeviceId(), record.serialize()));
97 }
98
99 @Override
100 public synchronized boolean containsSession(SignalProtocolAddress address) {
101 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
102 for (SessionInfo info : sessions) {
103 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
104 return true;
105 }
106 }
107 return false;
108 }
109
110 @Override
111 public synchronized void deleteSession(SignalProtocolAddress address) {
112 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
113 sessions.removeIf(info -> info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId());
114 }
115
116 @Override
117 public synchronized void deleteAllSessions(String name) {
118 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name);
119 deleteAllSessions(serviceAddress);
120 }
121
122 public synchronized void deleteAllSessions(SignalServiceAddress serviceAddress) {
123 sessions.removeIf(info -> info.address.matches(serviceAddress));
124 }
125
126 public static class JsonSessionStoreDeserializer extends JsonDeserializer<JsonSessionStore> {
127
128 @Override
129 public JsonSessionStore deserialize(
130 JsonParser jsonParser, DeserializationContext deserializationContext
131 ) throws IOException {
132 JsonNode node = jsonParser.getCodec().readTree(jsonParser);
133
134 JsonSessionStore sessionStore = new JsonSessionStore();
135
136 if (node.isArray()) {
137 for (JsonNode session : node) {
138 String sessionName = session.hasNonNull("name") ? session.get("name").asText() : null;
139 if (UuidUtil.isUuid(sessionName)) {
140 // Ignore sessions that were incorrectly created with UUIDs as name
141 continue;
142 }
143
144 UUID uuid = session.hasNonNull("uuid") ? UuidUtil.parseOrNull(session.get("uuid").asText()) : null;
145 final SignalServiceAddress serviceAddress = uuid == null
146 ? Util.getSignalServiceAddressFromIdentifier(sessionName)
147 : new SignalServiceAddress(uuid, sessionName);
148 final int deviceId = session.get("deviceId").asInt();
149 final String record = session.get("record").asText();
150 try {
151 SessionInfo sessionInfo = new SessionInfo(serviceAddress, deviceId, Base64.decode(record));
152 sessionStore.sessions.add(sessionInfo);
153 } catch (IOException e) {
154 System.err.println(String.format("Error while decoding session for: %s", sessionName));
155 }
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.encodeBytes(sessionInfo.sessionRecord));
180 json.writeEndObject();
181 }
182 json.writeEndArray();
183 }
184 }
185
186 }