]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java
90229575e8acf6892fba555dc913b0d39059fcc7
[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 @Override
66 public synchronized List<Integer> getSubDeviceSessions(String name) {
67 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name);
68
69 List<Integer> deviceIds = new LinkedList<>();
70 for (SessionInfo info : sessions) {
71 if (info.address.matches(serviceAddress) && info.deviceId != 1) {
72 deviceIds.add(info.deviceId);
73 }
74 }
75
76 return deviceIds;
77 }
78
79 @Override
80 public synchronized void storeSession(SignalProtocolAddress address, SessionRecord record) {
81 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
82 for (SessionInfo info : sessions) {
83 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
84 if (!info.address.getUuid().isPresent() || !info.address.getNumber().isPresent()) {
85 info.address = serviceAddress;
86 }
87 info.sessionRecord = record.serialize();
88 return;
89 }
90 }
91
92 sessions.add(new SessionInfo(serviceAddress, address.getDeviceId(), record.serialize()));
93 }
94
95 @Override
96 public synchronized boolean containsSession(SignalProtocolAddress address) {
97 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
98 for (SessionInfo info : sessions) {
99 if (info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId()) {
100 return true;
101 }
102 }
103 return false;
104 }
105
106 @Override
107 public synchronized void deleteSession(SignalProtocolAddress address) {
108 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(address.getName());
109 sessions.removeIf(info -> info.address.matches(serviceAddress) && info.deviceId == address.getDeviceId());
110 }
111
112 @Override
113 public synchronized void deleteAllSessions(String name) {
114 SignalServiceAddress serviceAddress = resolveSignalServiceAddress(name);
115 deleteAllSessions(serviceAddress);
116 }
117
118 public synchronized void deleteAllSessions(SignalServiceAddress serviceAddress) {
119 sessions.removeIf(info -> info.address.matches(serviceAddress));
120 }
121
122 public static class JsonSessionStoreDeserializer extends JsonDeserializer<JsonSessionStore> {
123
124 @Override
125 public JsonSessionStore deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
126 JsonNode node = jsonParser.getCodec().readTree(jsonParser);
127
128 JsonSessionStore sessionStore = new JsonSessionStore();
129
130 if (node.isArray()) {
131 for (JsonNode session : node) {
132 String sessionName = session.has("name")
133 ? session.get("name").asText()
134 : null;
135 if (UuidUtil.isUuid(sessionName)) {
136 // Ignore sessions that were incorrectly created with UUIDs as name
137 continue;
138 }
139
140 UUID uuid = session.hasNonNull("uuid")
141 ? UuidUtil.parseOrNull(session.get("uuid").asText())
142 : null;
143 final SignalServiceAddress serviceAddress = uuid == null
144 ? Util.getSignalServiceAddressFromIdentifier(sessionName)
145 : new SignalServiceAddress(uuid, sessionName);
146 final int deviceId = session.get("deviceId").asInt();
147 final String record = session.get("record").asText();
148 try {
149 SessionInfo sessionInfo = new SessionInfo(serviceAddress, deviceId, Base64.decode(record));
150 sessionStore.sessions.add(sessionInfo);
151 } catch (IOException e) {
152 System.out.println(String.format("Error while decoding session for: %s", sessionName));
153 }
154 }
155 }
156
157 return sessionStore;
158 }
159 }
160
161 public static class JsonPreKeyStoreSerializer extends JsonSerializer<JsonSessionStore> {
162
163 @Override
164 public void serialize(JsonSessionStore jsonSessionStore, JsonGenerator json, SerializerProvider serializerProvider) throws IOException {
165 json.writeStartArray();
166 for (SessionInfo sessionInfo : jsonSessionStore.sessions) {
167 json.writeStartObject();
168 if (sessionInfo.address.getNumber().isPresent()) {
169 json.writeStringField("name", sessionInfo.address.getNumber().get());
170 }
171 if (sessionInfo.address.getUuid().isPresent()) {
172 json.writeStringField("uuid", sessionInfo.address.getUuid().get().toString());
173 }
174 json.writeNumberField("deviceId", sessionInfo.deviceId);
175 json.writeStringField("record", Base64.encodeBytes(sessionInfo.sessionRecord));
176 json.writeEndObject();
177 }
178 json.writeEndArray();
179 }
180 }
181
182 }