mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-25 16:06:24 +00:00
Better UX handling on identity key mismatches.
1) Migrate from GSON to Jackson everywhere. 2) Add support for storing identity key conflicts on message rows. 3) Add limited support for surfacing identity key conflicts in UI.
This commit is contained in:
@@ -35,6 +35,8 @@ import org.whispersystems.textsecure.api.messages.TextSecureGroup;
|
||||
import org.whispersystems.textsecure.api.messages.TextSecureMessage;
|
||||
import org.whispersystems.textsecure.api.push.PushAddress;
|
||||
import org.whispersystems.textsecure.api.push.TrustStore;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.NetworkFailureException;
|
||||
import org.whispersystems.textsecure.api.push.exceptions.PushNetworkException;
|
||||
import org.whispersystems.textsecure.internal.push.MismatchedDevices;
|
||||
import org.whispersystems.textsecure.internal.push.OutgoingPushMessage;
|
||||
import org.whispersystems.textsecure.internal.push.OutgoingPushMessageList;
|
||||
@@ -176,6 +178,7 @@ public class TextSecureMessageSender {
|
||||
{
|
||||
List<UntrustedIdentityException> untrustedIdentities = new LinkedList<>();
|
||||
List<UnregisteredUserException> unregisteredUsers = new LinkedList<>();
|
||||
List<NetworkFailureException> networkExceptions = new LinkedList<>();
|
||||
|
||||
for (PushAddress recipient : recipients) {
|
||||
try {
|
||||
@@ -186,11 +189,14 @@ public class TextSecureMessageSender {
|
||||
} catch (UnregisteredUserException e) {
|
||||
Log.w(TAG, e);
|
||||
unregisteredUsers.add(e);
|
||||
} catch (PushNetworkException e) {
|
||||
Log.w(TAG, e);
|
||||
networkExceptions.add(new NetworkFailureException(recipient.getNumber(), e));
|
||||
}
|
||||
}
|
||||
|
||||
if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty()) {
|
||||
throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers);
|
||||
if (!untrustedIdentities.isEmpty() || !unregisteredUsers.isEmpty() || !networkExceptions.isEmpty()) {
|
||||
throw new EncapsulatedExceptions(untrustedIdentities, unregisteredUsers, networkExceptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,24 +16,28 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.api.push;
|
||||
|
||||
import com.google.thoughtcrimegson.GsonBuilder;
|
||||
import com.google.thoughtcrimegson.JsonDeserializationContext;
|
||||
import com.google.thoughtcrimegson.JsonDeserializer;
|
||||
import com.google.thoughtcrimegson.JsonElement;
|
||||
import com.google.thoughtcrimegson.JsonParseException;
|
||||
import com.google.thoughtcrimegson.JsonPrimitive;
|
||||
import com.google.thoughtcrimegson.JsonSerializationContext;
|
||||
import com.google.thoughtcrimegson.JsonSerializer;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.internal.util.Base64;
|
||||
import org.whispersystems.textsecure.internal.push.PreKeyEntity;
|
||||
import org.whispersystems.textsecure.internal.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class SignedPreKeyEntity extends PreKeyEntity {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ByteArraySerializer.class)
|
||||
@JsonDeserialize(using = ByteArrayDeserializer.class)
|
||||
private byte[] signature;
|
||||
|
||||
public SignedPreKeyEntity() {}
|
||||
@@ -47,42 +51,18 @@ public class SignedPreKeyEntity extends PreKeyEntity {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public static String toJson(SignedPreKeyEntity entity) {
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
return forBuilder(builder).create().toJson(entity);
|
||||
}
|
||||
|
||||
public static SignedPreKeyEntity fromJson(String serialized) {
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
return forBuilder(builder).create().fromJson(serialized, SignedPreKeyEntity.class);
|
||||
}
|
||||
|
||||
public static GsonBuilder forBuilder(GsonBuilder builder) {
|
||||
return PreKeyEntity.forBuilder(builder)
|
||||
.registerTypeAdapter(byte[].class, new ByteArrayJsonAdapter());
|
||||
|
||||
}
|
||||
|
||||
private static class ByteArrayJsonAdapter
|
||||
implements JsonSerializer<byte[]>, JsonDeserializer<byte[]>
|
||||
{
|
||||
private static class ByteArraySerializer extends JsonSerializer<byte[]> {
|
||||
@Override
|
||||
public JsonElement serialize(byte[] signature, Type type,
|
||||
JsonSerializationContext jsonSerializationContext)
|
||||
{
|
||||
return new JsonPrimitive(Base64.encodeBytesWithoutPadding(signature));
|
||||
public void serialize(byte[] value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeString(Base64.encodeBytesWithoutPadding(value));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ByteArrayDeserializer extends JsonDeserializer<byte[]> {
|
||||
|
||||
@Override
|
||||
public byte[] deserialize(JsonElement jsonElement, Type type,
|
||||
JsonDeserializationContext jsonDeserializationContext)
|
||||
throws JsonParseException
|
||||
{
|
||||
try {
|
||||
return Base64.decodeWithoutPadding(jsonElement.getAsJsonPrimitive().getAsString());
|
||||
} catch (IOException e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
public byte[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return Base64.decodeWithoutPadding(p.getValueAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,15 @@ public class EncapsulatedExceptions extends Throwable {
|
||||
|
||||
private final List<UntrustedIdentityException> untrustedIdentityExceptions;
|
||||
private final List<UnregisteredUserException> unregisteredUserExceptions;
|
||||
private final List<NetworkFailureException> networkExceptions;
|
||||
|
||||
public EncapsulatedExceptions(List<UntrustedIdentityException> untrustedIdentities,
|
||||
List<UnregisteredUserException> unregisteredUsers)
|
||||
List<UnregisteredUserException> unregisteredUsers,
|
||||
List<NetworkFailureException> networkExceptions)
|
||||
{
|
||||
this.untrustedIdentityExceptions = untrustedIdentities;
|
||||
this.unregisteredUserExceptions = unregisteredUsers;
|
||||
this.networkExceptions = networkExceptions;
|
||||
}
|
||||
|
||||
public List<UntrustedIdentityException> getUntrustedIdentityExceptions() {
|
||||
@@ -39,4 +42,8 @@ public class EncapsulatedExceptions extends Throwable {
|
||||
public List<UnregisteredUserException> getUnregisteredUserExceptions() {
|
||||
return unregisteredUserExceptions;
|
||||
}
|
||||
|
||||
public List<NetworkFailureException> getNetworkExceptions() {
|
||||
return networkExceptions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.whispersystems.textsecure.api.push.exceptions;
|
||||
|
||||
public class NetworkFailureException extends Exception {
|
||||
|
||||
private final String e164number;
|
||||
|
||||
public NetworkFailureException(String e164number, Exception nested) {
|
||||
super(nested);
|
||||
this.e164number = e164number;
|
||||
}
|
||||
|
||||
public String getE164number() {
|
||||
return e164number;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package org.whispersystems.textsecure.api.push.exceptions;
|
||||
import java.io.IOException;
|
||||
|
||||
public class PushNetworkException extends IOException {
|
||||
|
||||
public PushNetworkException(Exception exception) {
|
||||
super(exception);
|
||||
}
|
||||
@@ -26,4 +27,5 @@ public class PushNetworkException extends IOException {
|
||||
public PushNetworkException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,10 +16,17 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class AccountAttributes {
|
||||
|
||||
@JsonProperty
|
||||
private String signalingKey;
|
||||
|
||||
@JsonProperty
|
||||
private boolean supportsSms;
|
||||
|
||||
@JsonProperty
|
||||
private int registrationId;
|
||||
|
||||
public AccountAttributes(String signalingKey, boolean supportsSms, int registrationId) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class DeviceCode {
|
||||
|
||||
@JsonProperty
|
||||
private String verificationCode;
|
||||
|
||||
public String getVerificationCode() {
|
||||
|
||||
@@ -16,11 +16,15 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MismatchedDevices {
|
||||
@JsonProperty
|
||||
private List<Integer> missingDevices;
|
||||
|
||||
@JsonProperty
|
||||
private List<Integer> extraDevices;
|
||||
|
||||
public List<Integer> getMissingDevices() {
|
||||
|
||||
@@ -17,14 +17,20 @@
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.whispersystems.textsecure.api.push.PushAddress;
|
||||
import org.whispersystems.textsecure.internal.util.Base64;
|
||||
|
||||
public class OutgoingPushMessage {
|
||||
|
||||
@JsonProperty
|
||||
private int type;
|
||||
@JsonProperty
|
||||
private int destinationDeviceId;
|
||||
@JsonProperty
|
||||
private int destinationRegistrationId;
|
||||
@JsonProperty
|
||||
private String body;
|
||||
|
||||
public OutgoingPushMessage(PushAddress address, int deviceId, PushBody body) {
|
||||
|
||||
@@ -16,16 +16,22 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OutgoingPushMessageList {
|
||||
|
||||
@JsonProperty
|
||||
private String destination;
|
||||
|
||||
@JsonProperty
|
||||
private String relay;
|
||||
|
||||
@JsonProperty
|
||||
private long timestamp;
|
||||
|
||||
@JsonProperty
|
||||
private List<OutgoingPushMessage> messages;
|
||||
|
||||
public OutgoingPushMessageList(String destination, long timestamp, String relay,
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.google.thoughtcrimegson.GsonBuilder;
|
||||
import com.google.thoughtcrimegson.JsonDeserializationContext;
|
||||
import com.google.thoughtcrimegson.JsonDeserializer;
|
||||
import com.google.thoughtcrimegson.JsonElement;
|
||||
import com.google.thoughtcrimegson.JsonParseException;
|
||||
import com.google.thoughtcrimegson.JsonPrimitive;
|
||||
import com.google.thoughtcrimegson.JsonSerializationContext;
|
||||
import com.google.thoughtcrimegson.JsonSerializer;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
@@ -31,11 +32,15 @@ import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.internal.util.Base64;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class PreKeyEntity {
|
||||
|
||||
private int keyId;
|
||||
@JsonProperty
|
||||
private int keyId;
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = ECPublicKeySerializer.class)
|
||||
@JsonDeserialize(using = ECPublicKeyDeserializer.class)
|
||||
private ECPublicKey publicKey;
|
||||
|
||||
public PreKeyEntity() {}
|
||||
@@ -53,32 +58,21 @@ public class PreKeyEntity {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public static GsonBuilder forBuilder(GsonBuilder builder) {
|
||||
return builder.registerTypeAdapter(ECPublicKey.class, new ECPublicKeyJsonAdapter());
|
||||
private static class ECPublicKeySerializer extends JsonSerializer<ECPublicKey> {
|
||||
@Override
|
||||
public void serialize(ECPublicKey value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ECPublicKeyJsonAdapter
|
||||
implements JsonSerializer<ECPublicKey>, JsonDeserializer<ECPublicKey>
|
||||
{
|
||||
private static class ECPublicKeyDeserializer extends JsonDeserializer<ECPublicKey> {
|
||||
@Override
|
||||
public JsonElement serialize(ECPublicKey preKeyPublic, Type type,
|
||||
JsonSerializationContext jsonSerializationContext)
|
||||
{
|
||||
return new JsonPrimitive(Base64.encodeBytesWithoutPadding(preKeyPublic.serialize()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECPublicKey deserialize(JsonElement jsonElement, Type type,
|
||||
JsonDeserializationContext jsonDeserializationContext)
|
||||
throws JsonParseException
|
||||
{
|
||||
public ECPublicKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
try {
|
||||
return Curve.decodePoint(Base64.decodeWithoutPadding(jsonElement.getAsJsonPrimitive().getAsString()), 0);
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
throw new JsonParseException(e);
|
||||
return Curve.decodePoint(Base64.decodeWithoutPadding(p.getValueAsString()), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,26 +16,32 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.google.thoughtcrimegson.GsonBuilder;
|
||||
import com.google.thoughtcrimegson.JsonDeserializationContext;
|
||||
import com.google.thoughtcrimegson.JsonDeserializer;
|
||||
import com.google.thoughtcrimegson.JsonElement;
|
||||
import com.google.thoughtcrimegson.JsonParseException;
|
||||
import com.google.thoughtcrimegson.JsonPrimitive;
|
||||
import com.google.thoughtcrimegson.JsonSerializationContext;
|
||||
import com.google.thoughtcrimegson.JsonSerializer;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.internal.util.Base64;
|
||||
import org.whispersystems.textsecure.internal.util.JsonUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
public class PreKeyResponse {
|
||||
|
||||
private IdentityKey identityKey;
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = JsonUtil.IdentityKeySerializer.class)
|
||||
@JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class)
|
||||
private IdentityKey identityKey;
|
||||
|
||||
@JsonProperty
|
||||
private List<PreKeyResponseItem> devices;
|
||||
|
||||
public IdentityKey getIdentityKey() {
|
||||
@@ -46,36 +52,5 @@ public class PreKeyResponse {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public static PreKeyResponse fromJson(String serialized) {
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
return PreKeyResponseItem.forBuilder(builder)
|
||||
.registerTypeAdapter(IdentityKey.class, new IdentityKeyJsonAdapter())
|
||||
.create().fromJson(serialized, PreKeyResponse.class);
|
||||
}
|
||||
|
||||
public static class IdentityKeyJsonAdapter
|
||||
implements JsonSerializer<IdentityKey>, JsonDeserializer<IdentityKey>
|
||||
{
|
||||
@Override
|
||||
public JsonElement serialize(IdentityKey identityKey, Type type,
|
||||
JsonSerializationContext jsonSerializationContext)
|
||||
{
|
||||
return new JsonPrimitive(Base64.encodeBytesWithoutPadding(identityKey.serialize()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey deserialize(JsonElement jsonElement, Type type,
|
||||
JsonDeserializationContext jsonDeserializationContext)
|
||||
throws JsonParseException
|
||||
{
|
||||
try {
|
||||
return new IdentityKey(Base64.decodeWithoutPadding(jsonElement.getAsJsonPrimitive().getAsString()), 0);
|
||||
} catch (InvalidKeyException | IOException e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -16,15 +16,22 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.google.thoughtcrimegson.GsonBuilder;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.whispersystems.textsecure.api.push.SignedPreKeyEntity;
|
||||
|
||||
public class PreKeyResponseItem {
|
||||
|
||||
@JsonProperty
|
||||
private int deviceId;
|
||||
|
||||
@JsonProperty
|
||||
private int registrationId;
|
||||
|
||||
@JsonProperty
|
||||
private SignedPreKeyEntity signedPreKey;
|
||||
|
||||
@JsonProperty
|
||||
private PreKeyEntity preKey;
|
||||
|
||||
public int getDeviceId() {
|
||||
@@ -43,7 +50,4 @@ public class PreKeyResponseItem {
|
||||
return preKey;
|
||||
}
|
||||
|
||||
public static GsonBuilder forBuilder(GsonBuilder builder) {
|
||||
return SignedPreKeyEntity.forBuilder(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.google.thoughtcrimegson.GsonBuilder;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.textsecure.api.push.SignedPreKeyEntity;
|
||||
import org.whispersystems.textsecure.internal.util.JsonUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PreKeyState {
|
||||
|
||||
@JsonProperty
|
||||
@JsonSerialize(using = JsonUtil.IdentityKeySerializer.class)
|
||||
@JsonDeserialize(using = JsonUtil.IdentityKeyDeserializer.class)
|
||||
private IdentityKey identityKey;
|
||||
|
||||
@JsonProperty
|
||||
private List<PreKeyEntity> preKeys;
|
||||
|
||||
@JsonProperty
|
||||
private PreKeyEntity lastResortKey;
|
||||
|
||||
@JsonProperty
|
||||
private SignedPreKeyEntity signedPreKey;
|
||||
|
||||
|
||||
@@ -24,10 +36,4 @@ public class PreKeyState {
|
||||
this.identityKey = identityKey;
|
||||
}
|
||||
|
||||
public static String toJson(PreKeyState state) {
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
return SignedPreKeyEntity.forBuilder(builder)
|
||||
.registerTypeAdapter(IdentityKey.class, new PreKeyResponse.IdentityKeyJsonAdapter())
|
||||
.create().toJson(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class PreKeyStatus {
|
||||
|
||||
@JsonProperty
|
||||
private int count;
|
||||
|
||||
public PreKeyStatus() {}
|
||||
|
||||
@@ -18,8 +18,7 @@ package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.thoughtcrimegson.Gson;
|
||||
import com.google.thoughtcrimegson.JsonParseException;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
@@ -44,6 +43,7 @@ import org.whispersystems.textsecure.internal.push.exceptions.MismatchedDevicesE
|
||||
import org.whispersystems.textsecure.internal.push.exceptions.StaleDevicesException;
|
||||
import org.whispersystems.textsecure.internal.util.Base64;
|
||||
import org.whispersystems.textsecure.internal.util.BlacklistingTrustManager;
|
||||
import org.whispersystems.textsecure.internal.util.JsonUtil;
|
||||
import org.whispersystems.textsecure.internal.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
@@ -115,17 +115,17 @@ public class PushServiceSocket {
|
||||
{
|
||||
AccountAttributes signalingKeyEntity = new AccountAttributes(signalingKey, supportsSms, registrationId);
|
||||
makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode),
|
||||
"PUT", new Gson().toJson(signalingKeyEntity));
|
||||
"PUT", JsonUtil.toJson(signalingKeyEntity));
|
||||
}
|
||||
|
||||
public String getNewDeviceVerificationCode() throws IOException {
|
||||
String responseText = makeRequest(PROVISIONING_CODE_PATH, "GET", null);
|
||||
return new Gson().fromJson(responseText, DeviceCode.class).getVerificationCode();
|
||||
return JsonUtil.fromJson(responseText, DeviceCode.class).getVerificationCode();
|
||||
}
|
||||
|
||||
public void sendProvisioningMessage(String destination, byte[] body) throws IOException {
|
||||
makeRequest(String.format(PROVISIONING_MESSAGE_PATH, destination), "PUT",
|
||||
new Gson().toJson(new ProvisioningMessage(Base64.encodeBytes(body))));
|
||||
JsonUtil.toJson(new ProvisioningMessage(Base64.encodeBytes(body))));
|
||||
}
|
||||
|
||||
public void sendReceipt(String destination, long messageId, String relay) throws IOException {
|
||||
@@ -140,7 +140,7 @@ public class PushServiceSocket {
|
||||
|
||||
public void registerGcmId(String gcmRegistrationId) throws IOException {
|
||||
GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId, true);
|
||||
makeRequest(REGISTER_GCM_PATH, "PUT", new Gson().toJson(registration));
|
||||
makeRequest(REGISTER_GCM_PATH, "PUT", JsonUtil.toJson(registration));
|
||||
}
|
||||
|
||||
public void unregisterGcmId() throws IOException {
|
||||
@@ -151,11 +151,10 @@ public class PushServiceSocket {
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
String responseText = makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", new Gson().toJson(bundle));
|
||||
String responseText = makeRequest(String.format(MESSAGE_PATH, bundle.getDestination()), "PUT", JsonUtil.toJson(bundle));
|
||||
|
||||
if (responseText == null) return new SendMessageResponse(false);
|
||||
else return new Gson().fromJson(responseText, SendMessageResponse.class);
|
||||
|
||||
else return JsonUtil.fromJson(responseText, SendMessageResponse.class);
|
||||
} catch (NotFoundException nfe) {
|
||||
throw new UnregisteredUserException(bundle.getDestination(), nfe);
|
||||
}
|
||||
@@ -184,13 +183,13 @@ public class PushServiceSocket {
|
||||
signedPreKey.getSignature());
|
||||
|
||||
makeRequest(String.format(PREKEY_PATH, ""), "PUT",
|
||||
PreKeyState.toJson(new PreKeyState(entities, lastResortEntity,
|
||||
signedPreKeyEntity, identityKey)));
|
||||
JsonUtil.toJson(new PreKeyState(entities, lastResortEntity,
|
||||
signedPreKeyEntity, identityKey)));
|
||||
}
|
||||
|
||||
public int getAvailablePreKeys() throws IOException {
|
||||
String responseText = makeRequest(PREKEY_METADATA_PATH, "GET", null);
|
||||
PreKeyStatus preKeyStatus = new Gson().fromJson(responseText, PreKeyStatus.class);
|
||||
PreKeyStatus preKeyStatus = JsonUtil.fromJson(responseText, PreKeyStatus.class);
|
||||
|
||||
return preKeyStatus.getCount();
|
||||
}
|
||||
@@ -209,7 +208,7 @@ public class PushServiceSocket {
|
||||
}
|
||||
|
||||
String responseText = makeRequest(path, "GET", null);
|
||||
PreKeyResponse response = PreKeyResponse.fromJson(responseText);
|
||||
PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class);
|
||||
List<PreKeyBundle> bundles = new LinkedList<>();
|
||||
|
||||
for (PreKeyResponseItem device : response.getDevices()) {
|
||||
@@ -236,7 +235,7 @@ public class PushServiceSocket {
|
||||
}
|
||||
|
||||
return bundles;
|
||||
} catch (JsonParseException e) {
|
||||
} catch (JsonUtil.JsonParseException e) {
|
||||
throw new IOException(e);
|
||||
} catch (NotFoundException nfe) {
|
||||
throw new UnregisteredUserException(destination.getNumber(), nfe);
|
||||
@@ -253,7 +252,7 @@ public class PushServiceSocket {
|
||||
}
|
||||
|
||||
String responseText = makeRequest(path, "GET", null);
|
||||
PreKeyResponse response = PreKeyResponse.fromJson(responseText);
|
||||
PreKeyResponse response = JsonUtil.fromJson(responseText, PreKeyResponse.class);
|
||||
|
||||
if (response.getDevices() == null || response.getDevices().size() < 1)
|
||||
throw new IOException("Empty prekey list");
|
||||
@@ -278,7 +277,7 @@ public class PushServiceSocket {
|
||||
|
||||
return new PreKeyBundle(device.getRegistrationId(), device.getDeviceId(), preKeyId, preKey,
|
||||
signedPreKeyId, signedPreKey, signedPreKeySignature, response.getIdentityKey());
|
||||
} catch (JsonParseException e) {
|
||||
} catch (JsonUtil.JsonParseException e) {
|
||||
throw new IOException(e);
|
||||
} catch (NotFoundException nfe) {
|
||||
throw new UnregisteredUserException(destination.getNumber(), nfe);
|
||||
@@ -288,7 +287,7 @@ public class PushServiceSocket {
|
||||
public SignedPreKeyEntity getCurrentSignedPreKey() throws IOException {
|
||||
try {
|
||||
String responseText = makeRequest(SIGNED_PREKEY_PATH, "GET", null);
|
||||
return SignedPreKeyEntity.fromJson(responseText);
|
||||
return JsonUtil.fromJson(responseText, SignedPreKeyEntity.class);
|
||||
} catch (NotFoundException e) {
|
||||
Log.w("PushServiceSocket", e);
|
||||
return null;
|
||||
@@ -299,12 +298,12 @@ public class PushServiceSocket {
|
||||
SignedPreKeyEntity signedPreKeyEntity = new SignedPreKeyEntity(signedPreKey.getId(),
|
||||
signedPreKey.getKeyPair().getPublicKey(),
|
||||
signedPreKey.getSignature());
|
||||
makeRequest(SIGNED_PREKEY_PATH, "PUT", SignedPreKeyEntity.toJson(signedPreKeyEntity));
|
||||
makeRequest(SIGNED_PREKEY_PATH, "PUT", JsonUtil.toJson(signedPreKeyEntity));
|
||||
}
|
||||
|
||||
public long sendAttachment(PushAttachmentData attachment) throws IOException {
|
||||
String response = makeRequest(String.format(ATTACHMENT_PATH, ""), "GET", null);
|
||||
AttachmentDescriptor attachmentKey = new Gson().fromJson(response, AttachmentDescriptor.class);
|
||||
AttachmentDescriptor attachmentKey = JsonUtil.fromJson(response, AttachmentDescriptor.class);
|
||||
|
||||
if (attachmentKey == null || attachmentKey.getLocation() == null) {
|
||||
throw new IOException("Server failed to allocate an attachment key!");
|
||||
@@ -326,7 +325,7 @@ public class PushServiceSocket {
|
||||
}
|
||||
|
||||
String response = makeRequest(path, "GET", null);
|
||||
AttachmentDescriptor descriptor = new Gson().fromJson(response, AttachmentDescriptor.class);
|
||||
AttachmentDescriptor descriptor = JsonUtil.fromJson(response, AttachmentDescriptor.class);
|
||||
|
||||
Log.w("PushServiceSocket", "Attachment: " + attachmentId + " is at: " + descriptor.getLocation());
|
||||
|
||||
@@ -337,8 +336,8 @@ public class PushServiceSocket {
|
||||
throws NonSuccessfulResponseCodeException, PushNetworkException
|
||||
{
|
||||
ContactTokenList contactTokenList = new ContactTokenList(new LinkedList<>(contactTokens));
|
||||
String response = makeRequest(DIRECTORY_TOKENS_PATH, "PUT", new Gson().toJson(contactTokenList));
|
||||
ContactTokenDetailsList activeTokens = new Gson().fromJson(response, ContactTokenDetailsList.class);
|
||||
String response = makeRequest(DIRECTORY_TOKENS_PATH, "PUT", JsonUtil.toJson(contactTokenList));
|
||||
ContactTokenDetailsList activeTokens = JsonUtil.fromJson(response, ContactTokenDetailsList.class);
|
||||
|
||||
return activeTokens.getContacts();
|
||||
}
|
||||
@@ -346,7 +345,7 @@ public class PushServiceSocket {
|
||||
public ContactTokenDetails getContactTokenDetails(String contactToken) throws IOException {
|
||||
try {
|
||||
String response = makeRequest(String.format(DIRECTORY_VERIFY_PATH, contactToken), "GET", null);
|
||||
return new Gson().fromJson(response, ContactTokenDetails.class);
|
||||
return JsonUtil.fromJson(response, ContactTokenDetails.class);
|
||||
} catch (NotFoundException nfe) {
|
||||
return null;
|
||||
}
|
||||
@@ -463,14 +462,14 @@ public class PushServiceSocket {
|
||||
} catch (IOException e) {
|
||||
throw new PushNetworkException(e);
|
||||
}
|
||||
throw new MismatchedDevicesException(new Gson().fromJson(response, MismatchedDevices.class));
|
||||
throw new MismatchedDevicesException(JsonUtil.fromJson(response, MismatchedDevices.class));
|
||||
case 410:
|
||||
try {
|
||||
response = Util.readFully(connection.getErrorStream());
|
||||
} catch (IOException e) {
|
||||
throw new PushNetworkException(e);
|
||||
}
|
||||
throw new StaleDevicesException(new Gson().fromJson(response, StaleDevices.class));
|
||||
throw new StaleDevicesException(JsonUtil.fromJson(response, StaleDevices.class));
|
||||
case 417:
|
||||
throw new ExpectationFailedException();
|
||||
}
|
||||
@@ -524,9 +523,7 @@ public class PushServiceSocket {
|
||||
return connection;
|
||||
} catch (IOException e) {
|
||||
throw new PushNetworkException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (KeyManagementException e) {
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
@@ -540,7 +537,11 @@ public class PushServiceSocket {
|
||||
}
|
||||
|
||||
private static class GcmRegistrationId {
|
||||
|
||||
@JsonProperty
|
||||
private String gcmRegistrationId;
|
||||
|
||||
@JsonProperty
|
||||
private boolean webSocketChannel;
|
||||
|
||||
public GcmRegistrationId() {}
|
||||
@@ -552,7 +553,10 @@ public class PushServiceSocket {
|
||||
}
|
||||
|
||||
private static class AttachmentDescriptor {
|
||||
@JsonProperty
|
||||
private long id;
|
||||
|
||||
@JsonProperty
|
||||
private String location;
|
||||
|
||||
public long getId() {
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
*/
|
||||
package org.whispersystems.textsecure.internal.push;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StaleDevices {
|
||||
|
||||
@JsonProperty
|
||||
private List<Integer> staleDevices;
|
||||
|
||||
public List<Integer> getStaleDevices() {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.whispersystems.textsecure.internal.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class JsonUtil {
|
||||
|
||||
private static final String TAG = JsonUtil.class.getSimpleName();
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
public static String toJson(Object object) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
Log.w(TAG, e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T fromJson(String json, Class<T> clazz) {
|
||||
try {
|
||||
return objectMapper.readValue(json, clazz);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class JsonParseException extends RuntimeException {
|
||||
public JsonParseException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class IdentityKeySerializer extends JsonSerializer<IdentityKey> {
|
||||
@Override
|
||||
public void serialize(IdentityKey value, JsonGenerator gen, SerializerProvider serializers)
|
||||
throws IOException
|
||||
{
|
||||
gen.writeString(Base64.encodeBytesWithoutPadding(value.serialize()));
|
||||
}
|
||||
}
|
||||
|
||||
public static class IdentityKeyDeserializer extends JsonDeserializer<IdentityKey> {
|
||||
@Override
|
||||
public IdentityKey deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
try {
|
||||
return new IdentityKey(Base64.decodeWithoutPadding(p.getValueAsString()), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user