import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
+import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
+import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
+import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
import org.whispersystems.signalservice.internal.util.Hex;
import org.whispersystems.util.Base64;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
}
private SignalServiceAccountManager createSignalServiceAccountManager() {
- return new SignalServiceAccountManager(serviceConfiguration, account.getUuid(), account.getUsername(), account.getPassword(), account.getDeviceId(), userAgent, timer);
+ GroupsV2Operations groupsV2Operations;
+ try {
+ groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceConfiguration));
+ } catch (Throwable ignored) {
+ groupsV2Operations = null;
+ }
+ return new SignalServiceAccountManager(serviceConfiguration,
+ new DynamicCredentialsProvider(account.getUuid(), account.getUsername(), account.getPassword(), null, account.getDeviceId()),
+ userAgent,
+ groupsV2Operations,
+ timer);
}
private IdentityKeyPair getIdentityKeyPair() {
account.setProfileKey(KeyUtils.createProfileKey());
account.save();
}
+ // Store profile keys only in profile store
+ for (ContactInfo contact : account.getContactStore().getContacts()) {
+ String profileKeyString = contact.profileKey;
+ if (profileKeyString == null) {
+ continue;
+ }
+ final ProfileKey profileKey;
+ try {
+ profileKey = new ProfileKey(Base64.decode(profileKeyString));
+ } catch (InvalidInputException | IOException e) {
+ continue;
+ }
+ contact.profileKey = null;
+ account.getProfileStore().storeProfileKey(contact.getAddress(), profileKey);
+ }
}
public void checkAccountState() throws IOException {
private SignalServiceMessageSender getMessageSender() {
// TODO implement ZkGroup support
final ClientZkProfileOperations clientZkProfileOperations = null;
- final boolean attachmentsV3 = false;
final ExecutorService executor = null;
return new SignalServiceMessageSender(serviceConfiguration, account.getUuid(), account.getUsername(), account.getPassword(),
- account.getDeviceId(), account.getSignalProtocolStore(), userAgent, account.isMultiDevice(), attachmentsV3, Optional.fromNullable(messagePipe), Optional.fromNullable(unidentifiedMessagePipe), Optional.absent(), clientZkProfileOperations, executor);
+ account.getDeviceId(), account.getSignalProtocolStore(), userAgent, account.isMultiDevice(), Optional.fromNullable(messagePipe), Optional.fromNullable(unidentifiedMessagePipe), Optional.absent(), clientZkProfileOperations, executor, ServiceConfig.MAX_ENVELOPE_SIZE);
}
private SignalServiceProfile getEncryptedRecipientProfile(SignalServiceAddress address, Optional<UnidentifiedAccess> unidentifiedAccess) throws IOException {
// Profiles are cache for 24h before retrieving them again
if (profileEntry == null || profileEntry.getProfile() == null || now - profileEntry.getLastUpdateTimestamp() > 24 * 60 * 60 * 1000) {
SignalProfile profile = retrieveRecipientProfile(address, unidentifiedAccess, profileKey);
- profileEntry = new SignalProfileEntry(profileKey, now, profile);
- account.getProfileStore().updateProfile(address, profileEntry);
+ account.getProfileStore().updateProfile(address, profileKey, now, profile);
+ return profile;
}
return profileEntry.getProfile();
}
String packId = messageSender.uploadStickerManifest(manifest, packKey);
try {
- return new URI("https", "signal.art", "/addstickers/", "pack_id=" + URLEncoder.encode(packId, "utf-8") + "&pack_key=" + URLEncoder.encode(Hex.toStringCondensed(packKey), "utf-8"))
+ return new URI("https", "signal.art", "/addstickers/", "pack_id=" + URLEncoder.encode(packId, StandardCharsets.UTF_8) + "&pack_key=" + URLEncoder.encode(Hex.toStringCondensed(packKey), StandardCharsets.UTF_8))
.toString();
} catch (URISyntaxException e) {
throw new AssertionError(e);
}
private byte[] getTargetUnidentifiedAccessKey(SignalServiceAddress recipient) {
- ContactInfo contact = account.getContactStore().getContact(recipient);
- if (contact == null || contact.profileKey == null) {
+ ProfileKey theirProfileKey = account.getProfileStore().getProfileKey(recipient);
+ if (theirProfileKey == null) {
return null;
}
- ProfileKey theirProfileKey;
- try {
- theirProfileKey = new ProfileKey(Base64.decode(contact.profileKey));
- } catch (InvalidInputException | IOException e) {
- throw new AssertionError(e);
- }
SignalProfile targetProfile;
try {
targetProfile = getRecipientProfile(recipient, Optional.absent(), theirProfileKey);
SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);
try {
+ long startTime = System.currentTimeMillis();
messageSender.sendMessage(syncMessage, unidentifiedAccess);
- return SendMessageResult.success(recipient, unidentifiedAccess.isPresent(), false);
+ return SendMessageResult.success(recipient, unidentifiedAccess.isPresent(), false, System.currentTimeMillis() - startTime);
} catch (UntrustedIdentityException e) {
account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
return SendMessageResult.identityFailure(recipient, e.getIdentityKey());
}
}
if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) {
+ final ProfileKey profileKey;
+ try {
+ profileKey = new ProfileKey(message.getProfileKey().get());
+ } catch (InvalidInputException e) {
+ throw new AssertionError(e);
+ }
if (source.matches(account.getSelfAddress())) {
- try {
- this.account.setProfileKey(new ProfileKey(message.getProfileKey().get()));
- } catch (InvalidInputException ignored) {
- }
- ContactInfo contact = account.getContactStore().getContact(source);
- if (contact != null) {
- contact.profileKey = Base64.encodeBytes(message.getProfileKey().get());
- account.getContactStore().updateContact(contact);
- }
- } else {
- ContactInfo contact = account.getContactStore().getContact(source);
- if (contact == null) {
- contact = new ContactInfo(source);
- }
- contact.profileKey = Base64.encodeBytes(message.getProfileKey().get());
- account.getContactStore().updateContact(contact);
+ this.account.setProfileKey(profileKey);
}
+ this.account.getProfileStore().storeProfileKey(source, profileKey);
}
if (message.getPreviews().isPresent()) {
final List<SignalServiceDataMessage.Preview> previews = message.getPreviews().get();
if (!envelope.isReceipt()) {
try {
content = decryptMessage(envelope);
- } catch (Exception e) {
+ } catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
+ return;
+ } catch (Exception er) {
+ // All other errors are not recoverable, so delete the cached message
+ try {
+ Files.delete(fileEntry.toPath());
+ } catch (IOException e) {
+ System.err.println("Failed to delete cached message file “" + fileEntry + "”: " + e.getMessage());
+ }
return;
}
List<HandleAction> actions = handleMessage(envelope, content, ignoreAttachments);
e.printStackTrace();
}
}
+ account.save();
queuedActions.clear();
queuedActions = null;
}
System.err.println("Ignoring error: " + e.getMessage());
continue;
}
+
if (envelope.hasSource()) {
// Store uuid if we don't have it already
SignalServiceAddress source = envelope.getSourceAddress();
if (!(exception instanceof org.whispersystems.libsignal.UntrustedIdentityException)) {
File cacheFile = null;
try {
- cacheFile = getMessageCacheFile(envelope.getSourceE164().get(), now, envelope.getTimestamp());
+ String source = envelope.getSourceE164().isPresent() ? envelope.getSourceE164().get() : "";
+ cacheFile = getMessageCacheFile(source, now, envelope.getTimestamp());
Files.delete(cacheFile.toPath());
// Try to delete directory if empty
new File(getMessageCachePath()).delete();
contact.color = c.getColor().get();
}
if (c.getProfileKey().isPresent()) {
- contact.profileKey = Base64.encodeBytes(c.getProfileKey().get().serialize());
+ account.getProfileStore().storeProfileKey(address, c.getProfileKey().get());
}
if (c.getVerified().isPresent()) {
final VerifiedMessage verifiedMessage = c.getVerified().get();
verifiedMessage = new VerifiedMessage(record.getAddress(), currentIdentity.getIdentityKey(), currentIdentity.getTrustLevel().toVerifiedState(), currentIdentity.getDateAdded().getTime());
}
- ProfileKey profileKey = null;
- try {
- profileKey = record.profileKey == null ? null : new ProfileKey(Base64.decode(record.profileKey));
- } catch (InvalidInputException ignored) {
- }
+ ProfileKey profileKey = account.getProfileStore().getProfileKey(record.getAddress());
out.write(new DeviceContact(record.getAddress(), Optional.fromNullable(record.name),
createContactAvatarAttachment(record.number), Optional.fromNullable(record.color),
Optional.fromNullable(verifiedMessage), Optional.fromNullable(profileKey), record.blocked,