1 package org
.asamk
.signal
.manager
.storage
.profiles
;
3 import com
.fasterxml
.jackson
.annotation
.JsonProperty
;
4 import com
.fasterxml
.jackson
.core
.JsonGenerator
;
5 import com
.fasterxml
.jackson
.core
.JsonParser
;
6 import com
.fasterxml
.jackson
.databind
.DeserializationContext
;
7 import com
.fasterxml
.jackson
.databind
.JsonDeserializer
;
8 import com
.fasterxml
.jackson
.databind
.JsonNode
;
9 import com
.fasterxml
.jackson
.databind
.JsonSerializer
;
10 import com
.fasterxml
.jackson
.databind
.ObjectMapper
;
11 import com
.fasterxml
.jackson
.databind
.SerializerProvider
;
12 import com
.fasterxml
.jackson
.databind
.annotation
.JsonDeserialize
;
13 import com
.fasterxml
.jackson
.databind
.annotation
.JsonSerialize
;
15 import org
.signal
.zkgroup
.InvalidInputException
;
16 import org
.signal
.zkgroup
.profiles
.ProfileKey
;
17 import org
.signal
.zkgroup
.profiles
.ProfileKeyCredential
;
18 import org
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
19 import org
.whispersystems
.signalservice
.api
.util
.UuidUtil
;
21 import java
.io
.IOException
;
22 import java
.util
.ArrayList
;
23 import java
.util
.Base64
;
24 import java
.util
.List
;
26 public class ProfileStore
{
28 private static final ObjectMapper jsonProcessor
= new ObjectMapper();
30 @JsonProperty("profiles")
31 @JsonDeserialize(using
= ProfileStoreDeserializer
.class)
32 @JsonSerialize(using
= ProfileStoreSerializer
.class)
33 private final List
<SignalProfileEntry
> profiles
= new ArrayList
<>();
35 public SignalProfileEntry
getProfileEntry(SignalServiceAddress serviceAddress
) {
36 for (var entry
: profiles
) {
37 if (entry
.getServiceAddress().matches(serviceAddress
)) {
44 public ProfileKey
getProfileKey(SignalServiceAddress serviceAddress
) {
45 for (var entry
: profiles
) {
46 if (entry
.getServiceAddress().matches(serviceAddress
)) {
47 return entry
.getProfileKey();
53 public void updateProfile(
54 SignalServiceAddress serviceAddress
,
55 ProfileKey profileKey
,
57 SignalProfile profile
,
58 ProfileKeyCredential profileKeyCredential
60 var newEntry
= new SignalProfileEntry(serviceAddress
, profileKey
, now
, profile
, profileKeyCredential
);
61 for (var i
= 0; i
< profiles
.size(); i
++) {
62 if (profiles
.get(i
).getServiceAddress().matches(serviceAddress
)) {
63 profiles
.set(i
, newEntry
);
68 profiles
.add(newEntry
);
71 public void storeProfileKey(SignalServiceAddress serviceAddress
, ProfileKey profileKey
) {
72 var newEntry
= new SignalProfileEntry(serviceAddress
, profileKey
, 0, null, null);
73 for (var i
= 0; i
< profiles
.size(); i
++) {
74 if (profiles
.get(i
).getServiceAddress().matches(serviceAddress
)) {
75 if (!profiles
.get(i
).getProfileKey().equals(profileKey
)) {
76 profiles
.set(i
, newEntry
);
82 profiles
.add(newEntry
);
85 public static class ProfileStoreDeserializer
extends JsonDeserializer
<List
<SignalProfileEntry
>> {
88 public List
<SignalProfileEntry
> deserialize(
89 JsonParser jsonParser
, DeserializationContext deserializationContext
90 ) throws IOException
{
91 JsonNode node
= jsonParser
.getCodec().readTree(jsonParser
);
93 var addresses
= new ArrayList
<SignalProfileEntry
>();
96 for (var entry
: node
) {
97 var name
= entry
.hasNonNull("name") ? entry
.get("name").asText() : null;
98 var uuid
= entry
.hasNonNull("uuid") ? UuidUtil
.parseOrNull(entry
.get("uuid").asText()) : null;
99 final var serviceAddress
= new SignalServiceAddress(uuid
, name
);
100 ProfileKey profileKey
= null;
102 profileKey
= new ProfileKey(Base64
.getDecoder().decode(entry
.get("profileKey").asText()));
103 } catch (InvalidInputException ignored
) {
105 ProfileKeyCredential profileKeyCredential
= null;
106 if (entry
.hasNonNull("profileKeyCredential")) {
108 profileKeyCredential
= new ProfileKeyCredential(Base64
.getDecoder()
109 .decode(entry
.get("profileKeyCredential").asText()));
110 } catch (Throwable ignored
) {
113 var lastUpdateTimestamp
= entry
.get("lastUpdateTimestamp").asLong();
114 var profile
= jsonProcessor
.treeToValue(entry
.get("profile"), SignalProfile
.class);
115 addresses
.add(new SignalProfileEntry(serviceAddress
,
119 profileKeyCredential
));
127 public static class ProfileStoreSerializer
extends JsonSerializer
<List
<SignalProfileEntry
>> {
130 public void serialize(
131 List
<SignalProfileEntry
> profiles
, JsonGenerator json
, SerializerProvider serializerProvider
132 ) throws IOException
{
133 json
.writeStartArray();
134 for (var profileEntry
: profiles
) {
135 final var address
= profileEntry
.getServiceAddress();
136 json
.writeStartObject();
137 if (address
.getNumber().isPresent()) {
138 json
.writeStringField("name", address
.getNumber().get());
140 if (address
.getUuid().isPresent()) {
141 json
.writeStringField("uuid", address
.getUuid().get().toString());
143 json
.writeStringField("profileKey",
144 Base64
.getEncoder().encodeToString(profileEntry
.getProfileKey().serialize()));
145 json
.writeNumberField("lastUpdateTimestamp", profileEntry
.getLastUpdateTimestamp());
146 json
.writeObjectField("profile", profileEntry
.getProfile());
147 if (profileEntry
.getProfileKeyCredential() != null) {
148 json
.writeStringField("profileKeyCredential",
149 Base64
.getEncoder().encodeToString(profileEntry
.getProfileKeyCredential().serialize()));
151 json
.writeEndObject();
153 json
.writeEndArray();