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
.HashMap
;
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 Map
<SignalServiceAddress
, SignalProfileEntry
> profiles
= new HashMap
<>();
35 public SignalProfileEntry
getProfile(SignalServiceAddress serviceAddress
) {
36 return profiles
.get(serviceAddress
);
39 public void updateProfile(SignalServiceAddress serviceAddress
, SignalProfileEntry profile
) {
40 profiles
.put(serviceAddress
, profile
);
43 public static class ProfileStoreDeserializer
extends JsonDeserializer
<Map
<SignalServiceAddress
, SignalProfileEntry
>> {
46 public Map
<SignalServiceAddress
, SignalProfileEntry
> deserialize(JsonParser jsonParser
, DeserializationContext deserializationContext
) throws IOException
{
47 JsonNode node
= jsonParser
.getCodec().readTree(jsonParser
);
49 Map
<SignalServiceAddress
, SignalProfileEntry
> addresses
= new HashMap
<>();
52 for (JsonNode entry
: node
) {
53 String name
= entry
.hasNonNull("name")
54 ? entry
.get("name").asText()
56 UUID uuid
= entry
.hasNonNull("uuid")
57 ? UuidUtil
.parseOrNull(entry
.get("uuid").asText())
59 final SignalServiceAddress serviceAddress
= new SignalServiceAddress(uuid
, name
);
60 ProfileKey profileKey
= null;
62 profileKey
= new ProfileKey(Base64
.decode(entry
.get("profileKey").asText()));
63 } catch (InvalidInputException ignored
) {
65 long lastUpdateTimestamp
= entry
.get("lastUpdateTimestamp").asLong();
66 SignalProfile profile
= jsonProcessor
.treeToValue(entry
.get("profile"), SignalProfile
.class);
67 addresses
.put(serviceAddress
, new SignalProfileEntry(profileKey
, lastUpdateTimestamp
, profile
));
75 public static class ProfileStoreSerializer
extends JsonSerializer
<Map
<SignalServiceAddress
, SignalProfileEntry
>> {
78 public void serialize(Map
<SignalServiceAddress
, SignalProfileEntry
> profiles
, JsonGenerator json
, SerializerProvider serializerProvider
) throws IOException
{
79 json
.writeStartArray();
80 for (Map
.Entry
<SignalServiceAddress
, SignalProfileEntry
> entry
: profiles
.entrySet()) {
81 final SignalServiceAddress address
= entry
.getKey();
82 final SignalProfileEntry profileEntry
= entry
.getValue();
83 json
.writeStartObject();
84 if (address
.getNumber().isPresent()) {
85 json
.writeStringField("name", address
.getNumber().get());
87 if (address
.getUuid().isPresent()) {
88 json
.writeStringField("uuid", address
.getUuid().get().toString());
90 json
.writeStringField("profileKey", Base64
.encodeBytes(profileEntry
.getProfileKey().serialize()));
91 json
.writeNumberField("lastUpdateTimestamp", profileEntry
.getLastUpdateTimestamp());
92 json
.writeObjectField("profile", profileEntry
.getProfile());
93 json
.writeEndObject();