1 package org
.asamk
.signal
.storage
.protocol
;
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
;
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
;
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
;
25 class JsonSessionStore
implements SessionStore
{
27 private final List
<SessionInfo
> sessions
= new ArrayList
<>();
29 private SignalServiceAddressResolver resolver
;
31 public JsonSessionStore() {
34 public void setResolver(final SignalServiceAddressResolver resolver
) {
35 this.resolver
= resolver
;
38 private SignalServiceAddress
resolveSignalServiceAddress(String identifier
) {
39 if (resolver
!= null) {
40 return resolver
.resolveSignalServiceAddress(identifier
);
42 return Util
.getSignalServiceAddressFromIdentifier(identifier
);
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()) {
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();
62 return new SessionRecord();
65 public synchronized List
<SessionInfo
> getSessions() {
70 public synchronized List
<Integer
> getSubDeviceSessions(String name
) {
71 SignalServiceAddress serviceAddress
= resolveSignalServiceAddress(name
);
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
);
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
;
91 info
.sessionRecord
= record.serialize();
96 sessions
.add(new SessionInfo(serviceAddress
, address
.getDeviceId(), record.serialize()));
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()) {
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());
117 public synchronized void deleteAllSessions(String name
) {
118 SignalServiceAddress serviceAddress
= resolveSignalServiceAddress(name
);
119 deleteAllSessions(serviceAddress
);
122 public synchronized void deleteAllSessions(SignalServiceAddress serviceAddress
) {
123 sessions
.removeIf(info
-> info
.address
.matches(serviceAddress
));
126 public static class JsonSessionStoreDeserializer
extends JsonDeserializer
<JsonSessionStore
> {
129 public JsonSessionStore
deserialize(JsonParser jsonParser
, DeserializationContext deserializationContext
) throws IOException
{
130 JsonNode node
= jsonParser
.getCodec().readTree(jsonParser
);
132 JsonSessionStore sessionStore
= new JsonSessionStore();
134 if (node
.isArray()) {
135 for (JsonNode session
: node
) {
136 String sessionName
= session
.has("name")
137 ? session
.get("name").asText()
139 if (UuidUtil
.isUuid(sessionName
)) {
140 // Ignore sessions that were incorrectly created with UUIDs as name
144 UUID uuid
= session
.hasNonNull("uuid")
145 ? UuidUtil
.parseOrNull(session
.get("uuid").asText())
147 final SignalServiceAddress serviceAddress
= uuid
== null
148 ? Util
.getSignalServiceAddressFromIdentifier(sessionName
)
149 : new SignalServiceAddress(uuid
, sessionName
);
150 final int deviceId
= session
.get("deviceId").asInt();
151 final String
record = session
.get("record").asText();
153 SessionInfo sessionInfo
= new SessionInfo(serviceAddress
, deviceId
, Base64
.decode(record));
154 sessionStore
.sessions
.add(sessionInfo
);
155 } catch (IOException e
) {
156 System
.err
.println(String
.format("Error while decoding session for: %s", sessionName
));
165 public static class JsonSessionStoreSerializer
extends JsonSerializer
<JsonSessionStore
> {
168 public void serialize(JsonSessionStore jsonSessionStore
, JsonGenerator json
, SerializerProvider serializerProvider
) 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());
175 if (sessionInfo
.address
.getUuid().isPresent()) {
176 json
.writeStringField("uuid", sessionInfo
.address
.getUuid().get().toString());
178 json
.writeNumberField("deviceId", sessionInfo
.deviceId
);
179 json
.writeStringField("record", Base64
.encodeBytes(sessionInfo
.sessionRecord
));
180 json
.writeEndObject();
182 json
.writeEndArray();