]> nmode's Git Repositories - signal-cli/blob - src/main/java/org/asamk/signal/storage/protocol/JsonSessionStore.java
5ce99742eedb3b019dc797b4592e253feb523d25
[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(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
130 JsonNode node = jsonParser.getCodec().readTree(jsonParser);
131
132 JsonSessionStore sessionStore = new JsonSessionStore();
133
134 if (node.isArray()) {
135 for (JsonNode session : node) {
136 String sessionName = session.has("name")
137 ? session.get("name").asText()
138 : 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")
145 ? UuidUtil.parseOrNull(session.get("uuid").asText())
146 : null;
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();
152 try {
153 SessionInfo sessionInfo = new SessionInfo(serviceAddress, deviceId, Base64.decode(record));
154 sessionStore.sessions.add(sessionInfo);
155 } catch (IOException e) {
156 System.out.println(String.format("Error while decoding session for: %s", sessionName));
157 }
158 }
159 }
160
161 return sessionStore;
162 }
163 }
164
165 public static class JsonSessionStoreSerializer extends JsonSerializer<JsonSessionStore> {
166
167 @Override
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());
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 }