1 package org
.asamk
.signal
.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
.whispersystems
.signalservice
.api
.push
.SignalServiceAddress
;
18 import org
.whispersystems
.signalservice
.api
.util
.UuidUtil
;
19 import org
.whispersystems
.util
.Base64
;
21 import java
.io
.IOException
;
22 import java
.util
.ArrayList
;
23 import java
.util
.List
;
24 import java
.util
.UUID
;
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
getProfile(SignalServiceAddress serviceAddress
) {
36 for (SignalProfileEntry entry
: profiles
) {
37 if (entry
.getServiceAddress().matches(serviceAddress
)) {
44 public ProfileKey
getProfileKey(SignalServiceAddress serviceAddress
) {
45 for (SignalProfileEntry entry
: profiles
) {
46 if (entry
.getServiceAddress().matches(serviceAddress
)) {
47 return entry
.getProfileKey();
53 public void updateProfile(SignalServiceAddress serviceAddress
, ProfileKey profileKey
, long now
, SignalProfile profile
) {
54 SignalProfileEntry newEntry
= new SignalProfileEntry(serviceAddress
, profileKey
, now
, profile
);
55 for (int i
= 0; i
< profiles
.size(); i
++) {
56 if (profiles
.get(i
).getServiceAddress().matches(serviceAddress
)) {
57 profiles
.set(i
, newEntry
);
62 profiles
.add(newEntry
);
65 public void storeProfileKey(SignalServiceAddress serviceAddress
, ProfileKey profileKey
) {
66 SignalProfileEntry newEntry
= new SignalProfileEntry(serviceAddress
, profileKey
, 0, null);
67 for (int i
= 0; i
< profiles
.size(); i
++) {
68 if (profiles
.get(i
).getServiceAddress().matches(serviceAddress
)) {
69 if (!profiles
.get(i
).getProfileKey().equals(profileKey
)) {
70 profiles
.set(i
, newEntry
);
76 profiles
.add(newEntry
);
79 public static class ProfileStoreDeserializer
extends JsonDeserializer
<List
<SignalProfileEntry
>> {
82 public List
<SignalProfileEntry
> deserialize(JsonParser jsonParser
, DeserializationContext deserializationContext
) throws IOException
{
83 JsonNode node
= jsonParser
.getCodec().readTree(jsonParser
);
85 List
<SignalProfileEntry
> addresses
= new ArrayList
<>();
88 for (JsonNode entry
: node
) {
89 String name
= entry
.hasNonNull("name")
90 ? entry
.get("name").asText()
92 UUID uuid
= entry
.hasNonNull("uuid")
93 ? UuidUtil
.parseOrNull(entry
.get("uuid").asText())
95 final SignalServiceAddress serviceAddress
= new SignalServiceAddress(uuid
, name
);
96 ProfileKey profileKey
= null;
98 profileKey
= new ProfileKey(Base64
.decode(entry
.get("profileKey").asText()));
99 } catch (InvalidInputException ignored
) {
101 long lastUpdateTimestamp
= entry
.get("lastUpdateTimestamp").asLong();
102 SignalProfile profile
= jsonProcessor
.treeToValue(entry
.get("profile"), SignalProfile
.class);
103 addresses
.add(new SignalProfileEntry(serviceAddress
, profileKey
, lastUpdateTimestamp
, profile
));
111 public static class ProfileStoreSerializer
extends JsonSerializer
<List
<SignalProfileEntry
>> {
114 public void serialize(List
<SignalProfileEntry
> profiles
, JsonGenerator json
, SerializerProvider serializerProvider
) throws IOException
{
115 json
.writeStartArray();
116 for (SignalProfileEntry profileEntry
: profiles
) {
117 final SignalServiceAddress address
= profileEntry
.getServiceAddress();
118 json
.writeStartObject();
119 if (address
.getNumber().isPresent()) {
120 json
.writeStringField("name", address
.getNumber().get());
122 if (address
.getUuid().isPresent()) {
123 json
.writeStringField("uuid", address
.getUuid().get().toString());
125 json
.writeStringField("profileKey", Base64
.encodeBytes(profileEntry
.getProfileKey().serialize()));
126 json
.writeNumberField("lastUpdateTimestamp", profileEntry
.getLastUpdateTimestamp());
127 json
.writeObjectField("profile", profileEntry
.getProfile());
128 json
.writeEndObject();
130 json
.writeEndArray();