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 SignalProfileEntry
updateProfile(SignalServiceAddress serviceAddress
, SignalProfileEntry profile
) {
40 return 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 recipient
: node
) {
53 String recipientName
= recipient
.get("name").asText();
54 UUID uuid
= UuidUtil
.parseOrThrow(recipient
.get("uuid").asText());
55 final SignalServiceAddress serviceAddress
= new SignalServiceAddress(uuid
, recipientName
);
56 ProfileKey profileKey
= null;
58 profileKey
= new ProfileKey(Base64
.decode(recipient
.get("profileKey").asText()));
59 } catch (InvalidInputException ignored
) {
61 long lastUpdateTimestamp
= recipient
.get("lastUpdateTimestamp").asLong();
62 SignalProfile profile
= jsonProcessor
.treeToValue(recipient
.get("profile"), SignalProfile
.class);
63 addresses
.put(serviceAddress
, new SignalProfileEntry(profileKey
, lastUpdateTimestamp
, profile
));
71 public static class ProfileStoreSerializer
extends JsonSerializer
<Map
<SignalServiceAddress
, SignalProfileEntry
>> {
74 public void serialize(Map
<SignalServiceAddress
, SignalProfileEntry
> profiles
, JsonGenerator json
, SerializerProvider serializerProvider
) throws IOException
{
75 json
.writeStartArray();
76 for (Map
.Entry
<SignalServiceAddress
, SignalProfileEntry
> entry
: profiles
.entrySet()) {
77 final SignalServiceAddress address
= entry
.getKey();
78 final SignalProfileEntry profileEntry
= entry
.getValue();
79 json
.writeStartObject();
80 json
.writeStringField("name", address
.getNumber().get());
81 json
.writeStringField("uuid", address
.getUuid().get().toString());
82 json
.writeStringField("profileKey", Base64
.encodeBytes(profileEntry
.getProfileKey().serialize()));
83 json
.writeNumberField("lastUpdateTimestamp", profileEntry
.getLastUpdateTimestamp());
84 json
.writeObjectField("profile", profileEntry
.getProfile());
85 json
.writeEndObject();