...
 
Commits (13)
......@@ -75,7 +75,7 @@ deb:publish:
tags:
- package-signer
before_script:
- mc config host add minio "${S3_URL}" "${AWS_ACCESS_KEY_ID}" "${AWS_SECRET_ACCESS_KEY}"
- mc config host add minio "${S3_URL}" "${DO_ACCESS_KEY_ID}" "${DO_SECRET_ACCESS_KEY}"
- export SUITE=$(get-suite)
- export VERSION=$(./version.sh)
- mkdir -p public/dists/${SUITE}/
......@@ -115,7 +115,7 @@ deb:cleanup:
only:
- schedules
before_script:
- mc config host add minio "${S3_URL}" "${AWS_ACCESS_KEY_ID}" "${AWS_SECRET_ACCESS_KEY}"
- mc config host add minio "${S3_URL}" "${DO_ACCESS_KEY_ID}" "${DO_SECRET_ACCESS_KEY}"
script:
- repo-cron "minio/${S3_BUCKET}"
variables:
......
......@@ -7,8 +7,7 @@ WORKDIR /tmp/src
RUN gradle -Dorg.gradle.daemon=false build
RUN tar xf build/distributions/signald.tar -C /opt
FROM gradle:jre${JAVA_VERSION:-8}-alpine AS release
FROM gradle:jre${JAVA_VERSION:-8} AS release
USER root
WORKDIR /opt
COPY --from=build /opt/signald /opt/signald/
......
......@@ -40,7 +40,6 @@ dependencies {
compile 'com.kohlschutter.junixsocket:junixsocket-native-common:2.3.2'
compile 'org.apache.logging.log4j:log4j-api:2.13.3'
compile 'org.apache.logging.log4j:log4j-core:2.13.3'
compile 'io.sentry:sentry-log4j2:1.7.30'
compile 'org.slf4j:slf4j-nop:1.8.0-beta4'
compile 'info.picocli:picocli:4.3.2'
testCompile 'org.junit.jupiter:junit-jupiter-api:5.3.1'
......
......@@ -17,35 +17,26 @@
package io.finn.signald;
import io.finn.signald.BuildConfig;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.nio.file.Files;
import java.security.Security;
import java.util.concurrent.ConcurrentHashMap;
import io.finn.signald.storage.AccountData;
import org.newsclub.net.unix.AFUNIXServerSocket;
import org.newsclub.net.unix.AFUNIXSocketAddress;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.asamk.signal.util.SecurityProvider;
import io.sentry.Sentry;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.newsclub.net.unix.AFUNIXServerSocket;
import org.newsclub.net.unix.AFUNIXSocketAddress;
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.nio.file.Files;
import java.security.Security;
import java.util.concurrent.ConcurrentHashMap;
@Command(name=BuildConfig.NAME, mixinStandardHelpOptions=true, version=BuildConfig.NAME + " " + BuildConfig.VERSION)
......@@ -64,10 +55,9 @@ public class Main implements Runnable {
@Option(names={"-d", "--data"}, description="Data storage location")
private String data_path = System.getProperty("user.home") + "/.config/signald";
private static final Logger logger = LogManager.getLogger("signald");
private static final Logger logger = LogManager.getLogger();
public void run() {
Logger logger = LogManager.getLogger("signald");
if(verbose) {
Configurator.setLevel(System.getProperty("log4j.logger"), Level.DEBUG);
}
......@@ -75,11 +65,6 @@ public class Main implements Runnable {
logger.debug("Starting " + BuildConfig.NAME + " " + BuildConfig.VERSION);
try {
Sentry.init();
Sentry.getContext().addExtra("release", BuildConfig.VERSION);
Sentry.getContext().addExtra("signal_url", BuildConfig.SIGNAL_URL);
Sentry.getContext().addExtra("signal_cdn_url", BuildConfig.SIGNAL_CDN_URL);
// Workaround for BKS truststore
Security.insertProviderAt(new SecurityProvider(), 1);
Security.addProvider(new BouncyCastleProvider());
......
......@@ -302,7 +302,7 @@ public class Manager {
public void createNewIdentity() {
IdentityKeyPair identityKey = KeyHelper.generateIdentityKeyPair();
int registrationId = KeyHelper.generateRegistrationId(false);
accountData.axolotlStore = new SignalProtocolStore(identityKey, registrationId, accountData::resolveAddress);
accountData.axolotlStore = new SignalProtocolStore(identityKey, registrationId, accountData.getResolver());
accountData.registered = false;
logger.info("Generating new identity pair");
}
......@@ -721,7 +721,7 @@ public class Manager {
return null;
}
address = accountData.resolveAddress(address);
address = accountData.getResolver().resolve(address);
try {
SignalServiceMessageSender messageSender = getMessageSender();
......@@ -745,7 +745,7 @@ public class Manager {
return Collections.emptyList();
}
recipients = accountData.resolveAddresses(recipients);
recipients = accountData.getResolver().resolve(recipients);
SignalServiceDataMessage message = null;
try {
......@@ -821,13 +821,16 @@ public class Manager {
}
}
private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, InvalidMetadataVersionException, ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException, ProtocolLegacyMessageException, ProtocolNoSessionException, ProtocolInvalidVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyException, ProtocolDuplicateMessageException, SelfSendException, UnsupportedDataMessageException {
private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, InvalidMetadataVersionException, ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException, ProtocolLegacyMessageException, ProtocolNoSessionException, ProtocolInvalidVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyException, ProtocolDuplicateMessageException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException {
SignalServiceCipher cipher = new SignalServiceCipher(accountData.address.getSignalServiceAddress(), accountData.axolotlStore, getCertificateValidator());
try {
return cipher.decrypt(envelope);
} catch (ProtocolUntrustedIdentityException e) {
// TODO We don't get the new untrusted identity from ProtocolUntrustedIdentityException anymore ... we need to get it from somewhere else
// signalProtocolStore.saveIdentity(e.getSource(), e, TrustLevel.UNTRUSTED);
if(e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
org.whispersystems.libsignal.UntrustedIdentityException identityException = (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause();
accountData.axolotlStore.saveIdentity(identityException.getName(), identityException.getUntrustedIdentity(), TrustLevel.UNTRUSTED);
throw identityException;
}
throw e;
}
}
......
......@@ -19,6 +19,7 @@ package io.finn.signald;
import io.finn.signald.clientprotocol.v0.JsonMessageEnvelope;
import io.finn.signald.clientprotocol.v0.JsonMessageWrapper;
import io.finn.signald.clientprotocol.v0.JsonUntrustedIdentityException;
import io.finn.signald.exceptions.NoSuchAccountException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -157,19 +158,23 @@ public class MessageReceiver implements Manager.ReceiveMessageHandler, Runnable
public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) {
String type = "message";
if(exception != null) {
logger.catching(exception);
type = "unreadable_message";
logger.catching(exception);
type = "unreadable_message";
}
try {
if(envelope != null) {
JsonMessageEnvelope message = new JsonMessageEnvelope(envelope, content, username);
this.sockets.broadcast(new JsonMessageWrapper(type, message, exception));
} else {
this.sockets.broadcast(new JsonMessageWrapper(type, null, exception));
}
if(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
JsonUntrustedIdentityException message = new JsonUntrustedIdentityException((org.whispersystems.libsignal.UntrustedIdentityException) exception, username);
this.sockets.broadcast(new JsonMessageWrapper("inbound_identity_failure", message, (Throwable)null));
}
if(envelope != null) {
JsonMessageEnvelope message = new JsonMessageEnvelope(envelope, content, username);
this.sockets.broadcast(new JsonMessageWrapper(type, message, exception));
} else {
this.sockets.broadcast(new JsonMessageWrapper(type, null, exception));
}
} catch (IOException | NoSuchAccountException e) {
logger.catching(e);
logger.catching(e);
}
}
}
......@@ -23,7 +23,7 @@ import org.apache.logging.log4j.Logger;
import org.whispersystems.libsignal.logging.SignalProtocolLogger;
class ProtocolLogger implements SignalProtocolLogger {
private static final Logger logger = LogManager.getLogger("signal-protocol");
private static final Logger logger = LogManager.getLogger();
public void log(int priority, String tag, String message) {
logger.debug("[" + tag + "] " + message);
......
......@@ -18,22 +18,41 @@
package io.finn.signald.clientprotocol.v0;
import io.finn.signald.Manager;
import io.finn.signald.clientprotocol.v1.JsonAddress;
import io.finn.signald.exceptions.NoSuchAccountException;
import io.finn.signald.util.SafetyNumberHelper;
import org.asamk.signal.util.Hex;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.UntrustedIdentityException;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.io.IOException;
public class JsonUntrustedIdentityException {
public JsonAddress address;
public JsonAddress local_address;
public JsonAddress remote_address;
public String fingerprint;
public String safety_number;
public JsonRequest request;
public JsonUntrustedIdentityException(IdentityKey key, SignalServiceAddress address, Manager m, JsonRequest request) {
this.address = new JsonAddress(address);
this.local_address = new JsonAddress(m.getOwnAddress());
this.remote_address = new JsonAddress(address);
this.fingerprint = Hex.toStringCondensed(key.getPublicKey().serialize());
this.safety_number = SafetyNumberHelper.computeSafetyNumber(m.getOwnAddress(), m.getIdentity(), m.getOwnAddress(), key);
this.safety_number = SafetyNumberHelper.computeSafetyNumber(m.getOwnAddress(), m.getIdentity(), this.remote_address.getSignalServiceAddress(), key);
this.request = request;
}
public JsonUntrustedIdentityException(UntrustedIdentityException exception, String username) {
this.local_address = new JsonAddress(username);
this.remote_address = new JsonAddress(exception.getName());
this.fingerprint = Hex.toStringCondensed(exception.getUntrustedIdentity().getPublicKey().serialize());
try {
Manager m = Manager.get(username);
this.local_address = new JsonAddress(m.getOwnAddress());
this.safety_number = SafetyNumberHelper.computeSafetyNumber(m.getOwnAddress(), m.getIdentity(), this.remote_address.getSignalServiceAddress(), exception.getUntrustedIdentity());
} catch (IOException | NoSuchAccountException e) {
e.printStackTrace();
}
}
}
......@@ -19,11 +19,16 @@ package io.finn.signald.clientprotocol.v1;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.finn.signald.Util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.UUID;
public class JsonAddress {
private static final Logger logger = LogManager.getLogger();
public String number;
public String uuid;
......@@ -32,8 +37,13 @@ public class JsonAddress {
public JsonAddress() {};
public JsonAddress(String number, UUID uuid) {
this.number = number;
public JsonAddress(String n, UUID uuid) {
if(!n.startsWith("+") && UuidUtil.isUuid(n)) {
logger.warn("Number field has a valid UUID in it! Converting to UUID field (this is to fix a data migration issue in signald, do not rely on this behavior when using the socket API)");
uuid = UUID.fromString(n);
} else {
number = n;
}
if(uuid != null) {
this.uuid = uuid.toString();
}
......@@ -53,7 +63,13 @@ public class JsonAddress {
public JsonAddress(SignalServiceAddress address) {
if(address.getNumber().isPresent()) {
number = address.getNumber().get();
String n = address.getNumber().get();
if(!n.startsWith("+") && UuidUtil.isUuid(n)) {
logger.warn("Number field has a valid UUID in it! Converting to UUID field (this is to fix a data migration issue in signald, do not rely on this behavior when using the socket API)");
uuid = n;
} else {
number = n;
}
}
if(address.getUuid().isPresent()) {
......
......@@ -17,6 +17,7 @@
package io.finn.signald.clientprotocol.v1;
import org.asamk.signal.util.Hex;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
public class JsonSendMessageResult {
......@@ -32,7 +33,7 @@ public class JsonSendMessageResult {
networkFailure = result.isNetworkFailure();
unregisteredFailure = result.isUnregisteredFailure();
if(result.getIdentityFailure() != null) {
identityFailure = result.getIdentityFailure().getIdentityKey().getFingerprint();
identityFailure = Hex.toStringCondensed(result.getIdentityFailure().getIdentityKey().serialize()).trim();
}
}
}
......@@ -65,7 +65,7 @@ public class AccountData {
public List<JsonAddress> recipientStore = new ArrayList<>();
private static String dataPath;
private static Logger logger = LogManager.getLogger("AccountData");
private static final Logger logger = LogManager.getLogger();
public static AccountData load(File storageFile) throws IOException {
logger.debug("Loading account from disk.");
......@@ -93,21 +93,30 @@ public class AccountData {
a.deviceId = registration.getDeviceId();
a.signalingKey = signalingKey;
a.axolotlStore = new SignalProtocolStore(registration.getIdentity(), registrationId, a::resolveAddress);
a.axolotlStore = new SignalProtocolStore(registration.getIdentity(), registrationId, a.getResolver());
a.registered = true;
a.init();
a.save();
}
@JsonIgnore
public AddressResolver getResolver() {
return new Resolver();
}
public void initProtocolStore() {
axolotlStore.sessionStore.setResolver(this::resolveAddress);
axolotlStore.identityKeyStore.setResolver(this::resolveAddress);
axolotlStore.sessionStore.setResolver(getResolver());
axolotlStore.identityKeyStore.setResolver(getResolver());
}
private void update() {
private void update() throws IOException {
if(address == null) {
address = new JsonAddress(username);
}
axolotlStore.sessionStore.resolveAll();
if(axolotlStore.identityKeyStore.dedup()) {
save();
}
}
public void save() throws IOException {
......@@ -182,6 +191,7 @@ public class AccountData {
public void setProfileKey(ProfileKey key) {
if(key == null) {
profileKey = "";
return;
}
profileKey = Base64.encodeBytes(key.serialize());
}
......@@ -205,36 +215,6 @@ public class AccountData {
return address.getUUID();
}
public SignalServiceAddress resolveAddress(String identifier) {
SignalServiceAddress address = AddressUtil.fromIdentifier(identifier);
return resolveAddress(address);
}
public SignalServiceAddress resolveAddress(SignalServiceAddress a) {
if (a.matches(address.getSignalServiceAddress())) {
return address.getSignalServiceAddress();
}
for(JsonAddress i : recipientStore) {
if(i.getSignalServiceAddress().matches(a)) {
i.update(a);
return i.getSignalServiceAddress();
}
}
return a;
}
public Collection<SignalServiceAddress> resolveAddresses(Collection<SignalServiceAddress> partials) {
Collection <SignalServiceAddress> full = new ArrayList<>();
for(SignalServiceAddress p : partials) {
full.add(resolveAddress(p));
}
return full;
}
// Jackson getters and setters
// migrate old threadStore which tracked expiration timers, now moved to groups and contacts
......@@ -254,4 +234,35 @@ public class AccountData {
}
}
}
public class Resolver implements AddressResolver {
public SignalServiceAddress resolve(String identifier) {
SignalServiceAddress address = AddressUtil.fromIdentifier(identifier);
return resolve(address);
}
public SignalServiceAddress resolve(SignalServiceAddress a) {
if (a.matches(address.getSignalServiceAddress())) {
return address.getSignalServiceAddress();
}
for(JsonAddress i : recipientStore) {
if(i.getSignalServiceAddress().matches(a)) {
i.update(a);
return i.getSignalServiceAddress();
}
}
return a;
}
public Collection<SignalServiceAddress> resolve(Collection<SignalServiceAddress> partials) {
Collection <SignalServiceAddress> full = new ArrayList<>();
for(SignalServiceAddress p : partials) {
full.add(resolve(p));
}
return full;
}
}
}
......@@ -19,6 +19,8 @@ package io.finn.signald.storage;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.util.Collection;
// Based on signal-cli's SignalServiceAddressResolver
public interface AddressResolver {
......@@ -26,6 +28,21 @@ public interface AddressResolver {
* Get a SignalServiceAddress with number and/or uuid from an identifier name.
*
* @param identifier can be either a serialized uuid or a e164 phone number
* @return the full address
*/
SignalServiceAddress resolve(String identifier);
/**
* Attempt to create a full SignalServiceAddress with number and uuid from an address that may only have one of them
* @param partial the partial address. If the partial has a full set of identifiers it will be returned unmodified
* @return the full address
*/
SignalServiceAddress resolve(SignalServiceAddress partial);
/**
* Attempt to resolve a collection of addresses
* @param partials a list of addresses to resolve
* @return a list of resolved addresses
*/
SignalServiceAddress resolveSignalServiceAddress(String identifier);
Collection<SignalServiceAddress> resolve(Collection<SignalServiceAddress> partials);
}
\ No newline at end of file
......@@ -35,7 +35,8 @@ import java.util.Map;
@JsonSerialize(using=GroupStore.GroupStoreSerializer.class)
@JsonDeserialize(using=GroupStore.GroupStoreDeserializer.class)
public class GroupStore {
static final Logger logger = LogManager.getLogger(GroupStore.class);
static final Logger logger = LogManager.getLogger();
private static final ObjectMapper jsonProcessor = new ObjectMapper();
private Map<String, GroupInfo> groups = new HashMap<>();
......
......@@ -38,7 +38,8 @@ import java.util.Date;
import java.util.List;
public class IdentityKeyStore implements org.whispersystems.libsignal.state.IdentityKeyStore {
private static Logger log = LogManager.getLogger(IdentityKeyStore.class.getName());
private static final Logger logger = LogManager.getLogger();
private AddressResolver resolver;
public List<IdentityKeyStore.Identity> trustedKeys = new ArrayList<>();
......@@ -75,7 +76,7 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
}
public boolean saveIdentity(String identifier, IdentityKey identityKey, TrustLevel trustLevel) {
return saveIdentity(AddressUtil.fromIdentifier(identifier), identityKey, trustLevel);
return saveIdentity(resolver.resolve(identifier), identityKey, trustLevel);
}
public boolean saveIdentity(SignalServiceAddress address, IdentityKey identityKey, TrustLevel trustLevel) {
return saveIdentity(address, identityKey, trustLevel, null);
......@@ -86,14 +87,14 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
}
private List<Identity> getKeys(String identifier) {
return getKeys(resolver.resolveSignalServiceAddress(identifier));
return getKeys(resolver.resolve(identifier));
}
private List<Identity> getKeys(SignalServiceAddress other) {
List<Identity> matches = new ArrayList<>();
for(Identity key : trustedKeys) {
if(key.address == null) {
log.warn("Key has no address! This may indicate a corrupt data file.");
logger.warn("Key has no address! This may indicate a corrupt data file.");
continue;
}
if(key.address.matches(other)) {
......@@ -122,7 +123,7 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
if(!id.identityKey.equals(identityKey)) {
continue;
}
if(id.trustLevel.compareTo(trustLevel) < 0) {
if(id.trustLevel != null && id.trustLevel.compareTo(trustLevel) < 0) {
id.trustLevel = trustLevel;
}
if(added != null) {
......@@ -131,6 +132,7 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
return true;
}
trustedKeys.add(new Identity(address, identityKey, trustLevel, added != null ? added : new Date()));
return false;
}
......@@ -171,6 +173,32 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
return maxIdentity.getKey();
}
// returns true if any duplicates were found, forcing a save
public boolean dedup() {
List<Identity> duplicates = new ArrayList<>();
// this is a hell of a lot of nested loops...
for(Identity i : trustedKeys) {
for(Identity j : trustedKeys) {
if(i.identityKey.equals(j.identityKey)) {
for(Identity dup : duplicates) {
if(dup.address.matches(i.address)) {
i.address.update(dup.address.getSignalServiceAddress());
continue;
}
}
duplicates.add(i);
}
}
}
for(Identity dup : duplicates) {
logger.warn("Found duplicate identity key with address " + dup.address.toRedactedString());
trustedKeys.remove(dup);
}
return duplicates.size() > 0;
}
@JsonIgnore
public List<Identity> getIdentities() {
return trustedKeys;
......@@ -272,12 +300,17 @@ public class IdentityKeyStore implements org.whispersystems.libsignal.state.Iden
return added.getTime();
}
@JsonGetter("trustLevel")
public int getTrustLevelJSON() {
@JsonGetter("trust")
public String getTrustLevelString() {
if(trustLevel == null) {
return trustLevel.TRUSTED_UNVERIFIED.ordinal();
return trustLevel.TRUSTED_UNVERIFIED.name();
}
return trustLevel.ordinal();
return trustLevel.name();
}
@JsonSetter("trust")
public void setTrustLevelString(String level) {
trustLevel = TrustLevel.valueOf(level);
}
@JsonSetter("trustLevel")
......
......@@ -57,7 +57,7 @@ public class SessionStore implements org.whispersystems.libsignal.state.SessionS
private SignalServiceAddress resolveSignalServiceAddress(String identifier) {
if (resolver != null) {
return resolver.resolveSignalServiceAddress(identifier);
return resolver.resolve(identifier);
} else {
return AddressUtil.fromIdentifier(identifier);
}
......@@ -142,6 +142,12 @@ public class SessionStore implements org.whispersystems.libsignal.state.SessionS
sessions.removeIf(info -> info.address.matches(serviceAddress));
}
public void resolveAll() {
for(SessionInfo s : sessions) {
s.address = resolver.resolve(s.address);
}
}
public static class SessionStoreDeserializer extends JsonDeserializer<SessionStore> {
@Override
......
......@@ -18,6 +18,7 @@
package io.finn.signald.storage;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.asamk.signal.TrustLevel;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyIdException;
......@@ -61,6 +62,10 @@ public class SignalProtocolStore implements org.whispersystems.libsignal.state.S
return identityKeyStore.saveIdentity(address, identityKey);
}
public boolean saveIdentity(String identifier, IdentityKey key, TrustLevel level) {
return identityKeyStore.saveIdentity(identifier, key, level);
}
@Override
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
return identityKeyStore.isTrustedIdentity(address, identityKey, direction);
......
......@@ -18,10 +18,14 @@
package io.finn.signald.util;
import io.finn.signald.clientprotocol.v1.JsonAddress;
import io.finn.signald.storage.AddressResolver;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
public class AddressUtil {
import java.util.ArrayList;
import java.util.Collection;
public class AddressUtil implements AddressResolver {
public static SignalServiceAddress fromIdentifier(String identifier) {
if(UuidUtil.isUuid(identifier)) {
return new SignalServiceAddress(UuidUtil.parseOrNull(identifier), null);
......@@ -41,4 +45,24 @@ public class AddressUtil {
}
return old;
}
@Override
public SignalServiceAddress resolve(String identifier) {
return fromIdentifier(identifier);
}
@Override
public SignalServiceAddress resolve(SignalServiceAddress partial) {
return partial;
}
@Override
public Collection<SignalServiceAddress> resolve(Collection<SignalServiceAddress> partials) {
Collection <SignalServiceAddress> full = new ArrayList<>();
for(SignalServiceAddress p : partials) {
full.add(resolve(p));
}
return full;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="warn" packages="org.apache.logging.log4j.core,io.sentry.log4j2">
<configuration status="warn">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<Sentry name="Sentry" />
</appenders>
<loggers>
<root level="INFO">
<appender-ref ref="Console" />
<!-- Note that the Sentry logging threshold is overridden to the WARN level -->
<appender-ref ref="Sentry" level="WARN" />
</root>
</loggers>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="warn" packages="org.apache.logging.log4j.core,io.sentry.log4j2">
<configuration status="warn">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
<Sentry name="Sentry" />
</appenders>
<loggers>
<root level="DEBUG">
<appender-ref ref="Console" />
<!-- Note that the Sentry logging threshold is overridden to the WARN level -->
<appender-ref ref="Sentry" level="WARN" />
</root>
</loggers>
</configuration>