mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 20:15:21 +00:00
Merge pull request #101 from loki-project/closed-group-sync
Closed group sync
This commit is contained in:
commit
b6d22ae2dc
@ -186,7 +186,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
if (setUpStorageAPIIfNeeded()) {
|
if (setUpStorageAPIIfNeeded()) {
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
if (userHexEncodedPublicKey != null) {
|
if (userHexEncodedPublicKey != null) {
|
||||||
LokiFileServerAPI.Companion.getShared().getDeviceLinks(userHexEncodedPublicKey, true);
|
|
||||||
if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) {
|
if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) {
|
||||||
MultiDeviceUtilities.checkIsRevokedSlaveDevice(this);
|
MultiDeviceUtilities.checkIsRevokedSlaveDevice(this);
|
||||||
}
|
}
|
||||||
|
@ -206,14 +206,11 @@ public class GroupMessageProcessor {
|
|||||||
@NonNull GroupRecord record)
|
@NonNull GroupRecord record)
|
||||||
{
|
{
|
||||||
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender());
|
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender());
|
||||||
String ourPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(context));
|
if (record.getMembers().contains(Address.fromSerialized(hexEncodedPublicKey))) {
|
||||||
// If the requester is a group member and we are admin then we should send them the group update
|
|
||||||
if (record.getMembers().contains(Address.fromSerialized(hexEncodedPublicKey)) && record.getAdmins().contains(Address.fromSerialized(ourPublicKey))) {
|
|
||||||
ApplicationContext.getInstance(context)
|
ApplicationContext.getInstance(context)
|
||||||
.getJobManager()
|
.getJobManager()
|
||||||
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
|
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,18 +85,23 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
|
|||||||
reader = DatabaseFactory.getGroupDatabase(context).getGroups();
|
reader = DatabaseFactory.getGroupDatabase(context).getGroups();
|
||||||
|
|
||||||
while ((record = reader.getNext()) != null) {
|
while ((record = reader.getNext()) != null) {
|
||||||
if (!record.isMms() && !record.isPublicChat() && !record.isRSSFeed()) {
|
if (record.isSignalGroup()) {
|
||||||
List<String> members = new LinkedList<>();
|
List<String> members = new LinkedList<>();
|
||||||
|
List<String> admins = new LinkedList<>();
|
||||||
|
|
||||||
for (Address member : record.getMembers()) {
|
for (Address member : record.getMembers()) {
|
||||||
members.add(member.serialize());
|
members.add(member.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (Address admin : record.getAdmins()) {
|
||||||
|
admins.add(admin.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(record.getId(), record.isMms())), false);
|
Recipient recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(record.getId(), record.isMms())), false);
|
||||||
Optional<Integer> expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
|
Optional<Integer> expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
|
||||||
|
|
||||||
out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()),
|
out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()),
|
||||||
members, getAvatar(record.getAvatar()),
|
members, admins, getAvatar(record.getAvatar()),
|
||||||
record.isActive(), expirationTimer,
|
record.isActive(), expirationTimer,
|
||||||
Optional.of(recipient.getColor().serialize()),
|
Optional.of(recipient.getColor().serialize()),
|
||||||
recipient.isBlocked()));
|
recipient.isBlocked()));
|
||||||
@ -120,7 +125,8 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
public boolean onShouldRetry(@NonNull Exception exception) {
|
||||||
if (exception instanceof PushNetworkException) return true;
|
// Loki - Disabled because we have our own retrying
|
||||||
|
// if (exception instanceof PushNetworkException) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +145,6 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
|
|||||||
.withLength(contactsFile.length())
|
.withLength(contactsFile.length())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// TODO: Message ID
|
|
||||||
messageSender.sendMessage(0, SignalServiceSyncMessage.forGroups(attachmentStream),
|
messageSender.sendMessage(0, SignalServiceSyncMessage.forGroups(attachmentStream),
|
||||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
UnidentifiedAccessUtil.getAccessForSync(context));
|
||||||
}
|
}
|
||||||
|
@ -107,6 +107,7 @@ import org.whispersystems.libsignal.state.PreKeyBundle;
|
|||||||
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
import org.whispersystems.libsignal.state.SignalProtocolStore;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview;
|
||||||
@ -123,6 +124,8 @@ import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMess
|
|||||||
import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream;
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream;
|
||||||
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
|
||||||
|
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
|
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
|
||||||
@ -389,6 +392,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
|
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
|
||||||
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
|
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
|
||||||
else if (syncMessage.getContacts().isPresent()) handleContactSyncMessage(syncMessage.getContacts().get());
|
else if (syncMessage.getContacts().isPresent()) handleContactSyncMessage(syncMessage.getContacts().get());
|
||||||
|
else if (syncMessage.getGroups().isPresent()) handleGroupSyncMessage(content, syncMessage.getGroups().get());
|
||||||
else Log.w(TAG, "Contains no known sync types...");
|
else Log.w(TAG, "Contains no known sync types...");
|
||||||
} else if (content.getCallMessage().isPresent()) {
|
} else if (content.getCallMessage().isPresent()) {
|
||||||
Log.i(TAG, "Got call message...");
|
Log.i(TAG, "Got call message...");
|
||||||
@ -719,6 +723,32 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleGroupSyncMessage(@NonNull SignalServiceContent content, @NonNull SignalServiceAttachment groupMessage) {
|
||||||
|
if (groupMessage.isStream()) {
|
||||||
|
Log.d("Loki", "Received a group sync message.");
|
||||||
|
try {
|
||||||
|
InputStream in = groupMessage.asStream().getInputStream();
|
||||||
|
DeviceGroupsInputStream groupsInputStream = new DeviceGroupsInputStream(in);
|
||||||
|
List<DeviceGroup> groups = groupsInputStream.readAll();
|
||||||
|
for (DeviceGroup group : groups) {
|
||||||
|
SignalServiceGroup serviceGroup = new SignalServiceGroup(
|
||||||
|
SignalServiceGroup.Type.UPDATE,
|
||||||
|
group.getId(),
|
||||||
|
SignalServiceGroup.GroupType.SIGNAL,
|
||||||
|
group.getName().orNull(),
|
||||||
|
group.getMembers(),
|
||||||
|
group.getAvatar().orNull(),
|
||||||
|
group.getAdmins()
|
||||||
|
);
|
||||||
|
SignalServiceDataMessage dataMessage = new SignalServiceDataMessage(content.getTimestamp(), serviceGroup, null, null);
|
||||||
|
GroupMessageProcessor.process(context, content, dataMessage, false);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("Loki", "Failed to sync group due to error: " + e + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
|
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
|
||||||
@NonNull SentTranscriptMessage message)
|
@NonNull SentTranscriptMessage message)
|
||||||
throws StorageFailedException
|
throws StorageFailedException
|
||||||
|
@ -128,7 +128,7 @@ fun sendDeviceLinkMessage(context: Context, hexEncodedPublicKey: String, deviceL
|
|||||||
}
|
}
|
||||||
Promise.ofSuccess(Unit)
|
Promise.ofSuccess(Unit)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d("Loki", "Failed to send device link message to: $hexEncodedPublicKey.")
|
Log.d("Loki", "Failed to send device link message to: $hexEncodedPublicKey due to error: $e.")
|
||||||
Promise.ofFail(e)
|
Promise.ofFail(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.loki.redesign.dialogs.*
|
|||||||
import org.thoughtcrime.securesms.loki.signAndSendDeviceLinkMessage
|
import org.thoughtcrime.securesms.loki.signAndSendDeviceLinkMessage
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.util.Util
|
|
||||||
import org.whispersystems.signalservice.loki.api.DeviceLink
|
import org.whispersystems.signalservice.loki.api.DeviceLink
|
||||||
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
|
import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -145,20 +144,19 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity, LoaderManager
|
|||||||
|
|
||||||
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) {
|
||||||
LokiFileServerAPI.shared.addDeviceLink(deviceLink).success {
|
LokiFileServerAPI.shared.addDeviceLink(deviceLink).success {
|
||||||
signAndSendDeviceLinkMessage(this, deviceLink).success {
|
signAndSendDeviceLinkMessage(this, deviceLink).successUi {
|
||||||
|
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
||||||
|
}.success {
|
||||||
TextSecurePreferences.setMultiDevice(this, true)
|
TextSecurePreferences.setMultiDevice(this, true)
|
||||||
Util.runOnMain {
|
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, this)
|
|
||||||
}
|
|
||||||
Timer().schedule(4000) {
|
Timer().schedule(4000) {
|
||||||
|
MessageSender.syncAllGroups(this@LinkedDevicesActivity)
|
||||||
MessageSender.syncAllContacts(this@LinkedDevicesActivity, Address.fromSerialized(deviceLink.slaveHexEncodedPublicKey))
|
MessageSender.syncAllContacts(this@LinkedDevicesActivity, Address.fromSerialized(deviceLink.slaveHexEncodedPublicKey))
|
||||||
}
|
}
|
||||||
|
}.failUi {
|
||||||
|
Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show()
|
||||||
}.fail {
|
}.fail {
|
||||||
LokiFileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
|
LokiFileServerAPI.shared.removeDeviceLink(deviceLink) // If this fails we have a problem
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
||||||
Util.runOnMain {
|
|
||||||
Toast.makeText(this, "Couldn't link device", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.failUi {
|
}.failUi {
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(deviceLink.slaveHexEncodedPublicKey)
|
||||||
|
@ -16,6 +16,7 @@ import org.thoughtcrime.securesms.loki.redesign.utilities.insertOrUpdate
|
|||||||
import org.thoughtcrime.securesms.util.Base64
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.libsignal.IdentityKey
|
import org.whispersystems.libsignal.IdentityKey
|
||||||
|
import org.whispersystems.libsignal.InvalidKeyException
|
||||||
import org.whispersystems.libsignal.ecc.Curve
|
import org.whispersystems.libsignal.ecc.Curve
|
||||||
import org.whispersystems.libsignal.state.PreKeyBundle
|
import org.whispersystems.libsignal.state.PreKeyBundle
|
||||||
import org.whispersystems.libsignal.util.KeyHelper
|
import org.whispersystems.libsignal.util.KeyHelper
|
||||||
@ -41,6 +42,25 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun generatePreKeyBundle(hexEncodedPublicKey: String): PreKeyBundle? {
|
fun generatePreKeyBundle(hexEncodedPublicKey: String): PreKeyBundle? {
|
||||||
|
var failureCount = 0
|
||||||
|
while (failureCount < 3) {
|
||||||
|
try {
|
||||||
|
val preKey = generatePreKeyBundle(hexEncodedPublicKey, failureCount > 0) ?: return null
|
||||||
|
// Verify the bundle is correct
|
||||||
|
if (!Curve.verifySignature(preKey.identityKey.publicKey, preKey.signedPreKey.serialize(), preKey.signedPreKeySignature)) {
|
||||||
|
throw InvalidKeyException()
|
||||||
|
}
|
||||||
|
return preKey;
|
||||||
|
} catch (e: InvalidKeyException) {
|
||||||
|
failureCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.w("Loki", "Failed to generate a valid pre key bundle for: $hexEncodedPublicKey.")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generatePreKeyBundle(hexEncodedPublicKey: String, forceClean: Boolean): PreKeyBundle? {
|
||||||
|
if (hexEncodedPublicKey.isEmpty()) return null
|
||||||
var registrationID = TextSecurePreferences.getLocalRegistrationId(context)
|
var registrationID = TextSecurePreferences.getLocalRegistrationId(context)
|
||||||
if (registrationID == 0) {
|
if (registrationID == 0) {
|
||||||
registrationID = KeyHelper.generateRegistrationId(false)
|
registrationID = KeyHelper.generateRegistrationId(false)
|
||||||
@ -49,7 +69,7 @@ class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) :
|
|||||||
val deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID
|
val deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID
|
||||||
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getOrCreatePreKeyRecord(hexEncodedPublicKey)
|
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getOrCreatePreKeyRecord(hexEncodedPublicKey)
|
||||||
val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
|
val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
|
||||||
if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
if (!forceClean && TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
||||||
Log.d("Loki", "A signed pre key has already been registered.")
|
Log.d("Loki", "A signed pre key has already been registered.")
|
||||||
} else {
|
} else {
|
||||||
Log.d("Loki", "Registering a new signed pre key.")
|
Log.d("Loki", "Registering a new signed pre key.")
|
||||||
|
@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.jobmanager.Job;
|
|||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||||
@ -81,6 +82,10 @@ public class MessageSender {
|
|||||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, recipient, true));
|
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, recipient, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void syncAllGroups(Context context) {
|
||||||
|
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceGroupUpdateJob());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a contact sync message to all our devices telling them that we want to sync `contact`
|
* Send a contact sync message to all our devices telling them that we want to sync `contact`
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user