mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Pre key bundle removal pt1.
Device link functionality removed from app module.
This commit is contained in:
parent
bb74cdf2a0
commit
3bc4338444
@ -483,11 +483,6 @@
|
|||||||
<action android:name="network.loki.securesms.RESTART" />
|
<action android:name="network.loki.securesms.RESTART" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver android:name="org.thoughtcrime.securesms.service.RotateSignedPreKeyListener">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<receiver android:name="org.thoughtcrime.securesms.service.RotateSenderCertificateListener">
|
<receiver android:name="org.thoughtcrime.securesms.service.RotateSenderCertificateListener">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
@ -43,16 +43,13 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
|
|||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
|
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
|
||||||
import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
|
import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
||||||
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||||
@ -66,7 +63,6 @@ import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
|
|||||||
import org.thoughtcrime.securesms.loki.api.ClosedGroupPoller;
|
import org.thoughtcrime.securesms.loki.api.ClosedGroupPoller;
|
||||||
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
|
||||||
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
import org.thoughtcrime.securesms.loki.api.PublicChatManager;
|
||||||
import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||||
@ -89,7 +85,6 @@ import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
|||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.service.LocalBackupListener;
|
import org.thoughtcrime.securesms.service.LocalBackupListener;
|
||||||
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
|
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
|
||||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
|
||||||
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
@ -229,7 +224,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
initializeExpiringMessageManager();
|
initializeExpiringMessageManager();
|
||||||
initializeTypingStatusRepository();
|
initializeTypingStatusRepository();
|
||||||
initializeTypingStatusSender();
|
initializeTypingStatusSender();
|
||||||
initializeSignedPreKeyCheck();
|
|
||||||
initializePeriodicTasks();
|
initializePeriodicTasks();
|
||||||
initializeWebRtc();
|
initializeWebRtc();
|
||||||
initializePendingMessages();
|
initializePendingMessages();
|
||||||
@ -241,7 +235,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
public void onStart(@NonNull LifecycleOwner owner) {
|
public void onStart(@NonNull LifecycleOwner owner) {
|
||||||
isAppVisible = true;
|
isAppVisible = true;
|
||||||
Log.i(TAG, "App is now visible.");
|
Log.i(TAG, "App is now visible.");
|
||||||
executePendingContactSync();
|
|
||||||
KeyCachingService.onAppForegrounded(this);
|
KeyCachingService.onAppForegrounded(this);
|
||||||
// Loki
|
// Loki
|
||||||
if (poller != null) { poller.setCaughtUp(false); }
|
if (poller != null) { poller.setCaughtUp(false); }
|
||||||
@ -365,14 +358,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
|
|
||||||
private void initializeDependencyInjection() {
|
private void initializeDependencyInjection() {
|
||||||
communicationModule = new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this));
|
communicationModule = new SignalCommunicationModule(this, new SignalServiceNetworkAccess(this));
|
||||||
this.objectGraph = ObjectGraph.create(communicationModule, new AxolotlStorageModule(this));
|
this.objectGraph = ObjectGraph.create(communicationModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeSignedPreKeyCheck() {
|
|
||||||
if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
|
|
||||||
jobManager.add(new CreateSignedPreKeyJob(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeExpiringMessageManager() {
|
private void initializeExpiringMessageManager() {
|
||||||
this.expiringMessageManager = new ExpiringMessageManager(this);
|
this.expiringMessageManager = new ExpiringMessageManager(this);
|
||||||
@ -387,7 +375,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializePeriodicTasks() {
|
private void initializePeriodicTasks() {
|
||||||
RotateSignedPreKeyListener.schedule(this);
|
|
||||||
LocalBackupListener.schedule(this);
|
LocalBackupListener.schedule(this);
|
||||||
RotateSenderCertificateListener.schedule(this);
|
RotateSenderCertificateListener.schedule(this);
|
||||||
BackgroundPollWorker.schedulePeriodic(this); // Loki
|
BackgroundPollWorker.schedulePeriodic(this); // Loki
|
||||||
@ -432,12 +419,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executePendingContactSync() {
|
|
||||||
if (TextSecurePreferences.needsFullContactSync(this)) {
|
|
||||||
ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializePendingMessages() {
|
private void initializePendingMessages() {
|
||||||
if (TextSecurePreferences.getNeedsMessagePull(this)) {
|
if (TextSecurePreferences.getNeedsMessagePull(this)) {
|
||||||
Log.i(TAG, "Scheduling a message fetch.");
|
Log.i(TAG, "Scheduling a message fetch.");
|
||||||
|
@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.database.MmsDatabase.Reader;
|
|||||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
@ -227,9 +226,9 @@ public class DatabaseUpgradeActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (params[0] < SIGNED_PREKEY_VERSION) {
|
if (params[0] < SIGNED_PREKEY_VERSION) {
|
||||||
ApplicationContext.getInstance(getApplicationContext())
|
// ApplicationContext.getInstance(getApplicationContext())
|
||||||
.getJobManager()
|
// .getJobManager()
|
||||||
.add(new CreateSignedPreKeyJob(context));
|
// .add(new CreateSignedPreKeyJob(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params[0] < NO_DECRYPT_QUEUE_VERSION) {
|
if (params[0] < NO_DECRYPT_QUEUE_VERSION) {
|
||||||
|
@ -9,7 +9,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
@ -48,12 +47,6 @@ public class LinkPreviewsIntroFragment extends Fragment {
|
|||||||
View view = inflater.inflate(R.layout.experience_upgrade_link_previews_fragment, container, false);
|
View view = inflater.inflate(R.layout.experience_upgrade_link_previews_fragment, container, false);
|
||||||
|
|
||||||
view.findViewById(R.id.experience_ok_button).setOnClickListener(v -> {
|
view.findViewById(R.id.experience_ok_button).setOnClickListener(v -> {
|
||||||
ApplicationContext.getInstance(requireContext())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isLinkPreviewsEnabled(requireContext())));
|
|
||||||
controller.onLinkPreviewsFinished();
|
controller.onLinkPreviewsFinished();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.TypingIndicatorView;
|
import org.thoughtcrime.securesms.components.TypingIndicatorView;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
@ -60,12 +59,6 @@ public class TypingIndicatorIntroFragment extends Fragment {
|
|||||||
|
|
||||||
private void onButtonClicked(boolean typingEnabled) {
|
private void onButtonClicked(boolean typingEnabled) {
|
||||||
TextSecurePreferences.setTypingIndicatorsEnabled(getContext(), typingEnabled);
|
TextSecurePreferences.setTypingIndicatorsEnabled(getContext(), typingEnabled);
|
||||||
ApplicationContext.getInstance(requireContext())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
|
|
||||||
typingEnabled,
|
|
||||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
|
|
||||||
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
|
|
||||||
|
|
||||||
controller.onTypingIndicatorsFinished();
|
controller.onTypingIndicatorsFinished();
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,6 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
|||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||||
import org.thoughtcrime.securesms.qr.QrCode;
|
import org.thoughtcrime.securesms.qr.QrCode;
|
||||||
@ -188,6 +187,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,13 +602,6 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
|
|||||||
VerifiedStatus.DEFAULT);
|
VerifiedStatus.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationContext.getInstance(getActivity())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceVerifiedUpdateJob(recipient.getAddress(),
|
|
||||||
remoteIdentity,
|
|
||||||
isChecked ? VerifiedStatus.VERIFIED :
|
|
||||||
VerifiedStatus.DEFAULT));
|
|
||||||
|
|
||||||
IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
|
IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -140,7 +140,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
|||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
import org.thoughtcrime.securesms.database.model.StickerRecord;
|
||||||
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||||
@ -954,10 +953,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
||||||
.setBlocked(recipient, false);
|
.setBlocked(recipient, false);
|
||||||
|
|
||||||
ApplicationContext.getInstance(ConversationActivity.this)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceBlockedUpdateJob());
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
@ -1016,10 +1011,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
|
||||||
.setBlocked(recipient, true);
|
.setBlocked(recipient, true);
|
||||||
|
|
||||||
ApplicationContext.getInstance(ConversationActivity.this)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceBlockedUpdateJob());
|
|
||||||
|
|
||||||
Util.runOnMain(() -> finish());
|
Util.runOnMain(() -> finish());
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013-2018 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.thoughtcrime.securesms.crypto;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecurePreKeyStore;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKeyPair;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyException;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyIdException;
|
|
||||||
import org.session.libsignal.libsignal.ecc.Curve;
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair;
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyRecord;
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyStore;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyStore;
|
|
||||||
import org.session.libsignal.libsignal.util.Medium;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PreKeyUtil {
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final String TAG = PreKeyUtil.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final int BATCH_SIZE = 100;
|
|
||||||
|
|
||||||
public synchronized static List<PreKeyRecord> generatePreKeyRecords(Context context) {
|
|
||||||
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
|
|
||||||
List<PreKeyRecord> records = new LinkedList<>();
|
|
||||||
int preKeyIdOffset = TextSecurePreferences.getNextPreKeyId(context);
|
|
||||||
|
|
||||||
for (int i=0;i<BATCH_SIZE;i++) {
|
|
||||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
|
||||||
ECKeyPair keyPair = Curve.generateKeyPair();
|
|
||||||
PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair);
|
|
||||||
|
|
||||||
preKeyStore.storePreKey(preKeyId, record);
|
|
||||||
records.add(record);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextSecurePreferences.setNextPreKeyId(context, (preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
|
||||||
|
|
||||||
return records;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized static SignedPreKeyRecord generateSignedPreKey(Context context, IdentityKeyPair identityKeyPair, boolean active) {
|
|
||||||
try {
|
|
||||||
SignedPreKeyStore signedPreKeyStore = new TextSecurePreKeyStore(context);
|
|
||||||
int signedPreKeyId = TextSecurePreferences.getNextSignedPreKeyId(context);
|
|
||||||
ECKeyPair keyPair = Curve.generateKeyPair();
|
|
||||||
byte[] signature = Curve.calculateSignature(identityKeyPair.getPrivateKey(), keyPair.getPublicKey().serialize());
|
|
||||||
SignedPreKeyRecord record = new SignedPreKeyRecord(signedPreKeyId, System.currentTimeMillis(), keyPair, signature);
|
|
||||||
|
|
||||||
signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record);
|
|
||||||
TextSecurePreferences.setNextSignedPreKeyId(context, (signedPreKeyId + 1) % Medium.MAX_VALUE);
|
|
||||||
|
|
||||||
if (active) {
|
|
||||||
TextSecurePreferences.setActiveSignedPreKeyId(context, signedPreKeyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return record;
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized void setActiveSignedPreKeyId(Context context, int id) {
|
|
||||||
TextSecurePreferences.setActiveSignedPreKeyId(context, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized int getActiveSignedPreKeyId(Context context) {
|
|
||||||
return TextSecurePreferences.getActiveSignedPreKeyId(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// region Loki
|
|
||||||
public static synchronized @Nullable SignedPreKeyRecord getActiveSignedPreKey(Context context) {
|
|
||||||
SignedPreKeyStore signedPreKeyStore = new TextSecurePreKeyStore(context);
|
|
||||||
try {
|
|
||||||
return signedPreKeyStore.loadSignedPreKey(getActiveSignedPreKeyId(context));
|
|
||||||
} catch (InvalidKeyIdException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized static List<PreKeyRecord> generatePreKeyRecords(Context context, int amount) {
|
|
||||||
List<PreKeyRecord> records = new LinkedList<>();
|
|
||||||
int preKeyIDOffset = TextSecurePreferences.getNextPreKeyId(context);
|
|
||||||
for (int i = 0; i < amount; i++) {
|
|
||||||
int preKeyID = (preKeyIDOffset + i) % Medium.MAX_VALUE;
|
|
||||||
ECKeyPair keyPair = Curve.generateKeyPair();
|
|
||||||
PreKeyRecord record = new PreKeyRecord(preKeyID, keyPair);
|
|
||||||
records.add(record);
|
|
||||||
}
|
|
||||||
TextSecurePreferences.setNextPreKeyId(context, (preKeyIDOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
|
|
||||||
return records;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized static void storePreKeyRecords(Context context, List<PreKeyRecord> records) {
|
|
||||||
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
|
|
||||||
for (PreKeyRecord record : records) {
|
|
||||||
preKeyStore.storePreKey(record.getId(), record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized static PreKeyRecord loadPreKey(Context context, int preKeyID) throws InvalidKeyIdException {
|
|
||||||
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
|
|
||||||
return preKeyStore.loadPreKey(preKeyID);
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
}
|
|
@ -19,14 +19,14 @@ import java.util.List;
|
|||||||
|
|
||||||
public class SignalProtocolStoreImpl implements SignalProtocolStore {
|
public class SignalProtocolStoreImpl implements SignalProtocolStore {
|
||||||
|
|
||||||
private final PreKeyStore preKeyStore;
|
// private final PreKeyStore preKeyStore;
|
||||||
private final SignedPreKeyStore signedPreKeyStore;
|
// private final SignedPreKeyStore signedPreKeyStore;
|
||||||
private final IdentityKeyStore identityKeyStore;
|
private final IdentityKeyStore identityKeyStore;
|
||||||
private final SessionStore sessionStore;
|
private final SessionStore sessionStore;
|
||||||
|
|
||||||
public SignalProtocolStoreImpl(Context context) {
|
public SignalProtocolStoreImpl(Context context) {
|
||||||
this.preKeyStore = new TextSecurePreKeyStore(context);
|
// this.preKeyStore = new TextSecurePreKeyStore(context);
|
||||||
this.signedPreKeyStore = new TextSecurePreKeyStore(context);
|
// this.signedPreKeyStore = new TextSecurePreKeyStore(context);
|
||||||
this.identityKeyStore = new TextSecureIdentityKeyStore(context);
|
this.identityKeyStore = new TextSecureIdentityKeyStore(context);
|
||||||
this.sessionStore = new TextSecureSessionStore(context);
|
this.sessionStore = new TextSecureSessionStore(context);
|
||||||
}
|
}
|
||||||
@ -58,22 +58,26 @@ public class SignalProtocolStoreImpl implements SignalProtocolStore {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
||||||
return preKeyStore.loadPreKey(preKeyId);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// return preKeyStore.loadPreKey(preKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePreKey(int preKeyId, PreKeyRecord record) {
|
public void storePreKey(int preKeyId, PreKeyRecord record) {
|
||||||
preKeyStore.storePreKey(preKeyId, record);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// preKeyStore.storePreKey(preKeyId, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsPreKey(int preKeyId) {
|
public boolean containsPreKey(int preKeyId) {
|
||||||
return preKeyStore.containsPreKey(preKeyId);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// return preKeyStore.containsPreKey(preKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removePreKey(int preKeyId) {
|
public void removePreKey(int preKeyId) {
|
||||||
preKeyStore.removePreKey(preKeyId);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// preKeyStore.removePreKey(preKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -108,26 +112,31 @@ public class SignalProtocolStoreImpl implements SignalProtocolStore {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
|
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
|
||||||
return signedPreKeyStore.loadSignedPreKey(signedPreKeyId);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// return signedPreKeyStore.loadSignedPreKey(signedPreKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
||||||
return signedPreKeyStore.loadSignedPreKeys();
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// return signedPreKeyStore.loadSignedPreKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
|
public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
|
||||||
signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsSignedPreKey(int signedPreKeyId) {
|
public boolean containsSignedPreKey(int signedPreKeyId) {
|
||||||
return signedPreKeyStore.containsSignedPreKey(signedPreKeyId);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// return signedPreKeyStore.containsSignedPreKey(signedPreKeyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeSignedPreKey(int signedPreKeyId) {
|
public void removeSignedPreKey(int signedPreKeyId) {
|
||||||
signedPreKeyStore.removeSignedPreKey(signedPreKeyId);
|
throw new UnsupportedOperationException("This method will be removed with refactor.");
|
||||||
|
// signedPreKeyStore.removeSignedPreKey(signedPreKeyId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.crypto.storage;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyIdException;
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyRecord;
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyStore;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyStore;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final String TAG = TextSecurePreKeyStore.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final Object FILE_LOCK = new Object();
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public TextSecurePreKeyStore(@NonNull Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
|
||||||
synchronized (FILE_LOCK) {
|
|
||||||
PreKeyRecord preKeyRecord = DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId);
|
|
||||||
|
|
||||||
if (preKeyRecord == null) throw new InvalidKeyIdException("No such key: " + preKeyId);
|
|
||||||
else return preKeyRecord;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
|
|
||||||
synchronized (FILE_LOCK) {
|
|
||||||
SignedPreKeyRecord signedPreKeyRecord = DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId);
|
|
||||||
|
|
||||||
if (signedPreKeyRecord == null) throw new InvalidKeyIdException("No such signed prekey: " + signedPreKeyId);
|
|
||||||
else return signedPreKeyRecord;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
|
||||||
synchronized (FILE_LOCK) {
|
|
||||||
return DatabaseFactory.getSignedPreKeyDatabase(context).getAllSignedPreKeys();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storePreKey(int preKeyId, PreKeyRecord record) {
|
|
||||||
synchronized (FILE_LOCK) {
|
|
||||||
DatabaseFactory.getPreKeyDatabase(context).insertPreKey(preKeyId, record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
|
|
||||||
synchronized (FILE_LOCK) {
|
|
||||||
DatabaseFactory.getSignedPreKeyDatabase(context).insertSignedPreKey(signedPreKeyId, record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsPreKey(int preKeyId) {
|
|
||||||
return DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsSignedPreKey(int signedPreKeyId) {
|
|
||||||
return DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removePreKey(int preKeyId) {
|
|
||||||
DatabaseFactory.getPreKeyDatabase(context).removePreKey(preKeyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeSignedPreKey(int signedPreKeyId) {
|
|
||||||
DatabaseFactory.getSignedPreKeyDatabase(context).removeSignedPreKey(signedPreKeyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -33,8 +33,6 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
|||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
|
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
|
||||||
@ -68,8 +66,8 @@ public class DatabaseFactory {
|
|||||||
|
|
||||||
// Loki
|
// Loki
|
||||||
private final LokiAPIDatabase lokiAPIDatabase;
|
private final LokiAPIDatabase lokiAPIDatabase;
|
||||||
private final LokiPreKeyRecordDatabase lokiContactPreKeyDatabase;
|
// private final LokiPreKeyRecordDatabase lokiContactPreKeyDatabase;
|
||||||
private final LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase;
|
// private final LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase;
|
||||||
private final LokiMessageDatabase lokiMessageDatabase;
|
private final LokiMessageDatabase lokiMessageDatabase;
|
||||||
private final LokiThreadDatabase lokiThreadDatabase;
|
private final LokiThreadDatabase lokiThreadDatabase;
|
||||||
private final LokiUserDatabase lokiUserDatabase;
|
private final LokiUserDatabase lokiUserDatabase;
|
||||||
@ -166,13 +164,13 @@ public class DatabaseFactory {
|
|||||||
return getInstance(context).lokiAPIDatabase;
|
return getInstance(context).lokiAPIDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LokiPreKeyRecordDatabase getLokiPreKeyRecordDatabase(Context context) {
|
// public static LokiPreKeyRecordDatabase getLokiPreKeyRecordDatabase(Context context) {
|
||||||
return getInstance(context).lokiContactPreKeyDatabase;
|
// return getInstance(context).lokiContactPreKeyDatabase;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public static LokiPreKeyBundleDatabase getLokiPreKeyBundleDatabase(Context context) {
|
// public static LokiPreKeyBundleDatabase getLokiPreKeyBundleDatabase(Context context) {
|
||||||
return getInstance(context).lokiPreKeyBundleDatabase;
|
// return getInstance(context).lokiPreKeyBundleDatabase;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public static LokiMessageDatabase getLokiMessageDatabase(Context context) {
|
public static LokiMessageDatabase getLokiMessageDatabase(Context context) {
|
||||||
return getInstance(context).lokiMessageDatabase;
|
return getInstance(context).lokiMessageDatabase;
|
||||||
@ -226,8 +224,8 @@ public class DatabaseFactory {
|
|||||||
this.jobDatabase = new JobDatabase(context, databaseHelper);
|
this.jobDatabase = new JobDatabase(context, databaseHelper);
|
||||||
this.stickerDatabase = new StickerDatabase(context, databaseHelper, attachmentSecret);
|
this.stickerDatabase = new StickerDatabase(context, databaseHelper, attachmentSecret);
|
||||||
this.lokiAPIDatabase = new LokiAPIDatabase(context, databaseHelper);
|
this.lokiAPIDatabase = new LokiAPIDatabase(context, databaseHelper);
|
||||||
this.lokiContactPreKeyDatabase = new LokiPreKeyRecordDatabase(context, databaseHelper);
|
// this.lokiContactPreKeyDatabase = new LokiPreKeyRecordDatabase(context, databaseHelper);
|
||||||
this.lokiPreKeyBundleDatabase = new LokiPreKeyBundleDatabase(context, databaseHelper);
|
// this.lokiPreKeyBundleDatabase = new LokiPreKeyBundleDatabase(context, databaseHelper);
|
||||||
this.lokiMessageDatabase = new LokiMessageDatabase(context, databaseHelper);
|
this.lokiMessageDatabase = new LokiMessageDatabase(context, databaseHelper);
|
||||||
this.lokiThreadDatabase = new LokiThreadDatabase(context, databaseHelper);
|
this.lokiThreadDatabase = new LokiThreadDatabase(context, databaseHelper);
|
||||||
this.lokiUserDatabase = new LokiUserDatabase(context, databaseHelper);
|
this.lokiUserDatabase = new LokiUserDatabase(context, databaseHelper);
|
||||||
|
@ -1,225 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.database.helpers;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
|
||||||
import org.thoughtcrime.securesms.util.Conversions;
|
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyRecord;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
class PreKeyMigrationHelper {
|
|
||||||
|
|
||||||
private static final String PREKEY_DIRECTORY = "prekeys";
|
|
||||||
private static final String SIGNED_PREKEY_DIRECTORY = "signed_prekeys";
|
|
||||||
|
|
||||||
private static final int PLAINTEXT_VERSION = 2;
|
|
||||||
private static final int CURRENT_VERSION_MARKER = 2;
|
|
||||||
|
|
||||||
private static final String TAG = PreKeyMigrationHelper.class.getSimpleName();
|
|
||||||
|
|
||||||
static boolean migratePreKeys(Context context, SQLiteDatabase database) {
|
|
||||||
File[] preKeyFiles = getPreKeyDirectory(context).listFiles();
|
|
||||||
boolean clean = true;
|
|
||||||
|
|
||||||
if (preKeyFiles != null) {
|
|
||||||
for (File preKeyFile : preKeyFiles) {
|
|
||||||
if (!"index.dat".equals(preKeyFile.getName())) {
|
|
||||||
try {
|
|
||||||
PreKeyRecord preKey = new PreKeyRecord(loadSerializedRecord(preKeyFile));
|
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues();
|
|
||||||
contentValues.put(OneTimePreKeyDatabase.KEY_ID, preKey.getId());
|
|
||||||
contentValues.put(OneTimePreKeyDatabase.PUBLIC_KEY, Base64.encodeBytes(preKey.getKeyPair().getPublicKey().serialize()));
|
|
||||||
contentValues.put(OneTimePreKeyDatabase.PRIVATE_KEY, Base64.encodeBytes(preKey.getKeyPair().getPrivateKey().serialize()));
|
|
||||||
database.insert(OneTimePreKeyDatabase.TABLE_NAME, null, contentValues);
|
|
||||||
Log.i(TAG, "Migrated one-time prekey: " + preKey.getId());
|
|
||||||
} catch (IOException | InvalidMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
clean = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File[] signedPreKeyFiles = getSignedPreKeyDirectory(context).listFiles();
|
|
||||||
|
|
||||||
if (signedPreKeyFiles != null) {
|
|
||||||
for (File signedPreKeyFile : signedPreKeyFiles) {
|
|
||||||
if (!"index.dat".equals(signedPreKeyFile.getName())) {
|
|
||||||
try {
|
|
||||||
SignedPreKeyRecord signedPreKey = new SignedPreKeyRecord(loadSerializedRecord(signedPreKeyFile));
|
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues();
|
|
||||||
contentValues.put(SignedPreKeyDatabase.KEY_ID, signedPreKey.getId());
|
|
||||||
contentValues.put(SignedPreKeyDatabase.PUBLIC_KEY, Base64.encodeBytes(signedPreKey.getKeyPair().getPublicKey().serialize()));
|
|
||||||
contentValues.put(SignedPreKeyDatabase.PRIVATE_KEY, Base64.encodeBytes(signedPreKey.getKeyPair().getPrivateKey().serialize()));
|
|
||||||
contentValues.put(SignedPreKeyDatabase.SIGNATURE, Base64.encodeBytes(signedPreKey.getSignature()));
|
|
||||||
contentValues.put(SignedPreKeyDatabase.TIMESTAMP, signedPreKey.getTimestamp());
|
|
||||||
database.insert(SignedPreKeyDatabase.TABLE_NAME, null, contentValues);
|
|
||||||
Log.i(TAG, "Migrated signed prekey: " + signedPreKey.getId());
|
|
||||||
} catch (IOException | InvalidMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
clean = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File oneTimePreKeyIndex = new File(getPreKeyDirectory(context), PreKeyIndex.FILE_NAME);
|
|
||||||
File signedPreKeyIndex = new File(getSignedPreKeyDirectory(context), SignedPreKeyIndex.FILE_NAME);
|
|
||||||
|
|
||||||
if (oneTimePreKeyIndex.exists()) {
|
|
||||||
try {
|
|
||||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(oneTimePreKeyIndex));
|
|
||||||
PreKeyIndex index = JsonUtils.fromJson(reader, PreKeyIndex.class);
|
|
||||||
reader.close();
|
|
||||||
|
|
||||||
Log.i(TAG, "Setting next prekey id: " + index.nextPreKeyId);
|
|
||||||
TextSecurePreferences.setNextPreKeyId(context, index.nextPreKeyId);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signedPreKeyIndex.exists()) {
|
|
||||||
try {
|
|
||||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(signedPreKeyIndex));
|
|
||||||
SignedPreKeyIndex index = JsonUtils.fromJson(reader, SignedPreKeyIndex.class);
|
|
||||||
reader.close();
|
|
||||||
|
|
||||||
Log.i(TAG, "Setting next signed prekey id: " + index.nextSignedPreKeyId);
|
|
||||||
Log.i(TAG, "Setting active signed prekey id: " + index.activeSignedPreKeyId);
|
|
||||||
TextSecurePreferences.setNextSignedPreKeyId(context, index.nextSignedPreKeyId);
|
|
||||||
TextSecurePreferences.setActiveSignedPreKeyId(context, index.activeSignedPreKeyId);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cleanUpPreKeys(@NonNull Context context) {
|
|
||||||
File preKeyDirectory = getPreKeyDirectory(context);
|
|
||||||
File[] preKeyFiles = preKeyDirectory.listFiles();
|
|
||||||
|
|
||||||
if (preKeyFiles != null) {
|
|
||||||
for (File preKeyFile : preKeyFiles) {
|
|
||||||
Log.i(TAG, "Deleting: " + preKeyFile.getAbsolutePath());
|
|
||||||
preKeyFile.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Deleting: " + preKeyDirectory.getAbsolutePath());
|
|
||||||
preKeyDirectory.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
File signedPreKeyDirectory = getSignedPreKeyDirectory(context);
|
|
||||||
File[] signedPreKeyFiles = signedPreKeyDirectory.listFiles();
|
|
||||||
|
|
||||||
if (signedPreKeyFiles != null) {
|
|
||||||
for (File signedPreKeyFile : signedPreKeyFiles) {
|
|
||||||
Log.i(TAG, "Deleting: " + signedPreKeyFile.getAbsolutePath());
|
|
||||||
signedPreKeyFile.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Deleting: " + signedPreKeyDirectory.getAbsolutePath());
|
|
||||||
signedPreKeyDirectory.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] loadSerializedRecord(File recordFile)
|
|
||||||
throws IOException, InvalidMessageException
|
|
||||||
{
|
|
||||||
FileInputStream fin = new FileInputStream(recordFile);
|
|
||||||
int recordVersion = readInteger(fin);
|
|
||||||
|
|
||||||
if (recordVersion > CURRENT_VERSION_MARKER) {
|
|
||||||
throw new IOException("Invalid version: " + recordVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] serializedRecord = readBlob(fin);
|
|
||||||
|
|
||||||
if (recordVersion < PLAINTEXT_VERSION) {
|
|
||||||
throw new IOException("Migration didn't happen! " + recordFile.getAbsolutePath() + ", " + recordVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
fin.close();
|
|
||||||
return serializedRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getPreKeyDirectory(Context context) {
|
|
||||||
return getRecordsDirectory(context, PREKEY_DIRECTORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getSignedPreKeyDirectory(Context context) {
|
|
||||||
return getRecordsDirectory(context, SIGNED_PREKEY_DIRECTORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getRecordsDirectory(Context context, String directoryName) {
|
|
||||||
File directory = new File(context.getFilesDir(), directoryName);
|
|
||||||
|
|
||||||
if (!directory.exists()) {
|
|
||||||
if (!directory.mkdirs()) {
|
|
||||||
Log.w(TAG, "PreKey directory creation failed!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] readBlob(FileInputStream in) throws IOException {
|
|
||||||
int length = readInteger(in);
|
|
||||||
byte[] blobBytes = new byte[length];
|
|
||||||
|
|
||||||
in.read(blobBytes, 0, blobBytes.length);
|
|
||||||
return blobBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int readInteger(FileInputStream in) throws IOException {
|
|
||||||
byte[] integer = new byte[4];
|
|
||||||
in.read(integer, 0, integer.length);
|
|
||||||
return Conversions.byteArrayToInt(integer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PreKeyIndex {
|
|
||||||
static final String FILE_NAME = "index.dat";
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private int nextPreKeyId;
|
|
||||||
|
|
||||||
public PreKeyIndex() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SignedPreKeyIndex {
|
|
||||||
static final String FILE_NAME = "index.dat";
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private int nextSignedPreKeyId;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private int activeSignedPreKeyId = -1;
|
|
||||||
|
|
||||||
public SignedPreKeyIndex() {}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ import net.sqlcipher.database.SQLiteDatabase;
|
|||||||
import net.sqlcipher.database.SQLiteDatabaseHook;
|
import net.sqlcipher.database.SQLiteDatabaseHook;
|
||||||
import net.sqlcipher.database.SQLiteOpenHelper;
|
import net.sqlcipher.database.SQLiteOpenHelper;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
||||||
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
@ -24,7 +24,6 @@ import org.thoughtcrime.securesms.database.GroupDatabase;
|
|||||||
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
|
import org.thoughtcrime.securesms.database.GroupReceiptDatabase;
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||||
import org.thoughtcrime.securesms.database.JobDatabase;
|
import org.thoughtcrime.securesms.database.JobDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
|
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
|
||||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||||
@ -35,12 +34,10 @@ import org.thoughtcrime.securesms.database.SignedPreKeyDatabase;
|
|||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
|
||||||
|
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
|
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
|
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
|
||||||
@ -48,10 +45,8 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
|||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
@ -161,8 +156,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand());
|
||||||
db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
|
db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
|
||||||
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
|
|
||||||
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
|
|
||||||
db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand());
|
db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand());
|
||||||
db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand());
|
db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand());
|
||||||
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
|
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
|
||||||
@ -195,12 +188,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null);
|
if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null);
|
||||||
else TextSecurePreferences.setNeedsSqlCipherMigration(context, true);
|
else TextSecurePreferences.setNeedsSqlCipherMigration(context, true);
|
||||||
|
|
||||||
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
SessionStoreMigrationHelper.migrateSessions(context, db);
|
SessionStoreMigrationHelper.migrateSessions(context, db);
|
||||||
PreKeyMigrationHelper.cleanUpPreKeys(context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,10 +217,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
if (oldVersion < MIGRATE_PREKEYS_VERSION) {
|
if (oldVersion < MIGRATE_PREKEYS_VERSION) {
|
||||||
db.execSQL("CREATE TABLE signed_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL, signature TEXT NOT NULL, timestamp INTEGER DEFAULT 0)");
|
db.execSQL("CREATE TABLE signed_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL, signature TEXT NOT NULL, timestamp INTEGER DEFAULT 0)");
|
||||||
db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)");
|
db.execSQL("CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL)");
|
||||||
|
|
||||||
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
|
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldVersion < MIGRATE_SESSIONS_VERSION) {
|
if (oldVersion < MIGRATE_SESSIONS_VERSION) {
|
||||||
@ -671,7 +655,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (oldVersion < MIGRATE_PREKEYS_VERSION) {
|
if (oldVersion < MIGRATE_PREKEYS_VERSION) {
|
||||||
PreKeyMigrationHelper.cleanUpPreKeys(context);
|
// PreKeyMigrationHelper.cleanUpPreKeys(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.dependencies;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
|
|
||||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyStore;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module (complete = false, injects = {CleanPreKeysJob.class})
|
|
||||||
public class AxolotlStorageModule {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public AxolotlStorageModule(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides SignedPreKeyStoreFactory provideSignedPreKeyStoreFactory() {
|
|
||||||
return new SignedPreKeyStoreFactory() {
|
|
||||||
@Override
|
|
||||||
public SignedPreKeyStore create() {
|
|
||||||
return new SignalProtocolStoreImpl(context);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface SignedPreKeyStoreFactory {
|
|
||||||
public SignedPreKeyStore create();
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,17 +16,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackOperationJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackSyncJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||||
@ -34,14 +23,12 @@ import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
|||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.StickerDownloadJob;
|
import org.thoughtcrime.securesms.jobs.StickerDownloadJob;
|
||||||
@ -50,7 +37,6 @@ import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
|||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||||
import org.thoughtcrime.securesms.push.MessageSenderEventListener;
|
import org.thoughtcrime.securesms.push.MessageSenderEventListener;
|
||||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||||
@ -64,34 +50,23 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import network.loki.messenger.BuildConfig;
|
import network.loki.messenger.BuildConfig;
|
||||||
|
|
||||||
@Module(complete = false, injects = {CleanPreKeysJob.class,
|
@Module(complete = false, injects = {PushGroupSendJob.class,
|
||||||
CreateSignedPreKeyJob.class,
|
|
||||||
PushGroupSendJob.class,
|
|
||||||
PushTextSendJob.class,
|
PushTextSendJob.class,
|
||||||
PushMediaSendJob.class,
|
PushMediaSendJob.class,
|
||||||
AttachmentDownloadJob.class,
|
AttachmentDownloadJob.class,
|
||||||
RefreshPreKeysJob.class,
|
|
||||||
IncomingMessageObserver.class,
|
IncomingMessageObserver.class,
|
||||||
PushNotificationReceiveJob.class,
|
PushNotificationReceiveJob.class,
|
||||||
MultiDeviceContactUpdateJob.class,
|
|
||||||
MultiDeviceGroupUpdateJob.class,
|
|
||||||
MultiDeviceReadUpdateJob.class,
|
|
||||||
MultiDeviceBlockedUpdateJob.class,
|
|
||||||
RefreshAttributesJob.class,
|
RefreshAttributesJob.class,
|
||||||
RequestGroupInfoJob.class,
|
RequestGroupInfoJob.class,
|
||||||
PushGroupUpdateJob.class,
|
PushGroupUpdateJob.class,
|
||||||
AvatarDownloadJob.class,
|
AvatarDownloadJob.class,
|
||||||
RotateSignedPreKeyJob.class,
|
|
||||||
RetrieveProfileJob.class,
|
RetrieveProfileJob.class,
|
||||||
MultiDeviceVerifiedUpdateJob.class,
|
|
||||||
RetrieveProfileAvatarJob.class,
|
RetrieveProfileAvatarJob.class,
|
||||||
MultiDeviceProfileKeyUpdateJob.class,
|
|
||||||
SendReadReceiptJob.class,
|
SendReadReceiptJob.class,
|
||||||
AppProtectionPreferenceFragment.class,
|
AppProtectionPreferenceFragment.class,
|
||||||
RotateCertificateJob.class,
|
RotateCertificateJob.class,
|
||||||
SendDeliveryReceiptJob.class,
|
SendDeliveryReceiptJob.class,
|
||||||
RotateProfileKeyJob.class,
|
RotateProfileKeyJob.class,
|
||||||
MultiDeviceConfigurationUpdateJob.class,
|
|
||||||
RefreshUnidentifiedDeliveryAbilityJob.class,
|
RefreshUnidentifiedDeliveryAbilityJob.class,
|
||||||
TypingSendJob.class,
|
TypingSendJob.class,
|
||||||
AttachmentUploadJob.class,
|
AttachmentUploadJob.class,
|
||||||
@ -100,10 +75,7 @@ import network.loki.messenger.BuildConfig;
|
|||||||
StickerPackPreviewRepository.class,
|
StickerPackPreviewRepository.class,
|
||||||
StickerRemoteUriLoader.Factory.class,
|
StickerRemoteUriLoader.Factory.class,
|
||||||
StickerPackDownloadJob.class,
|
StickerPackDownloadJob.class,
|
||||||
MultiDeviceStickerPackOperationJob.class,
|
LinkPreviewRepository.class})
|
||||||
MultiDeviceStickerPackSyncJob.class,
|
|
||||||
LinkPreviewRepository.class,
|
|
||||||
MultiDeviceOpenGroupUpdateJob.class})
|
|
||||||
|
|
||||||
public class SignalCommunicationModule {
|
public class SignalCommunicationModule {
|
||||||
|
|
||||||
@ -149,7 +121,8 @@ public class SignalCommunicationModule {
|
|||||||
DatabaseFactory.getSSKDatabase(context),
|
DatabaseFactory.getSSKDatabase(context),
|
||||||
DatabaseFactory.getLokiThreadDatabase(context),
|
DatabaseFactory.getLokiThreadDatabase(context),
|
||||||
DatabaseFactory.getLokiMessageDatabase(context),
|
DatabaseFactory.getLokiMessageDatabase(context),
|
||||||
DatabaseFactory.getLokiPreKeyBundleDatabase(context),
|
// DatabaseFactory.getLokiPreKeyBundleDatabase(context),
|
||||||
|
null,
|
||||||
new SessionResetImplementation(context),
|
new SessionResetImplementation(context),
|
||||||
DatabaseFactory.getLokiUserDatabase(context),
|
DatabaseFactory.getLokiUserDatabase(context),
|
||||||
DatabaseFactory.getGroupDatabase(context),
|
DatabaseFactory.getGroupDatabase(context),
|
||||||
|
@ -6,19 +6,10 @@ import androidx.annotation.Nullable;
|
|||||||
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.LocalBackupJob;
|
import org.thoughtcrime.securesms.jobs.LocalBackupJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MmsDownloadJob;
|
import org.thoughtcrime.securesms.jobs.MmsDownloadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MmsReceiveJob;
|
import org.thoughtcrime.securesms.jobs.MmsReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||||
@ -27,14 +18,12 @@ import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
|||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SmsReceiveJob;
|
import org.thoughtcrime.securesms.jobs.SmsReceiveJob;
|
||||||
@ -48,7 +37,6 @@ import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob;
|
|||||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -59,21 +47,11 @@ public class WorkManagerFactoryMappings {
|
|||||||
put(AttachmentDownloadJob.class.getName(), AttachmentDownloadJob.KEY);
|
put(AttachmentDownloadJob.class.getName(), AttachmentDownloadJob.KEY);
|
||||||
put(AttachmentUploadJob.class.getName(), AttachmentUploadJob.KEY);
|
put(AttachmentUploadJob.class.getName(), AttachmentUploadJob.KEY);
|
||||||
put(AvatarDownloadJob.class.getName(), AvatarDownloadJob.KEY);
|
put(AvatarDownloadJob.class.getName(), AvatarDownloadJob.KEY);
|
||||||
put(CleanPreKeysJob.class.getName(), CleanPreKeysJob.KEY);
|
|
||||||
put(ClosedGroupUpdateMessageSendJob.class.getName(), ClosedGroupUpdateMessageSendJob.KEY);
|
put(ClosedGroupUpdateMessageSendJob.class.getName(), ClosedGroupUpdateMessageSendJob.KEY);
|
||||||
put(CreateSignedPreKeyJob.class.getName(), CreateSignedPreKeyJob.KEY);
|
|
||||||
put(LocalBackupJob.class.getName(), LocalBackupJob.KEY);
|
put(LocalBackupJob.class.getName(), LocalBackupJob.KEY);
|
||||||
put(MmsDownloadJob.class.getName(), MmsDownloadJob.KEY);
|
put(MmsDownloadJob.class.getName(), MmsDownloadJob.KEY);
|
||||||
put(MmsReceiveJob.class.getName(), MmsReceiveJob.KEY);
|
put(MmsReceiveJob.class.getName(), MmsReceiveJob.KEY);
|
||||||
put(MmsSendJob.class.getName(), MmsSendJob.KEY);
|
put(MmsSendJob.class.getName(), MmsSendJob.KEY);
|
||||||
put(MultiDeviceBlockedUpdateJob.class.getName(), MultiDeviceBlockedUpdateJob.KEY);
|
|
||||||
put(MultiDeviceConfigurationUpdateJob.class.getName(), MultiDeviceConfigurationUpdateJob.KEY);
|
|
||||||
put(MultiDeviceContactUpdateJob.class.getName(), MultiDeviceContactUpdateJob.KEY);
|
|
||||||
put(MultiDeviceGroupUpdateJob.class.getName(), MultiDeviceGroupUpdateJob.KEY);
|
|
||||||
put(MultiDeviceProfileKeyUpdateJob.class.getName(), MultiDeviceProfileKeyUpdateJob.KEY);
|
|
||||||
put(MultiDeviceOpenGroupUpdateJob.class.getName(), MultiDeviceOpenGroupUpdateJob.KEY);
|
|
||||||
put(MultiDeviceReadUpdateJob.class.getName(), MultiDeviceReadUpdateJob.KEY);
|
|
||||||
put(MultiDeviceVerifiedUpdateJob.class.getName(), MultiDeviceVerifiedUpdateJob.KEY);
|
|
||||||
put(NullMessageSendJob.class.getName(), NullMessageSendJob.KEY);
|
put(NullMessageSendJob.class.getName(), NullMessageSendJob.KEY);
|
||||||
put(PushContentReceiveJob.class.getName(), PushContentReceiveJob.KEY);
|
put(PushContentReceiveJob.class.getName(), PushContentReceiveJob.KEY);
|
||||||
put(PushDecryptJob.class.getName(), PushDecryptJob.KEY);
|
put(PushDecryptJob.class.getName(), PushDecryptJob.KEY);
|
||||||
@ -83,14 +61,12 @@ public class WorkManagerFactoryMappings {
|
|||||||
put(PushNotificationReceiveJob.class.getName(), PushNotificationReceiveJob.KEY);
|
put(PushNotificationReceiveJob.class.getName(), PushNotificationReceiveJob.KEY);
|
||||||
put(PushTextSendJob.class.getName(), PushTextSendJob.KEY);
|
put(PushTextSendJob.class.getName(), PushTextSendJob.KEY);
|
||||||
put(RefreshAttributesJob.class.getName(), RefreshAttributesJob.KEY);
|
put(RefreshAttributesJob.class.getName(), RefreshAttributesJob.KEY);
|
||||||
put(RefreshPreKeysJob.class.getName(), RefreshPreKeysJob.KEY);
|
|
||||||
put(RefreshUnidentifiedDeliveryAbilityJob.class.getName(), RefreshUnidentifiedDeliveryAbilityJob.KEY);
|
put(RefreshUnidentifiedDeliveryAbilityJob.class.getName(), RefreshUnidentifiedDeliveryAbilityJob.KEY);
|
||||||
put(RequestGroupInfoJob.class.getName(), RequestGroupInfoJob.KEY);
|
put(RequestGroupInfoJob.class.getName(), RequestGroupInfoJob.KEY);
|
||||||
put(RetrieveProfileAvatarJob.class.getName(), RetrieveProfileAvatarJob.KEY);
|
put(RetrieveProfileAvatarJob.class.getName(), RetrieveProfileAvatarJob.KEY);
|
||||||
put(RetrieveProfileJob.class.getName(), RetrieveProfileJob.KEY);
|
put(RetrieveProfileJob.class.getName(), RetrieveProfileJob.KEY);
|
||||||
put(RotateCertificateJob.class.getName(), RotateCertificateJob.KEY);
|
put(RotateCertificateJob.class.getName(), RotateCertificateJob.KEY);
|
||||||
put(RotateProfileKeyJob.class.getName(), RotateProfileKeyJob.KEY);
|
put(RotateProfileKeyJob.class.getName(), RotateProfileKeyJob.KEY);
|
||||||
put(RotateSignedPreKeyJob.class.getName(), RotateSignedPreKeyJob.KEY);
|
|
||||||
put(SendDeliveryReceiptJob.class.getName(), SendDeliveryReceiptJob.KEY);
|
put(SendDeliveryReceiptJob.class.getName(), SendDeliveryReceiptJob.KEY);
|
||||||
put(SendReadReceiptJob.class.getName(), SendReadReceiptJob.KEY);
|
put(SendReadReceiptJob.class.getName(), SendReadReceiptJob.KEY);
|
||||||
put(SessionRequestMessageSendJob.class.getName(), SessionRequestMessageSendJob.KEY);
|
put(SessionRequestMessageSendJob.class.getName(), SessionRequestMessageSendJob.KEY);
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyIdException;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyStore;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceAccountManager;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.dependencies.AxolotlStorageModule.SignedPreKeyStoreFactory;
|
|
||||||
|
|
||||||
public class CleanPreKeysJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "CleanPreKeysJob";
|
|
||||||
|
|
||||||
private static final String TAG = CleanPreKeysJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final long ARCHIVE_AGE = TimeUnit.DAYS.toMillis(7);
|
|
||||||
|
|
||||||
@Inject SignalServiceAccountManager accountManager;
|
|
||||||
@Inject SignedPreKeyStoreFactory signedPreKeyStoreFactory;
|
|
||||||
|
|
||||||
public CleanPreKeysJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("CleanPreKeysJob")
|
|
||||||
.setMaxAttempts(3)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CleanPreKeysJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException {
|
|
||||||
try {
|
|
||||||
Log.i(TAG, "Cleaning prekeys...");
|
|
||||||
|
|
||||||
int activeSignedPreKeyId = PreKeyUtil.getActiveSignedPreKeyId(context);
|
|
||||||
SignedPreKeyStore signedPreKeyStore = signedPreKeyStoreFactory.create();
|
|
||||||
|
|
||||||
if (activeSignedPreKeyId < 0) return;
|
|
||||||
|
|
||||||
SignedPreKeyRecord currentRecord = signedPreKeyStore.loadSignedPreKey(activeSignedPreKeyId);
|
|
||||||
List<SignedPreKeyRecord> allRecords = signedPreKeyStore.loadSignedPreKeys();
|
|
||||||
LinkedList<SignedPreKeyRecord> oldRecords = removeRecordFrom(currentRecord, allRecords);
|
|
||||||
|
|
||||||
Collections.sort(oldRecords, new SignedPreKeySorter());
|
|
||||||
|
|
||||||
Log.i(TAG, "Active signed prekey: " + activeSignedPreKeyId);
|
|
||||||
Log.i(TAG, "Old signed prekey record count: " + oldRecords.size());
|
|
||||||
|
|
||||||
boolean foundAgedRecord = false;
|
|
||||||
|
|
||||||
for (SignedPreKeyRecord oldRecord : oldRecords) {
|
|
||||||
long archiveDuration = System.currentTimeMillis() - oldRecord.getTimestamp();
|
|
||||||
|
|
||||||
if (archiveDuration >= ARCHIVE_AGE) {
|
|
||||||
if (!foundAgedRecord) {
|
|
||||||
foundAgedRecord = true;
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Removing signed prekey record: " + oldRecord.getId() + " with timestamp: " + oldRecord.getTimestamp());
|
|
||||||
signedPreKeyStore.removeSignedPreKey(oldRecord.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InvalidKeyIdException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception throwable) {
|
|
||||||
if (throwable instanceof NonSuccessfulResponseCodeException) return false;
|
|
||||||
if (throwable instanceof PushNetworkException) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
Log.w(TAG, "Failed to execute clean signed prekeys task.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private LinkedList<SignedPreKeyRecord> removeRecordFrom(SignedPreKeyRecord currentRecord,
|
|
||||||
List<SignedPreKeyRecord> records)
|
|
||||||
|
|
||||||
{
|
|
||||||
LinkedList<SignedPreKeyRecord> others = new LinkedList<>();
|
|
||||||
|
|
||||||
for (SignedPreKeyRecord record : records) {
|
|
||||||
if (record.getId() != currentRecord.getId()) {
|
|
||||||
others.add(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return others;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SignedPreKeySorter implements Comparator<SignedPreKeyRecord> {
|
|
||||||
@Override
|
|
||||||
public int compare(SignedPreKeyRecord lhs, SignedPreKeyRecord rhs) {
|
|
||||||
if (lhs.getTimestamp() > rhs.getTimestamp()) return -1;
|
|
||||||
else if (lhs.getTimestamp() < rhs.getTimestamp()) return 1;
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<CleanPreKeysJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull CleanPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new CleanPreKeysJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKeyPair;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceAccountManager;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
|
|
||||||
public class CreateSignedPreKeyJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "CreateSignedPreKeyJob";
|
|
||||||
|
|
||||||
private static final String TAG = CreateSignedPreKeyJob.class.getSimpleName();
|
|
||||||
|
|
||||||
@Inject SignalServiceAccountManager accountManager;
|
|
||||||
|
|
||||||
public CreateSignedPreKeyJob(Context context) {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue("CreateSignedPreKeyJob")
|
|
||||||
.setMaxAttempts(25)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreateSignedPreKeyJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException {
|
|
||||||
if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
|
||||||
Log.w(TAG, "Signed prekey already registered...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TextSecurePreferences.isPushRegistered(context)) {
|
|
||||||
Log.w(TAG, "Not yet registered...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context);
|
|
||||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true);
|
|
||||||
|
|
||||||
accountManager.setSignedPreKey(signedPreKeyRecord);
|
|
||||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
if (exception instanceof PushNetworkException) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<CreateSignedPreKeyJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull CreateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new CreateSignedPreKeyJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,7 +19,6 @@ import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob;
|
|||||||
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -33,23 +32,11 @@ public final class JobManagerFactories {
|
|||||||
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
|
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
|
||||||
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
||||||
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
||||||
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
|
||||||
put(ClosedGroupUpdateMessageSendJob.KEY, new ClosedGroupUpdateMessageSendJob.Factory());
|
put(ClosedGroupUpdateMessageSendJob.KEY, new ClosedGroupUpdateMessageSendJob.Factory());
|
||||||
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
|
||||||
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
|
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
|
||||||
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());
|
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());
|
||||||
put(MmsReceiveJob.KEY, new MmsReceiveJob.Factory());
|
put(MmsReceiveJob.KEY, new MmsReceiveJob.Factory());
|
||||||
put(MmsSendJob.KEY, new MmsSendJob.Factory());
|
put(MmsSendJob.KEY, new MmsSendJob.Factory());
|
||||||
put(MultiDeviceBlockedUpdateJob.KEY, new MultiDeviceBlockedUpdateJob.Factory());
|
|
||||||
put(MultiDeviceConfigurationUpdateJob.KEY, new MultiDeviceConfigurationUpdateJob.Factory());
|
|
||||||
put(MultiDeviceContactUpdateJob.KEY, new MultiDeviceContactUpdateJob.Factory());
|
|
||||||
put(MultiDeviceGroupUpdateJob.KEY, new MultiDeviceGroupUpdateJob.Factory());
|
|
||||||
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
|
|
||||||
put(MultiDeviceProfileKeyUpdateJob.KEY, new MultiDeviceProfileKeyUpdateJob.Factory());
|
|
||||||
put(MultiDeviceReadUpdateJob.KEY, new MultiDeviceReadUpdateJob.Factory());
|
|
||||||
put(MultiDeviceStickerPackOperationJob.KEY, new MultiDeviceStickerPackOperationJob.Factory());
|
|
||||||
put(MultiDeviceStickerPackSyncJob.KEY, new MultiDeviceStickerPackSyncJob.Factory());
|
|
||||||
put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory());
|
|
||||||
put(NullMessageSendJob.KEY, new NullMessageSendJob.Factory());
|
put(NullMessageSendJob.KEY, new NullMessageSendJob.Factory());
|
||||||
put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory());
|
put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory());
|
||||||
put(PushDecryptJob.KEY, new PushDecryptJob.Factory());
|
put(PushDecryptJob.KEY, new PushDecryptJob.Factory());
|
||||||
@ -59,14 +46,12 @@ public final class JobManagerFactories {
|
|||||||
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
||||||
put(PushTextSendJob.KEY, new PushTextSendJob.Factory());
|
put(PushTextSendJob.KEY, new PushTextSendJob.Factory());
|
||||||
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
||||||
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
|
|
||||||
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
|
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
|
||||||
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
|
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
|
||||||
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application));
|
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application));
|
||||||
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory(application));
|
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory(application));
|
||||||
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
|
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
|
||||||
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
|
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
|
||||||
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
|
|
||||||
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
|
||||||
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory());
|
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory());
|
||||||
put(SessionRequestMessageSendJob.KEY, new SessionRequestMessageSendJob.Factory());
|
put(SessionRequestMessageSendJob.KEY, new SessionRequestMessageSendJob.Factory());
|
||||||
|
@ -1,108 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientReader;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceBlockedUpdateJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceBlockedUpdateJob";
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final String TAG = MultiDeviceBlockedUpdateJob.class.getSimpleName();
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
public MultiDeviceBlockedUpdateJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue("MultiDeviceBlockedUpdateJob")
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiDeviceBlockedUpdateJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun()
|
|
||||||
throws IOException, UntrustedIdentityException
|
|
||||||
{
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device, aborting...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);
|
|
||||||
|
|
||||||
try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
|
|
||||||
List<String> blockedIndividuals = new LinkedList<>();
|
|
||||||
List<byte[]> blockedGroups = new LinkedList<>();
|
|
||||||
|
|
||||||
Recipient recipient;
|
|
||||||
|
|
||||||
while ((recipient = reader.getNext()) != null) {
|
|
||||||
if (recipient.isGroupRecipient()) {
|
|
||||||
blockedGroups.add(GroupUtil.getDecodedId(recipient.getAddress().toGroupString()));
|
|
||||||
} else {
|
|
||||||
blockedIndividuals.add(recipient.getAddress().serialize());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
|
|
||||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
if (exception instanceof PushNetworkException) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MultiDeviceBlockedUpdateJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceBlockedUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MultiDeviceBlockedUpdateJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.ConfigurationMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceConfigurationUpdateJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceConfigurationUpdateJob";
|
|
||||||
|
|
||||||
private static final String TAG = MultiDeviceConfigurationUpdateJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_READ_RECEIPTS_ENABLED = "read_receipts_enabled";
|
|
||||||
private static final String KEY_TYPING_INDICATORS_ENABLED = "typing_indicators_enabled";
|
|
||||||
private static final String KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED = "unidentified_delivery_indicators_enabled";
|
|
||||||
private static final String KEY_LINK_PREVIEWS_ENABLED = "link_previews_enabled";
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
private boolean readReceiptsEnabled;
|
|
||||||
private boolean typingIndicatorsEnabled;
|
|
||||||
private boolean unidentifiedDeliveryIndicatorsEnabled;
|
|
||||||
private boolean linkPreviewsEnabled;
|
|
||||||
|
|
||||||
public MultiDeviceConfigurationUpdateJob(boolean readReceiptsEnabled,
|
|
||||||
boolean typingIndicatorsEnabled,
|
|
||||||
boolean unidentifiedDeliveryIndicatorsEnabled,
|
|
||||||
boolean linkPreviewsEnabled)
|
|
||||||
{
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("__MULTI_DEVICE_CONFIGURATION_UPDATE_JOB__")
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setMaxAttempts(10)
|
|
||||||
.build(),
|
|
||||||
readReceiptsEnabled,
|
|
||||||
typingIndicatorsEnabled,
|
|
||||||
unidentifiedDeliveryIndicatorsEnabled,
|
|
||||||
linkPreviewsEnabled);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiDeviceConfigurationUpdateJob(@NonNull Job.Parameters parameters,
|
|
||||||
boolean readReceiptsEnabled,
|
|
||||||
boolean typingIndicatorsEnabled,
|
|
||||||
boolean unidentifiedDeliveryIndicatorsEnabled,
|
|
||||||
boolean linkPreviewsEnabled)
|
|
||||||
{
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.readReceiptsEnabled = readReceiptsEnabled;
|
|
||||||
this.typingIndicatorsEnabled = typingIndicatorsEnabled;
|
|
||||||
this.unidentifiedDeliveryIndicatorsEnabled = unidentifiedDeliveryIndicatorsEnabled;
|
|
||||||
this.linkPreviewsEnabled = linkPreviewsEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return new Data.Builder().putBoolean(KEY_READ_RECEIPTS_ENABLED, readReceiptsEnabled)
|
|
||||||
.putBoolean(KEY_TYPING_INDICATORS_ENABLED, typingIndicatorsEnabled)
|
|
||||||
.putBoolean(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, unidentifiedDeliveryIndicatorsEnabled)
|
|
||||||
.putBoolean(KEY_LINK_PREVIEWS_ENABLED, linkPreviewsEnabled)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException, UntrustedIdentityException {
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device, aborting...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled),
|
|
||||||
Optional.of(unidentifiedDeliveryIndicatorsEnabled),
|
|
||||||
Optional.of(typingIndicatorsEnabled),
|
|
||||||
Optional.of(linkPreviewsEnabled))),
|
|
||||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception e) {
|
|
||||||
return e instanceof PushNetworkException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
Log.w(TAG, "**** Failed to synchronize read receipts state!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MultiDeviceConfigurationUpdateJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceConfigurationUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MultiDeviceConfigurationUpdateJob(parameters,
|
|
||||||
data.getBooleanOrDefault(KEY_READ_RECEIPTS_ENABLED, false),
|
|
||||||
data.getBooleanOrDefault(KEY_TYPING_INDICATORS_ENABLED, false),
|
|
||||||
data.getBooleanOrDefault(KEY_UNIDENTIFIED_DELIVERY_INDICATORS_ENABLED, false),
|
|
||||||
data.getBooleanOrDefault(KEY_LINK_PREVIEWS_ENABLED, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,352 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.provider.ContactsContract;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.crypto.UnidentifiedAccessPair;
|
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.ContactsMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceContact;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceContactsOutputStream;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage;
|
|
||||||
import org.session.libsignal.service.api.util.InvalidNumberException;
|
|
||||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceContactUpdateJob";
|
|
||||||
|
|
||||||
private static final String TAG = MultiDeviceContactUpdateJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6);
|
|
||||||
|
|
||||||
private static final String KEY_ADDRESS = "address";
|
|
||||||
private static final String KEY_FORCE_SYNC = "force_sync";
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
private @Nullable String address;
|
|
||||||
|
|
||||||
private boolean forceSync;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a full contact sync job that syncs to all linked devices.
|
|
||||||
*/
|
|
||||||
public MultiDeviceContactUpdateJob(@NonNull Context context) {
|
|
||||||
this(context, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) {
|
|
||||||
this(context, null, forceSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a single contact sync job that syncs `address` to all linked devices.
|
|
||||||
*/
|
|
||||||
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) {
|
|
||||||
this(context, address, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue("MultiDeviceContactUpdateJob")
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(1)
|
|
||||||
.build(),
|
|
||||||
address,
|
|
||||||
forceSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address address, boolean forceSync) {
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.forceSync = forceSync;
|
|
||||||
|
|
||||||
if (address != null) this.address = address.serialize();
|
|
||||||
else this.address = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return new Data.Builder().putString(KEY_ADDRESS, address)
|
|
||||||
.putBoolean(KEY_FORCE_SYNC, forceSync)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun()
|
|
||||||
throws IOException, UntrustedIdentityException, NetworkException
|
|
||||||
{
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device, aborting...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address == null) generateFullContactUpdate();
|
|
||||||
else if (SyncMessagesProtocol.shouldSyncContact(context, address)) generateSingleContactUpdate(Address.fromSerialized(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateSingleContactUpdate(@NonNull Address address)
|
|
||||||
throws IOException, UntrustedIdentityException, NetworkException
|
|
||||||
{
|
|
||||||
// Loki - Only sync regular contacts
|
|
||||||
if (!PublicKeyValidation.isValid(address.serialize())) { return; }
|
|
||||||
|
|
||||||
File contactDataFile = createTempFile("multidevice-contact-update");
|
|
||||||
|
|
||||||
try {
|
|
||||||
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
|
|
||||||
Recipient recipient = Recipient.from(context, address, false);
|
|
||||||
Optional<IdentityDatabase.IdentityRecord> identityRecord = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
|
|
||||||
Optional<VerifiedMessage> verifiedMessage = getVerifiedMessage(recipient, identityRecord);
|
|
||||||
|
|
||||||
if (SyncMessagesProtocol.shouldSyncContact(context, address.serialize())) {
|
|
||||||
out.write(new DeviceContact(address.toPhoneString(),
|
|
||||||
Optional.fromNullable(recipient.getName()),
|
|
||||||
getAvatar(recipient.getContactUri()),
|
|
||||||
Optional.fromNullable(recipient.getColor().serialize()),
|
|
||||||
verifiedMessage,
|
|
||||||
Optional.fromNullable(recipient.getProfileKey()),
|
|
||||||
recipient.isBlocked(),
|
|
||||||
recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent()));
|
|
||||||
}
|
|
||||||
|
|
||||||
out.close();
|
|
||||||
sendUpdate(messageSender, contactDataFile, false);
|
|
||||||
|
|
||||||
} catch(InvalidNumberException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
} finally {
|
|
||||||
if (contactDataFile != null) contactDataFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateFullContactUpdate()
|
|
||||||
throws IOException, UntrustedIdentityException, NetworkException
|
|
||||||
{
|
|
||||||
boolean isAppVisible = ApplicationContext.getInstance(context).isAppVisible();
|
|
||||||
long timeSinceLastSync = System.currentTimeMillis() - TextSecurePreferences.getLastFullContactSyncTime(context);
|
|
||||||
|
|
||||||
Log.d(TAG, "Requesting a full contact sync. forced = " + forceSync + ", appVisible = " + isAppVisible + ", timeSinceLastSync = " + timeSinceLastSync + " ms");
|
|
||||||
|
|
||||||
if (!forceSync && !isAppVisible && timeSinceLastSync < FULL_SYNC_TIME) {
|
|
||||||
Log.i(TAG, "App is backgrounded and the last contact sync was too soon (" + timeSinceLastSync + " ms ago). Marking that we need a sync. Skipping multi-device contact update...");
|
|
||||||
TextSecurePreferences.setNeedsFullContactSync(context, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextSecurePreferences.setLastFullContactSyncTime(context, System.currentTimeMillis());
|
|
||||||
TextSecurePreferences.setNeedsFullContactSync(context, false);
|
|
||||||
|
|
||||||
File contactDataFile = createTempFile("multidevice-contact-update");
|
|
||||||
|
|
||||||
try {
|
|
||||||
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
|
|
||||||
List<ContactData> contacts = SyncMessagesProtocol.getContactsToSync(context);
|
|
||||||
|
|
||||||
for (ContactData contactData : contacts) {
|
|
||||||
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id));
|
|
||||||
Address address = Address.fromExternal(context, contactData.numbers.get(0).number);
|
|
||||||
Recipient recipient = Recipient.from(context, address, false);
|
|
||||||
Optional<IdentityDatabase.IdentityRecord> identity = DatabaseFactory.getIdentityDatabase(context).getIdentity(address);
|
|
||||||
Optional<VerifiedMessage> verified = getVerifiedMessage(recipient, identity);
|
|
||||||
Optional<String> name = Optional.fromNullable(contactData.name);
|
|
||||||
Optional<String> color = Optional.of(recipient.getColor().serialize());
|
|
||||||
Optional<byte[]> profileKey = Optional.fromNullable(recipient.getProfileKey());
|
|
||||||
boolean blocked = recipient.isBlocked();
|
|
||||||
Optional<Integer> expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
|
|
||||||
|
|
||||||
out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ProfileKeyUtil.hasProfileKey(context)) {
|
|
||||||
Recipient self = Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), false);
|
|
||||||
out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context),
|
|
||||||
Optional.absent(), Optional.absent(),
|
|
||||||
Optional.of(self.getColor().serialize()), Optional.absent(),
|
|
||||||
Optional.of(ProfileKeyUtil.getProfileKey(context)),
|
|
||||||
false, self.getExpireMessages() > 0 ? Optional.of(self.getExpireMessages()) : Optional.absent()));
|
|
||||||
}
|
|
||||||
|
|
||||||
out.close();
|
|
||||||
sendUpdate(messageSender, contactDataFile, true);
|
|
||||||
} catch(InvalidNumberException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
} finally {
|
|
||||||
if (contactDataFile != null) contactDataFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
// Loki - Disabled since we have our own retrying
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile, boolean complete)
|
|
||||||
throws IOException, UntrustedIdentityException, NetworkException
|
|
||||||
{
|
|
||||||
if (contactsFile.length() > 0) {
|
|
||||||
FileInputStream contactsFileStream = new FileInputStream(contactsFile);
|
|
||||||
SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(contactsFileStream)
|
|
||||||
.withContentType("application/octet-stream")
|
|
||||||
.withLength(contactsFile.length())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Optional<UnidentifiedAccessPair> unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)) : Optional.absent();
|
|
||||||
|
|
||||||
try {
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
throw new NetworkException(ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<SignalServiceAttachmentStream> getAvatar(@Nullable Uri uri) throws IOException {
|
|
||||||
return Optional.absent();
|
|
||||||
|
|
||||||
/* Loki - Disabled until we support custom profile pictures. This will then need to be reworked.
|
|
||||||
if (uri == null) {
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri displayPhotoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
|
|
||||||
|
|
||||||
try {
|
|
||||||
AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(displayPhotoUri, "r");
|
|
||||||
|
|
||||||
if (fd == null) {
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.of(SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(fd.createInputStream())
|
|
||||||
.withContentType("image/*")
|
|
||||||
.withLength(fd.getLength())
|
|
||||||
.build());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.i(TAG, "Could not find avatar for URI: " + displayPhotoUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri photoUri = Uri.withAppendedPath(uri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
|
|
||||||
|
|
||||||
if (photoUri == null) {
|
|
||||||
return Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
Cursor cursor = context.getContentResolver().query(photoUri,
|
|
||||||
new String[] {
|
|
||||||
ContactsContract.CommonDataKinds.Photo.PHOTO,
|
|
||||||
ContactsContract.CommonDataKinds.Phone.MIMETYPE
|
|
||||||
}, null, null, null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
|
||||||
byte[] data = cursor.getBlob(0);
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
return Optional.of(SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(new ByteArrayInputStream(data))
|
|
||||||
.withContentType("image/*")
|
|
||||||
.withLength(data.length)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.absent();
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<VerifiedMessage> getVerifiedMessage(Recipient recipient, Optional<IdentityDatabase.IdentityRecord> identity) throws InvalidNumberException {
|
|
||||||
if (!identity.isPresent()) return Optional.absent();
|
|
||||||
|
|
||||||
String destination = recipient.getAddress().toPhoneString();
|
|
||||||
IdentityKey identityKey = identity.get().getIdentityKey();
|
|
||||||
|
|
||||||
VerifiedMessage.VerifiedState state;
|
|
||||||
|
|
||||||
switch (identity.get().getVerifiedStatus()) {
|
|
||||||
case VERIFIED: state = VerifiedMessage.VerifiedState.VERIFIED; break;
|
|
||||||
case UNVERIFIED: state = VerifiedMessage.VerifiedState.UNVERIFIED; break;
|
|
||||||
case DEFAULT: state = VerifiedMessage.VerifiedState.DEFAULT; break;
|
|
||||||
default: throw new AssertionError("Unknown state: " + identity.get().getVerifiedStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.of(new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createTempFile(String prefix) throws IOException {
|
|
||||||
File file = File.createTempFile(prefix, "tmp", context.getCacheDir());
|
|
||||||
file.deleteOnExit();
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NetworkException extends Exception {
|
|
||||||
|
|
||||||
public NetworkException(Exception ioe) {
|
|
||||||
super(ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MultiDeviceContactUpdateJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceContactUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
String serialized = data.getString(KEY_ADDRESS);
|
|
||||||
Address address = serialized != null ? Address.fromSerialized(serialized) : null;
|
|
||||||
|
|
||||||
return new MultiDeviceContactUpdateJob(parameters, address, data.getBoolean(KEY_FORCE_SYNC));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,174 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceGroup;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceGroupsOutputStream;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceGroupUpdateJob";
|
|
||||||
|
|
||||||
private static final String TAG = MultiDeviceGroupUpdateJob.class.getSimpleName();
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
public MultiDeviceGroupUpdateJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue("MultiDeviceGroupUpdateJob")
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiDeviceGroupUpdateJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws Exception {
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device, aborting...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
File contactDataFile = createTempFile("multidevice-contact-update");
|
|
||||||
GroupDatabase.Reader reader = null;
|
|
||||||
|
|
||||||
GroupDatabase.GroupRecord record;
|
|
||||||
|
|
||||||
try {
|
|
||||||
DeviceGroupsOutputStream out = new DeviceGroupsOutputStream(new FileOutputStream(contactDataFile));
|
|
||||||
|
|
||||||
reader = DatabaseFactory.getGroupDatabase(context).getGroups();
|
|
||||||
|
|
||||||
while ((record = reader.getNext()) != null) {
|
|
||||||
if (record.isClosedGroup()) {
|
|
||||||
List<String> members = new LinkedList<>();
|
|
||||||
List<String> admins = new LinkedList<>();
|
|
||||||
|
|
||||||
for (Address member : record.getMembers()) {
|
|
||||||
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);
|
|
||||||
Optional<Integer> expirationTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
|
|
||||||
|
|
||||||
out.write(new DeviceGroup(record.getId(), Optional.fromNullable(record.getTitle()),
|
|
||||||
members, admins, getAvatar(record.getAvatar()),
|
|
||||||
record.isActive(), expirationTimer,
|
|
||||||
Optional.of(recipient.getColor().serialize()),
|
|
||||||
recipient.isBlocked()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
if (contactDataFile.exists() && contactDataFile.length() > 0) {
|
|
||||||
sendUpdate(messageSender, contactDataFile);
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "No groups present for sync message...");
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
if (contactDataFile != null) contactDataFile.delete();
|
|
||||||
if (reader != null) reader.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
// Loki - Disabled since we have our own retrying
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile)
|
|
||||||
throws IOException, UntrustedIdentityException
|
|
||||||
{
|
|
||||||
FileInputStream contactsFileStream = new FileInputStream(contactsFile);
|
|
||||||
SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(contactsFileStream)
|
|
||||||
.withContentType("application/octet-stream")
|
|
||||||
.withLength(contactsFile.length())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream),
|
|
||||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Optional<SignalServiceAttachmentStream> getAvatar(@Nullable byte[] avatar) {
|
|
||||||
if (avatar == null) return Optional.absent();
|
|
||||||
|
|
||||||
return Optional.of(SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(new ByteArrayInputStream(avatar))
|
|
||||||
.withContentType("image/*")
|
|
||||||
.withLength(avatar.length)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createTempFile(String prefix) throws IOException {
|
|
||||||
File file = File.createTempFile(prefix, "tmp", context.getCacheDir());
|
|
||||||
file.deleteOnExit();
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MultiDeviceGroupUpdateJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceGroupUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MultiDeviceGroupUpdateJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.ContactsMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceContact;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceContactsOutputStream;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceProfileKeyUpdateJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static String KEY = "MultiDeviceProfileKeyUpdateJob";
|
|
||||||
|
|
||||||
private static final String TAG = MultiDeviceProfileKeyUpdateJob.class.getSimpleName();
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
public MultiDeviceProfileKeyUpdateJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue("MultiDeviceProfileKeyUpdateJob")
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiDeviceProfileKeyUpdateJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException, UntrustedIdentityException {
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<byte[]> profileKey = Optional.of(ProfileKeyUtil.getProfileKey(context));
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
DeviceContactsOutputStream out = new DeviceContactsOutputStream(baos);
|
|
||||||
|
|
||||||
out.write(new DeviceContact(TextSecurePreferences.getLocalNumber(context),
|
|
||||||
Optional.absent(),
|
|
||||||
Optional.absent(),
|
|
||||||
Optional.absent(),
|
|
||||||
Optional.absent(),
|
|
||||||
profileKey, false, Optional.absent()));
|
|
||||||
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(new ByteArrayInputStream(baos.toByteArray()))
|
|
||||||
.withContentType("application/octet-stream")
|
|
||||||
.withLength(baos.toByteArray().length)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));
|
|
||||||
|
|
||||||
messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
if (exception instanceof PushNetworkException) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
Log.w(TAG, "Profile key sync failed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MultiDeviceProfileKeyUpdateJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceProfileKeyUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MultiDeviceProfileKeyUpdateJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.ReadMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceReadUpdateJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceReadUpdateJob";
|
|
||||||
|
|
||||||
private static final String TAG = MultiDeviceReadUpdateJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_MESSAGE_IDS = "message_ids";
|
|
||||||
|
|
||||||
private List<SerializableSyncMessageId> messageIds;
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
public MultiDeviceReadUpdateJob(List<SyncMessageId> messageIds) {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.build(),
|
|
||||||
messageIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiDeviceReadUpdateJob(@NonNull Job.Parameters parameters, @NonNull List<SyncMessageId> messageIds) {
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.messageIds = new LinkedList<>();
|
|
||||||
|
|
||||||
for (SyncMessageId messageId : messageIds) {
|
|
||||||
this.messageIds.add(new SerializableSyncMessageId(messageId.getAddress().toPhoneString(), messageId.getTimetamp()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
String[] ids = new String[messageIds.size()];
|
|
||||||
|
|
||||||
for (int i = 0; i < ids.length; i++) {
|
|
||||||
try {
|
|
||||||
ids[i] = JsonUtils.toJson(messageIds.get(i));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Data.Builder().putStringArray(KEY_MESSAGE_IDS, ids).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException, UntrustedIdentityException {
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ReadMessage> readMessages = new LinkedList<>();
|
|
||||||
|
|
||||||
for (SerializableSyncMessageId messageId : messageIds) {
|
|
||||||
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
|
|
||||||
}
|
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return exception instanceof PushNetworkException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SerializableSyncMessageId implements Serializable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private final String sender;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private final long timestamp;
|
|
||||||
|
|
||||||
private SerializableSyncMessageId(@JsonProperty("sender") String sender, @JsonProperty("timestamp") long timestamp) {
|
|
||||||
this.sender = sender;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MultiDeviceReadUpdateJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceReadUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
List<SyncMessageId> ids = Stream.of(data.getStringArray(KEY_MESSAGE_IDS))
|
|
||||||
.map(id -> {
|
|
||||||
try {
|
|
||||||
return JsonUtils.fromJson(id, SerializableSyncMessageId.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(id -> new SyncMessageId(Address.fromSerialized(id.sender), id.timestamp))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
return new MultiDeviceReadUpdateJob(parameters, ids);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceStickerPackOperationJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(MultiDeviceStickerPackOperationJob.class);
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceStickerPackOperationJob";
|
|
||||||
|
|
||||||
private static final String KEY_PACK_ID = "pack_id";
|
|
||||||
private static final String KEY_PACK_KEY = "pack_key";
|
|
||||||
private static final String KEY_TYPE = "type";
|
|
||||||
|
|
||||||
private final String packId;
|
|
||||||
private final String packKey;
|
|
||||||
private final Type type;
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
public MultiDeviceStickerPackOperationJob(@NonNull String packId,
|
|
||||||
@NonNull String packKey,
|
|
||||||
@NonNull Type type)
|
|
||||||
{
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("MultiDeviceStickerPackOperationJob")
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.build(),
|
|
||||||
packId,
|
|
||||||
packKey,
|
|
||||||
type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiDeviceStickerPackOperationJob(@NonNull Parameters parameters,
|
|
||||||
@NonNull String packId,
|
|
||||||
@NonNull String packKey,
|
|
||||||
@NonNull Type type)
|
|
||||||
{
|
|
||||||
super(parameters);
|
|
||||||
this.packId = packId;
|
|
||||||
this.packKey = packKey;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return new Data.Builder().putString(KEY_PACK_ID, packId)
|
|
||||||
.putString(KEY_PACK_KEY, packKey)
|
|
||||||
.putString(KEY_TYPE, type.name())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onRun() throws Exception {
|
|
||||||
/*
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device, aborting...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] packIdBytes = Hex.fromStringCondensed(packId);
|
|
||||||
byte[] packKeyBytes = Hex.fromStringCondensed(packKey);
|
|
||||||
|
|
||||||
StickerPackOperationMessage.Type remoteType;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case INSTALL: remoteType = StickerPackOperationMessage.Type.INSTALL; break;
|
|
||||||
case REMOVE: remoteType = StickerPackOperationMessage.Type.REMOVE; break;
|
|
||||||
default: throw new AssertionError("No matching type?");
|
|
||||||
}
|
|
||||||
|
|
||||||
StickerPackOperationMessage stickerPackOperation = new StickerPackOperationMessage(packIdBytes, packKeyBytes, remoteType);
|
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)),
|
|
||||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean onShouldRetry(@NonNull Exception e) {
|
|
||||||
return e instanceof PushNetworkException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
Log.w(TAG, "Failed to sync sticker pack operation!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// NEVER rename these -- they're persisted by name
|
|
||||||
public enum Type {
|
|
||||||
INSTALL, REMOVE
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<MultiDeviceStickerPackOperationJob> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceStickerPackOperationJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MultiDeviceStickerPackOperationJob(parameters,
|
|
||||||
data.getString(KEY_PACK_ID),
|
|
||||||
data.getString(KEY_PACK_KEY),
|
|
||||||
Type.valueOf(data.getString(KEY_TYPE)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells a linked desktop about all installed sticker packs.
|
|
||||||
*/
|
|
||||||
public class MultiDeviceStickerPackSyncJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(MultiDeviceStickerPackSyncJob.class);
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceStickerPackSyncJob";
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
public MultiDeviceStickerPackSyncJob() {
|
|
||||||
this(new Parameters.Builder()
|
|
||||||
.setQueue("MultiDeviceStickerPackSyncJob")
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiDeviceStickerPackSyncJob(@NonNull Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onRun() throws Exception {
|
|
||||||
return;
|
|
||||||
/*
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device, aborting...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<StickerPackOperationMessage> operations = new LinkedList<>();
|
|
||||||
|
|
||||||
try (StickerPackRecordReader reader = new StickerPackRecordReader(DatabaseFactory.getStickerDatabase(context).getInstalledStickerPacks())) {
|
|
||||||
StickerPackRecord pack;
|
|
||||||
while ((pack = reader.getNext()) != null) {
|
|
||||||
byte[] packIdBytes = Hex.fromStringCondensed(pack.getPackId());
|
|
||||||
byte[] packKeyBytes = Hex.fromStringCondensed(pack.getPackKey());
|
|
||||||
|
|
||||||
operations.add(new StickerPackOperationMessage(packIdBytes, packKeyBytes, StickerPackOperationMessage.Type.INSTALL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(operations),
|
|
||||||
UnidentifiedAccessUtil.getAccessForSync(context));
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean onShouldRetry(@NonNull Exception e) {
|
|
||||||
return e instanceof PushNetworkException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
Log.w(TAG, "Failed to sync sticker pack operation!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements Job.Factory<MultiDeviceStickerPackSyncJob> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull
|
|
||||||
MultiDeviceStickerPackSyncJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new MultiDeviceStickerPackSyncJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
|
||||||
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.util.Base64;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender;
|
|
||||||
import org.session.libsignal.service.api.crypto.UntrustedIdentityException;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MultiDeviceVerifiedUpdateJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "MultiDeviceVerifiedUpdateJob";
|
|
||||||
|
|
||||||
private static final String TAG = MultiDeviceVerifiedUpdateJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final String KEY_DESTINATION = "destination";
|
|
||||||
private static final String KEY_IDENTITY_KEY = "identity_key";
|
|
||||||
private static final String KEY_VERIFIED_STATUS = "verified_status";
|
|
||||||
private static final String KEY_TIMESTAMP = "timestamp";
|
|
||||||
|
|
||||||
@Inject SignalServiceMessageSender messageSender;
|
|
||||||
|
|
||||||
private String destination;
|
|
||||||
private byte[] identityKey;
|
|
||||||
private VerifiedStatus verifiedStatus;
|
|
||||||
private long timestamp;
|
|
||||||
|
|
||||||
public MultiDeviceVerifiedUpdateJob(Address destination, IdentityKey identityKey, VerifiedStatus verifiedStatus) {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue("__MULTI_DEVICE_VERIFIED_UPDATE__")
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.build(),
|
|
||||||
destination,
|
|
||||||
identityKey.serialize(),
|
|
||||||
verifiedStatus,
|
|
||||||
System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiDeviceVerifiedUpdateJob(@NonNull Job.Parameters parameters,
|
|
||||||
@NonNull Address destination,
|
|
||||||
@NonNull byte[] identityKey,
|
|
||||||
@NonNull VerifiedStatus verifiedStatus,
|
|
||||||
long timestamp)
|
|
||||||
{
|
|
||||||
super(parameters);
|
|
||||||
|
|
||||||
this.destination = destination.serialize();
|
|
||||||
this.identityKey = identityKey;
|
|
||||||
this.verifiedStatus = verifiedStatus;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return new Data.Builder().putString(KEY_DESTINATION, destination)
|
|
||||||
.putString(KEY_IDENTITY_KEY, Base64.encodeBytes(identityKey))
|
|
||||||
.putInt(KEY_VERIFIED_STATUS, verifiedStatus.toInt())
|
|
||||||
.putLong(KEY_TIMESTAMP, timestamp)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException, UntrustedIdentityException {
|
|
||||||
/*
|
|
||||||
try {
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.i(TAG, "Not multi device...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destination == null) {
|
|
||||||
Log.w(TAG, "No destination...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Address canonicalDestination = Address.fromSerialized(destination);
|
|
||||||
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
|
|
||||||
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
|
|
||||||
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
|
|
||||||
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false)));
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private VerifiedMessage.VerifiedState getVerifiedState(VerifiedStatus status) {
|
|
||||||
VerifiedMessage.VerifiedState verifiedState;
|
|
||||||
|
|
||||||
switch (status) {
|
|
||||||
case DEFAULT: verifiedState = VerifiedMessage.VerifiedState.DEFAULT; break;
|
|
||||||
case VERIFIED: verifiedState = VerifiedMessage.VerifiedState.VERIFIED; break;
|
|
||||||
case UNVERIFIED: verifiedState = VerifiedMessage.VerifiedState.UNVERIFIED; break;
|
|
||||||
default: throw new AssertionError("Unknown status: " + verifiedStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
return verifiedState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return exception instanceof PushNetworkException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<MultiDeviceVerifiedUpdateJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull MultiDeviceVerifiedUpdateJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
try {
|
|
||||||
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
|
|
||||||
VerifiedStatus verifiedStatus = VerifiedStatus.forState(data.getInt(KEY_VERIFIED_STATUS));
|
|
||||||
long timestamp = data.getLong(KEY_TIMESTAMP);
|
|
||||||
byte[] identityKey = Base64.decode(data.getString(KEY_IDENTITY_KEY));
|
|
||||||
|
|
||||||
return new MultiDeviceVerifiedUpdateJob(parameters, destination, identityKey, verifiedStatus, timestamp);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -70,8 +70,6 @@ import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
|
|||||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceProtocol;
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol;
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
|
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
|
||||||
import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities;
|
import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities;
|
||||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||||
@ -265,13 +263,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
|
||||||
|
|
||||||
if (content.getDeviceLink().isPresent()) {
|
if (content.getDeviceLink().isPresent()) {
|
||||||
MultiDeviceProtocol.handleDeviceLinkMessageIfNeeded(context, content.getDeviceLink().get(), content);
|
throw new UnsupportedOperationException("Device link operations are not supported!");
|
||||||
} else if (content.getDataMessage().isPresent()) {
|
} else if (content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
||||||
|
|
||||||
if (message.isDeviceUnlinkingRequest()) {
|
if (message.isDeviceUnlinkingRequest()) {
|
||||||
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
|
throw new UnsupportedOperationException("Device link operations are not supported!");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (message.getClosedGroupUpdate().isPresent()) {
|
if (message.getClosedGroupUpdate().isPresent()) {
|
||||||
@ -303,20 +301,22 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (content.getSyncMessage().isPresent()) {
|
} else if (content.getSyncMessage().isPresent()) {
|
||||||
TextSecurePreferences.setMultiDevice(context, true);
|
throw new UnsupportedOperationException("Device link operations are not supported!");
|
||||||
|
|
||||||
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
|
// TextSecurePreferences.setMultiDevice(context, true);
|
||||||
|
//
|
||||||
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
|
// SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
|
||||||
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
|
//
|
||||||
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
|
// if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
|
||||||
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
|
// else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
|
||||||
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
|
// else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
|
||||||
else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get());
|
// else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
|
||||||
else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get());
|
// else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
|
||||||
else if (syncMessage.getOpenGroups().isPresent()) SyncMessagesProtocol.handleOpenGroupSyncMessage(context, content, syncMessage.getOpenGroups().get());
|
// else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get());
|
||||||
else if (syncMessage.getBlockedList().isPresent()) SyncMessagesProtocol.handleBlockedContactsSyncMessage(context, content, syncMessage.getBlockedList().get());
|
// else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get());
|
||||||
else Log.w(TAG, "Contains no known sync types...");
|
// else if (syncMessage.getOpenGroups().isPresent()) SyncMessagesProtocol.handleOpenGroupSyncMessage(context, content, syncMessage.getOpenGroups().get());
|
||||||
|
// else if (syncMessage.getBlockedList().isPresent()) SyncMessagesProtocol.handleBlockedContactsSyncMessage(context, content, syncMessage.getBlockedList().get());
|
||||||
|
// else Log.w(TAG, "Contains no known sync types...");
|
||||||
} else if (content.getReceiptMessage().isPresent()) {
|
} else if (content.getReceiptMessage().isPresent()) {
|
||||||
SignalServiceReceiptMessage message = content.getReceiptMessage().get();
|
SignalServiceReceiptMessage message = content.getReceiptMessage().get();
|
||||||
|
|
||||||
@ -330,9 +330,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
|
|
||||||
resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false));
|
resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false));
|
||||||
|
|
||||||
if (envelope.isPreKeySignalMessage()) {
|
// if (envelope.isPreKeySignalMessage()) {
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
// ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
||||||
}
|
// }
|
||||||
} catch (ProtocolInvalidVersionException e) {
|
} catch (ProtocolInvalidVersionException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
||||||
@ -560,68 +560,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSynchronizeRequestMessage(@NonNull RequestMessage message)
|
|
||||||
{
|
|
||||||
if (message.isContactsRequest()) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceContactUpdateJob(context, true));
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new RefreshUnidentifiedDeliveryAbilityJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.isGroupsRequest()) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceGroupUpdateJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.isBlockedListRequest()) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceBlockedUpdateJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.isConfigurationRequest()) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(context),
|
|
||||||
TextSecurePreferences.isTypingIndicatorsEnabled(context),
|
|
||||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context),
|
|
||||||
TextSecurePreferences.isLinkPreviewsEnabled(context)));
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceStickerPackSyncJob());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSynchronizeReadMessage(@NonNull List<ReadMessage> readMessages, long envelopeTimestamp)
|
|
||||||
{
|
|
||||||
for (ReadMessage readMessage : readMessages) {
|
|
||||||
List<Pair<Long, Long>> expiringText = DatabaseFactory.getSmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromSerialized(readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp);
|
|
||||||
List<Pair<Long, Long>> expiringMedia = DatabaseFactory.getMmsDatabase(context).setTimestampRead(new SyncMessageId(Address.fromSerialized(readMessage.getSender()), readMessage.getTimestamp()), envelopeTimestamp);
|
|
||||||
|
|
||||||
for (Pair<Long, Long> expiringMessage : expiringText) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getExpiringMessageManager()
|
|
||||||
.scheduleDeletion(expiringMessage.first, false, envelopeTimestamp, expiringMessage.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Pair<Long, Long> expiringMessage : expiringMedia) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getExpiringMessageManager()
|
|
||||||
.scheduleDeletion(expiringMessage.first, true, envelopeTimestamp, expiringMessage.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
messageNotifier.setLastDesktopActivityTimestamp(envelopeTimestamp);
|
|
||||||
messageNotifier.cancelDelayedNotifications();
|
|
||||||
messageNotifier.updateNotification(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleMediaMessage(@NonNull SignalServiceContent content,
|
public void handleMediaMessage(@NonNull SignalServiceContent content,
|
||||||
@NonNull SignalServiceDataMessage message,
|
@NonNull SignalServiceDataMessage message,
|
||||||
@NonNull Optional<Long> smsMessageId,
|
@NonNull Optional<Long> smsMessageId,
|
||||||
@ -1413,7 +1351,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
return sender.isBlocked();
|
return sender.isBlocked();
|
||||||
}
|
}
|
||||||
} else if (content.getSyncMessage().isPresent()) {
|
} else if (content.getSyncMessage().isPresent()) {
|
||||||
return SyncMessagesProtocol.shouldIgnoreSyncMessage(context, sender);
|
throw new UnsupportedOperationException("Device link operations are not supported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -71,13 +71,13 @@ public abstract class PushSendJob extends SendJob {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void onSend() throws Exception {
|
protected final void onSend() throws Exception {
|
||||||
if (TextSecurePreferences.getSignedPreKeyFailureCount(context) > 5) {
|
// if (TextSecurePreferences.getSignedPreKeyFailureCount(context) > 5) {
|
||||||
ApplicationContext.getInstance(context)
|
// ApplicationContext.getInstance(context)
|
||||||
.getJobManager()
|
// .getJobManager()
|
||||||
.add(new RotateSignedPreKeyJob());
|
// .add(new RotateSignedPreKeyJob());
|
||||||
|
//
|
||||||
throw new TextSecureExpiredException("Too many signed prekey rotation failures");
|
// throw new TextSecureExpiredException("Too many signed prekey rotation failures");
|
||||||
}
|
// }
|
||||||
|
|
||||||
onPushSend();
|
onPushSend();
|
||||||
}
|
}
|
||||||
|
@ -197,17 +197,17 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
|
|||||||
|
|
||||||
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
|
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
|
||||||
|
|
||||||
PreKeyBundle preKeyBundle = null;
|
// PreKeyBundle preKeyBundle = null;
|
||||||
if (message.isEndSession()) {
|
// if (message.isEndSession()) {
|
||||||
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
|
// preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
|
||||||
}
|
// }
|
||||||
|
|
||||||
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
|
||||||
.withTimestamp(message.getDateSent())
|
.withTimestamp(message.getDateSent())
|
||||||
.withBody(message.getBody())
|
.withBody(message.getBody())
|
||||||
.withExpiration((int)(message.getExpiresIn() / 1000))
|
.withExpiration((int)(message.getExpiresIn() / 1000))
|
||||||
.withProfileKey(profileKey.orNull())
|
.withProfileKey(profileKey.orNull())
|
||||||
.withPreKeyBundle(preKeyBundle)
|
// .withPreKeyBundle(preKeyBundle)
|
||||||
.asEndSessionMessage(message.isEndSession())
|
.asEndSessionMessage(message.isEndSession())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceAccountManager;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.NonSuccessfulResponseCodeException;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class RefreshPreKeysJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "RefreshPreKeysJob";
|
|
||||||
|
|
||||||
private static final String TAG = RefreshPreKeysJob.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final int PREKEY_MINIMUM = 10;
|
|
||||||
|
|
||||||
@Inject SignalServiceAccountManager accountManager;
|
|
||||||
|
|
||||||
public RefreshPreKeysJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.setQueue("RefreshPreKeysJob")
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setMaxAttempts(3)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private RefreshPreKeysJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException {
|
|
||||||
SessionManagementProtocol.refreshSignedPreKey(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loki - Original code
|
|
||||||
@Override
|
|
||||||
public void onRun() throws IOException {
|
|
||||||
if (!TextSecurePreferences.isPushRegistered(context)) return;
|
|
||||||
|
|
||||||
int availableKeys = accountManager.getPreKeysCount();
|
|
||||||
|
|
||||||
if (availableKeys >= PREKEY_MINIMUM && TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
|
||||||
Log.i(TAG, "Available keys sufficient: " + availableKeys);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PreKeyRecord> preKeyRecords = PreKeyUtil.generatePreKeyRecords(context);
|
|
||||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
|
|
||||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false);
|
|
||||||
|
|
||||||
Log.i(TAG, "Registering new prekeys...");
|
|
||||||
|
|
||||||
accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKeyRecord, preKeyRecords);
|
|
||||||
|
|
||||||
PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId());
|
|
||||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new CleanPreKeysJob());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
if (exception instanceof NonSuccessfulResponseCodeException) return false;
|
|
||||||
if (exception instanceof PushNetworkException) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<RefreshPreKeysJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull RefreshPreKeysJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new RefreshPreKeysJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.jobs;
|
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
import org.session.libsignal.libsignal.IdentityKeyPair;
|
|
||||||
import org.session.libsignal.libsignal.state.SignedPreKeyRecord;
|
|
||||||
import org.session.libsignal.service.api.SignalServiceAccountManager;
|
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class RotateSignedPreKeyJob extends BaseJob implements InjectableType {
|
|
||||||
|
|
||||||
public static final String KEY = "RotateSignedPreKeyJob";
|
|
||||||
|
|
||||||
private static final String TAG = RotateSignedPreKeyJob.class.getSimpleName();
|
|
||||||
|
|
||||||
@Inject SignalServiceAccountManager accountManager;
|
|
||||||
|
|
||||||
public RotateSignedPreKeyJob() {
|
|
||||||
this(new Job.Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setMaxAttempts(3)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private RotateSignedPreKeyJob(@NonNull Job.Parameters parameters) {
|
|
||||||
super(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Data serialize() {
|
|
||||||
return Data.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String getFactoryKey() {
|
|
||||||
return KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRun() throws Exception {
|
|
||||||
Log.i(TAG, "Rotating signed prekey...");
|
|
||||||
|
|
||||||
if (!IdentityKeyUtil.hasIdentityKey(context)) { return; }
|
|
||||||
|
|
||||||
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
|
|
||||||
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false);
|
|
||||||
|
|
||||||
// Loki - Don't upload the new signed pre key
|
|
||||||
// accountManager.setSignedPreKey(signedPreKeyRecord);
|
|
||||||
|
|
||||||
PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId());
|
|
||||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true);
|
|
||||||
TextSecurePreferences.setSignedPreKeyFailureCount(context, 0);
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new CleanPreKeysJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onShouldRetry(@NonNull Exception exception) {
|
|
||||||
return exception instanceof PushNetworkException;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCanceled() {
|
|
||||||
TextSecurePreferences.setSignedPreKeyFailureCount(context, TextSecurePreferences.getSignedPreKeyFailureCount(context) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Factory implements Job.Factory<RotateSignedPreKeyJob> {
|
|
||||||
@Override
|
|
||||||
public @NonNull RotateSignedPreKeyJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
|
||||||
return new RotateSignedPreKeyJob(parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,6 @@ import android.content.Intent
|
|||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.AsyncTask
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
@ -32,10 +31,7 @@ import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
|||||||
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
import org.thoughtcrime.securesms.conversation.ConversationActivity
|
||||||
import org.thoughtcrime.securesms.database.Address
|
import org.thoughtcrime.securesms.database.Address
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob
|
|
||||||
import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob
|
import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet
|
import org.thoughtcrime.securesms.loki.dialogs.ConversationOptionsBottomSheet
|
||||||
import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet
|
import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet
|
||||||
@ -329,7 +325,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
|||||||
.setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ ->
|
.setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ ->
|
||||||
Thread {
|
Thread {
|
||||||
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true)
|
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true)
|
||||||
ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob())
|
|
||||||
Util.runOnMain {
|
Util.runOnMain {
|
||||||
recyclerView.adapter!!.notifyDataSetChanged()
|
recyclerView.adapter!!.notifyDataSetChanged()
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
@ -346,7 +341,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
|||||||
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ ->
|
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ ->
|
||||||
Thread {
|
Thread {
|
||||||
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false)
|
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false)
|
||||||
ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob())
|
|
||||||
Util.runOnMain {
|
Util.runOnMain {
|
||||||
recyclerView.adapter!!.notifyDataSetChanged()
|
recyclerView.adapter!!.notifyDataSetChanged()
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
@ -3,14 +3,14 @@ package org.thoughtcrime.securesms.loki.activities
|
|||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentPagerAdapter
|
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentPagerAdapter
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.android.synthetic.main.activity_join_public_chat.*
|
import kotlinx.android.synthetic.main.activity_join_public_chat.*
|
||||||
import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
|
import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
|
||||||
@ -18,15 +18,11 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import nl.komponents.kovenant.ui.failUi
|
|
||||||
import nl.komponents.kovenant.ui.successUi
|
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
|
||||||
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
||||||
import java.lang.Exception
|
|
||||||
|
|
||||||
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
|
||||||
private val adapter = JoinPublicChatActivityAdapter(this)
|
private val adapter = JoinPublicChatActivityAdapter(this)
|
||||||
@ -83,7 +79,6 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
|
|||||||
}
|
}
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
SyncMessagesProtocol.syncAllOpenGroups(this@JoinPublicChatActivity)
|
|
||||||
withContext(Dispatchers.Main) { finish() }
|
withContext(Dispatchers.Main) { finish() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,130 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
|
||||||
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.content.Context
|
|
||||||
import net.sqlcipher.Cursor
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
|
||||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.get
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getBase64EncodedData
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getInt
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyException
|
|
||||||
import org.session.libsignal.libsignal.ecc.Curve
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyBundle
|
|
||||||
import org.session.libsignal.libsignal.util.KeyHelper
|
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
|
||||||
import org.session.libsignal.service.loki.database.LokiPreKeyBundleDatabaseProtocol
|
|
||||||
|
|
||||||
class LokiPreKeyBundleDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyBundleDatabaseProtocol {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val table = "loki_pre_key_bundle_database"
|
|
||||||
private val publicKey = "public_key"
|
|
||||||
private val preKeyID = "pre_key_id"
|
|
||||||
private val preKeyPublic = "pre_key_public"
|
|
||||||
private val signedPreKeyID = "signed_pre_key_id"
|
|
||||||
private val signedPreKeyPublic = "signed_pre_key_public"
|
|
||||||
private val signedPreKeySignature = "signed_pre_key_signature"
|
|
||||||
private val identityKey = "identity_key"
|
|
||||||
private val deviceID = "device_id"
|
|
||||||
private val registrationID = "registration_id"
|
|
||||||
@JvmStatic val createTableCommand = "CREATE TABLE $table (" + "$publicKey TEXT PRIMARY KEY," + "$preKeyID INTEGER," +
|
|
||||||
"$preKeyPublic TEXT NOT NULL," + "$signedPreKeyID INTEGER," + "$signedPreKeyPublic TEXT NOT NULL," +
|
|
||||||
"$signedPreKeySignature TEXT," + "$identityKey TEXT NOT NULL," + "$deviceID INTEGER," + "$registrationID INTEGER" + ");"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generatePreKeyBundle(publicKey: String): PreKeyBundle? {
|
|
||||||
var failureCount = 0
|
|
||||||
while (failureCount < 3) {
|
|
||||||
try {
|
|
||||||
val preKey = generatePreKeyBundle(publicKey, 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: $publicKey.")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generatePreKeyBundle(publicKey: String, forceClean: Boolean): PreKeyBundle? {
|
|
||||||
if (publicKey.isEmpty()) return null
|
|
||||||
var registrationID = TextSecurePreferences.getLocalRegistrationId(context)
|
|
||||||
if (registrationID == 0) {
|
|
||||||
registrationID = KeyHelper.generateRegistrationId(false)
|
|
||||||
TextSecurePreferences.setLocalRegistrationId(context, registrationID)
|
|
||||||
}
|
|
||||||
val deviceID = SignalServiceAddress.DEFAULT_DEVICE_ID
|
|
||||||
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getOrCreatePreKeyRecord(publicKey)
|
|
||||||
val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
|
|
||||||
if (!forceClean && TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
|
||||||
Log.d("Loki", "A signed pre key has already been registered.")
|
|
||||||
} else {
|
|
||||||
Log.d("Loki", "Registering a new signed pre key.")
|
|
||||||
PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true)
|
|
||||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true)
|
|
||||||
}
|
|
||||||
val activeSignedPreKey = PreKeyUtil.getActiveSignedPreKey(context) ?: return null
|
|
||||||
return PreKeyBundle(registrationID, deviceID, preKeyRecord.id, preKeyRecord.keyPair.publicKey, activeSignedPreKey.id, activeSignedPreKey.keyPair.publicKey, activeSignedPreKey.signature, identityKeyPair.publicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPreKeyBundle(publicKey: String): PreKeyBundle? {
|
|
||||||
val database = databaseHelper.readableDatabase
|
|
||||||
return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor ->
|
|
||||||
val registrationID = cursor.getInt(registrationID)
|
|
||||||
val deviceID = cursor.getInt(deviceID)
|
|
||||||
val preKeyID = cursor.getInt(preKeyID)
|
|
||||||
val preKey = Curve.decodePoint(cursor.getBase64EncodedData(preKeyPublic), 0)
|
|
||||||
val signedPreKeyID = cursor.getInt(signedPreKeyID)
|
|
||||||
val signedPreKey = Curve.decodePoint(cursor.getBase64EncodedData(signedPreKeyPublic), 0)
|
|
||||||
val signedPreKeySignature = cursor.getBase64EncodedData(signedPreKeySignature)
|
|
||||||
val identityKey = IdentityKey(cursor.getBase64EncodedData(identityKey), 0)
|
|
||||||
PreKeyBundle(registrationID, deviceID, preKeyID, preKey, signedPreKeyID, signedPreKey, signedPreKeySignature, identityKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setPreKeyBundle(publicKey: String, preKeyBundle: PreKeyBundle) {
|
|
||||||
val database = databaseHelper.writableDatabase
|
|
||||||
val values = ContentValues(9)
|
|
||||||
values.put(registrationID, preKeyBundle.registrationId)
|
|
||||||
values.put(deviceID, preKeyBundle.deviceId)
|
|
||||||
values.put(preKeyID, preKeyBundle.preKeyId)
|
|
||||||
values.put(preKeyPublic, Base64.encodeBytes(preKeyBundle.preKey.serialize()))
|
|
||||||
values.put(signedPreKeyID, preKeyBundle.signedPreKeyId)
|
|
||||||
values.put(signedPreKeyPublic, Base64.encodeBytes(preKeyBundle.signedPreKey.serialize()))
|
|
||||||
values.put(signedPreKeySignature, Base64.encodeBytes(preKeyBundle.signedPreKeySignature))
|
|
||||||
values.put(identityKey, Base64.encodeBytes(preKeyBundle.identityKey.serialize()))
|
|
||||||
values.put(Companion.publicKey, publicKey)
|
|
||||||
database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey ))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removePreKeyBundle(publicKey: String) {
|
|
||||||
val database = databaseHelper.writableDatabase
|
|
||||||
database.delete(table, "${Companion.publicKey} = ?", arrayOf( publicKey ))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasPreKeyBundle(publicKey: String): Boolean {
|
|
||||||
val database = databaseHelper.readableDatabase
|
|
||||||
var cursor: Cursor? = null
|
|
||||||
return try {
|
|
||||||
cursor = database.query(table, null, "${Companion.publicKey} = ?", arrayOf( publicKey ), null, null, null)
|
|
||||||
cursor != null && cursor.count > 0
|
|
||||||
} catch (e: Exception) {
|
|
||||||
false
|
|
||||||
} finally {
|
|
||||||
cursor?.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.database
|
|
||||||
|
|
||||||
import android.content.ContentValues
|
|
||||||
import android.content.Context
|
|
||||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
|
||||||
import org.thoughtcrime.securesms.database.Database
|
|
||||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.get
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.getInt
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.insertOrUpdate
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyRecord
|
|
||||||
import org.session.libsignal.service.loki.database.LokiPreKeyRecordDatabaseProtocol
|
|
||||||
|
|
||||||
class LokiPreKeyRecordDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiPreKeyRecordDatabaseProtocol {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val table = "loki_pre_key_record_database"
|
|
||||||
private val publicKey = "public_key"
|
|
||||||
private val preKeyID = "pre_key_id"
|
|
||||||
@JvmStatic val createTableCommand = "CREATE TABLE $table ($publicKey TEXT PRIMARY KEY, $preKeyID INTEGER);"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasPreKey(publicKey: String): Boolean {
|
|
||||||
val database = databaseHelper.readableDatabase
|
|
||||||
return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { it.count > 0 } ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPreKeyRecord(publicKey: String): PreKeyRecord? {
|
|
||||||
val database = databaseHelper.readableDatabase
|
|
||||||
return database.get(table, "${Companion.publicKey} = ?", arrayOf( publicKey )) { cursor ->
|
|
||||||
val preKeyID = cursor.getInt(preKeyID)
|
|
||||||
PreKeyUtil.loadPreKey(context, preKeyID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getOrCreatePreKeyRecord(publicKey: String): PreKeyRecord {
|
|
||||||
return getPreKeyRecord(publicKey) ?: generateAndStorePreKeyRecord(publicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun generateAndStorePreKeyRecord(publicKey: String): PreKeyRecord {
|
|
||||||
val records = PreKeyUtil.generatePreKeyRecords(context, 1)
|
|
||||||
PreKeyUtil.storePreKeyRecords(context, records)
|
|
||||||
val record = records.first()
|
|
||||||
val database = databaseHelper.writableDatabase
|
|
||||||
val values = ContentValues(2)
|
|
||||||
values.put(Companion.publicKey, publicKey)
|
|
||||||
values.put(preKeyID, record.id)
|
|
||||||
database.insertOrUpdate(table, values, "${Companion.publicKey} = ?", arrayOf( publicKey ))
|
|
||||||
return record
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.loki.protocol
|
|||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||||
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data
|
import org.thoughtcrime.securesms.jobmanager.Data
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
@ -124,7 +123,8 @@ class ClosedGroupUpdateMessageSendJob private constructor(parameters: Parameters
|
|||||||
val recipient = recipient(context, destination)
|
val recipient = recipient(context, destination)
|
||||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
|
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
|
||||||
val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.ClosedGroupUpdate)
|
val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.ClosedGroupUpdate)
|
||||||
val useFallbackEncryption = SignalProtocolStoreImpl(context).containsSession(SignalProtocolAddress(destination, 1))
|
// val useFallbackEncryption = SignalProtocolStoreImpl(context).containsSession(SignalProtocolAddress(destination, 1))
|
||||||
|
val useFallbackEncryption = true
|
||||||
try {
|
try {
|
||||||
// isClosedGroup can always be false as it's only used in the context of legacy closed groups
|
// isClosedGroup can always be false as it's only used in the context of legacy closed groups
|
||||||
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
|
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
|
||||||
|
@ -3,12 +3,9 @@ package org.thoughtcrime.securesms.loki.protocol
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
import org.thoughtcrime.securesms.logging.Log
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
|
||||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
|
||||||
import org.thoughtcrime.securesms.crypto.SecurityEvent
|
import org.thoughtcrime.securesms.crypto.SecurityEvent
|
||||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.recipient
|
import org.thoughtcrime.securesms.loki.utilities.recipient
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage
|
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage
|
||||||
@ -41,18 +38,18 @@ object SessionManagementProtocol {
|
|||||||
lokiThreadDB.removeAllSessionRestoreDevices(threadID)
|
lokiThreadDB.removeAllSessionRestoreDevices(threadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
// @JvmStatic
|
||||||
fun refreshSignedPreKey(context: Context) {
|
// fun refreshSignedPreKey(context: Context) {
|
||||||
if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
// if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
||||||
Log.d("Loki", "Skipping signed pre key refresh; using existing signed pre key.")
|
// Log.d("Loki", "Skipping signed pre key refresh; using existing signed pre key.")
|
||||||
} else {
|
// } else {
|
||||||
Log.d("Loki", "Signed pre key refreshed successfully.")
|
// Log.d("Loki", "Signed pre key refreshed successfully.")
|
||||||
val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
|
// val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
|
||||||
PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true)
|
// PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true)
|
||||||
TextSecurePreferences.setSignedPreKeyRegistered(context, true)
|
// TextSecurePreferences.setSignedPreKeyRegistered(context, true)
|
||||||
ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob())
|
// ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun shouldProcessSessionRequest(context: Context, publicKey: String, timestamp: Long): Boolean {
|
fun shouldProcessSessionRequest(context: Context, publicKey: String, timestamp: Long): Boolean {
|
||||||
@ -74,9 +71,9 @@ object SessionManagementProtocol {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val registrationID = TextSecurePreferences.getLocalRegistrationId(context)
|
val registrationID = TextSecurePreferences.getLocalRegistrationId(context)
|
||||||
val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
|
// val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
|
||||||
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
|
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
|
||||||
lokiPreKeyBundleDatabase.setPreKeyBundle(publicKey, preKeyBundle)
|
// lokiPreKeyBundleDatabase.setPreKeyBundle(publicKey, preKeyBundle)
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestProcessedTimestamp(publicKey, Date().time)
|
DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestProcessedTimestamp(publicKey, Date().time)
|
||||||
val job = NullMessageSendJob(publicKey)
|
val job = NullMessageSendJob(publicKey)
|
||||||
ApplicationContext.getInstance(context).jobManager.add(job)
|
ApplicationContext.getInstance(context).jobManager.add(job)
|
||||||
|
@ -44,16 +44,16 @@ class SessionRequestMessageSendJob private constructor(parameters: Parameters, p
|
|||||||
// Prepare
|
// Prepare
|
||||||
val contentMessage = SignalServiceProtos.Content.newBuilder()
|
val contentMessage = SignalServiceProtos.Content.newBuilder()
|
||||||
// Attach the pre key bundle message
|
// Attach the pre key bundle message
|
||||||
val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
|
// val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
|
||||||
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return
|
// val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return
|
||||||
preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize())
|
// preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize())
|
||||||
preKeyBundleMessage.deviceId = preKeyBundle.deviceId
|
// preKeyBundleMessage.deviceId = preKeyBundle.deviceId
|
||||||
preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId
|
// preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId
|
||||||
preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId
|
// preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId
|
||||||
preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
|
// preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
|
||||||
preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
|
// preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
|
||||||
preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
|
// preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
|
||||||
contentMessage.preKeyBundleMessage = preKeyBundleMessage.build()
|
// contentMessage.preKeyBundleMessage = preKeyBundleMessage.build()
|
||||||
// Attach the null message
|
// Attach the null message
|
||||||
val nullMessage = SignalServiceProtos.NullMessage.newBuilder()
|
val nullMessage = SignalServiceProtos.NullMessage.newBuilder()
|
||||||
val sr = SecureRandom()
|
val sr = SecureRandom()
|
||||||
|
@ -36,8 +36,8 @@ class SessionResetImplementation(private val context: Context) : SessionResetPro
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage) {
|
override fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage) {
|
||||||
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(publicKey) ?: return
|
// val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(publicKey) ?: return
|
||||||
// TODO: Checking that the pre key record isn't null is causing issues when it shouldn't
|
// // TODO: Checking that the pre key record isn't null is causing issues when it shouldn't
|
||||||
check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
|
// check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,76 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.protocol.shelved
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType
|
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Data
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.Job
|
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
|
||||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.session.libsignal.service.api.SignalServiceMessageSender
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage
|
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KEY = "MultiDeviceOpenGroupUpdateJob"
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var messageSender: SignalServiceMessageSender
|
|
||||||
|
|
||||||
constructor() : this(Parameters.Builder()
|
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
|
||||||
.setQueue(KEY)
|
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
|
||||||
.build())
|
|
||||||
|
|
||||||
override fun getFactoryKey(): String { return KEY }
|
|
||||||
|
|
||||||
override fun serialize(): Data { return Data.EMPTY }
|
|
||||||
|
|
||||||
@Throws(Exception::class)
|
|
||||||
public override fun onRun() {
|
|
||||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
Log.d("Loki", "Not multi device; aborting...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Gather open groups
|
|
||||||
val openGroups = mutableListOf<PublicChat>()
|
|
||||||
DatabaseFactory.getGroupDatabase(context).groups.use { reader ->
|
|
||||||
while (true) {
|
|
||||||
val record = reader.next ?: return@use
|
|
||||||
if (!record.isOpenGroup) { continue; }
|
|
||||||
val threadID = GroupManager.getThreadIDFromGroupID(record.encodedId, context)
|
|
||||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
|
||||||
if (openGroup != null) { openGroups.add(openGroup) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Send the message
|
|
||||||
if (openGroups.size > 0) {
|
|
||||||
messageSender.sendMessage(SignalServiceSyncMessage.forOpenGroups(openGroups), UnidentifiedAccessUtil.getAccessForSync(context))
|
|
||||||
} else {
|
|
||||||
Log.d("Loki", "No open groups to sync.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onShouldRetry(exception: Exception): Boolean {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCanceled() { }
|
|
||||||
|
|
||||||
class Factory : Job.Factory<MultiDeviceOpenGroupUpdateJob> {
|
|
||||||
|
|
||||||
override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob {
|
|
||||||
return MultiDeviceOpenGroupUpdateJob(parameters)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.protocol.shelved
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
|
||||||
import nl.komponents.kovenant.Promise
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
|
||||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
|
||||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushSendJob
|
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob
|
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.Broadcaster
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.recipient
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceContent
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
|
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLinkingSession
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
|
||||||
import org.session.libsignal.service.loki.utilities.retryIfNeeded
|
|
||||||
|
|
||||||
object MultiDeviceProtocol {
|
|
||||||
|
|
||||||
enum class MessageType { Text, Media }
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) {
|
|
||||||
sendMessagePush(context, recipient, messageID, MessageType.Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) {
|
|
||||||
sendMessagePush(context, recipient, messageID, MessageType.Media)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType) {
|
|
||||||
val jobManager = ApplicationContext.getInstance(context).jobManager
|
|
||||||
val isMultiDeviceRequired = !recipient.address.isOpenGroup
|
|
||||||
if (!isMultiDeviceRequired) {
|
|
||||||
when (messageType) {
|
|
||||||
MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address))
|
|
||||||
MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val publicKey = recipient.address.serialize()
|
|
||||||
FileServerAPI.shared.getDeviceLinks(publicKey).success {
|
|
||||||
val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey)
|
|
||||||
val jobs = devices.map {
|
|
||||||
when (messageType) {
|
|
||||||
MessageType.Text -> PushTextSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob
|
|
||||||
MessageType.Media -> PushMediaSendJob(messageID, messageID, recipient(context, it).address) as PushSendJob
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
when (messageType) {
|
|
||||||
MessageType.Text -> jobManager.startChain(jobs).enqueue()
|
|
||||||
MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, jobs as List<PushMediaSendJob>)
|
|
||||||
}
|
|
||||||
}.fail {
|
|
||||||
// Proceed even if updating the recipient's device links failed, so that message sending
|
|
||||||
// is independent of whether the file server is online
|
|
||||||
when (messageType) {
|
|
||||||
MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address))
|
|
||||||
MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendDeviceLinkMessage(context: Context, publicKey: String, deviceLink: DeviceLink): Promise<Unit, Exception> {
|
|
||||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
|
||||||
val address = SignalServiceAddress(publicKey)
|
|
||||||
val message = SignalServiceDataMessage.newBuilder().withDeviceLink(deviceLink)
|
|
||||||
// A request should include a pre key bundle. An authorization should be a normal message.
|
|
||||||
if (deviceLink.type == DeviceLink.Type.REQUEST) {
|
|
||||||
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.number)
|
|
||||||
message.withPreKeyBundle(preKeyBundle)
|
|
||||||
} else {
|
|
||||||
// Include the user's profile key so that the slave device can get the user's profile picture
|
|
||||||
message.withProfileKey(ProfileKeyUtil.getProfileKey(context))
|
|
||||||
}
|
|
||||||
return try {
|
|
||||||
Log.d("Loki", "Sending device link message to: $publicKey.")
|
|
||||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient(context, publicKey))
|
|
||||||
val result = messageSender.sendMessage(0, address, udAccess, message.build())
|
|
||||||
if (result.success == null) {
|
|
||||||
val exception = when {
|
|
||||||
result.isNetworkFailure -> "Failed to send device link message due to a network error."
|
|
||||||
else -> "Failed to send device link message."
|
|
||||||
}
|
|
||||||
throw Exception(exception)
|
|
||||||
}
|
|
||||||
Promise.ofSuccess(Unit)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.d("Loki", "Failed to send device link message to: $publicKey due to error: $e.")
|
|
||||||
Promise.ofFail(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun signAndSendDeviceLinkMessage(context: Context, deviceLink: DeviceLink): Promise<Unit, Exception> {
|
|
||||||
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
|
|
||||||
val signedDeviceLink = deviceLink.sign(DeviceLink.Type.AUTHORIZATION, userPrivateKey)
|
|
||||||
if (signedDeviceLink == null || signedDeviceLink.type != DeviceLink.Type.AUTHORIZATION) {
|
|
||||||
return Promise.ofFail(Exception("Failed to sign device link."))
|
|
||||||
}
|
|
||||||
return retryIfNeeded(8) {
|
|
||||||
sendDeviceLinkMessage(context, deviceLink.slavePublicKey, signedDeviceLink)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun handleDeviceLinkMessageIfNeeded(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
if (deviceLink.type == DeviceLink.Type.REQUEST) {
|
|
||||||
handleDeviceLinkRequestMessage(context, deviceLink, content)
|
|
||||||
} else if (deviceLink.slavePublicKey == userPublicKey) {
|
|
||||||
handleDeviceLinkAuthorizedMessage(context, deviceLink, content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isValidDeviceLinkMessage(context: Context, deviceLink: DeviceLink): Boolean {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val isRequest = (deviceLink.type == DeviceLink.Type.REQUEST)
|
|
||||||
if (deviceLink.requestSignature == null) {
|
|
||||||
Log.d("Loki", "Ignoring device link without a request signature.")
|
|
||||||
return false
|
|
||||||
} else if (isRequest && TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null) {
|
|
||||||
Log.d("Loki", "Ignoring unexpected device link message (the device is a slave device).")
|
|
||||||
return false
|
|
||||||
} else if (isRequest && deviceLink.masterPublicKey != userPublicKey) {
|
|
||||||
Log.d("Loki", "Ignoring device linking message addressed to another user.")
|
|
||||||
return false
|
|
||||||
} else if (isRequest && deviceLink.slavePublicKey == userPublicKey) {
|
|
||||||
Log.d("Loki", "Ignoring device linking request message from self.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return deviceLink.verify()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleDeviceLinkRequestMessage(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) {
|
|
||||||
val linkingSession = DeviceLinkingSession.shared
|
|
||||||
if (!linkingSession.isListeningForLinkingRequests) {
|
|
||||||
return Broadcaster(context).broadcast("unexpectedDeviceLinkRequestReceived")
|
|
||||||
}
|
|
||||||
val isValid = isValidDeviceLinkMessage(context, deviceLink)
|
|
||||||
if (!isValid) { return }
|
|
||||||
// The line below isn't actually necessary because this is called after PushDecryptJob
|
|
||||||
// calls handlePreKeyBundleMessageIfNeeded, but it also doesn't hurt.
|
|
||||||
SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content)
|
|
||||||
linkingSession.processLinkingRequest(deviceLink)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleDeviceLinkAuthorizedMessage(context: Context, deviceLink: DeviceLink, content: SignalServiceContent) {
|
|
||||||
val linkingSession = DeviceLinkingSession.shared
|
|
||||||
if (!linkingSession.isListeningForLinkingRequests) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val isValid = isValidDeviceLinkMessage(context, deviceLink)
|
|
||||||
if (!isValid) { return }
|
|
||||||
linkingSession.processLinkingAuthorization(deviceLink)
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userPublicKey)
|
|
||||||
DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(deviceLink)
|
|
||||||
TextSecurePreferences.setMasterHexEncodedPublicKey(context, deviceLink.masterPublicKey)
|
|
||||||
TextSecurePreferences.setMultiDevice(context, true)
|
|
||||||
FileServerAPI.shared.addDeviceLink(deviceLink)
|
|
||||||
org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol.handleProfileKeyUpdate(context, content)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun handleUnlinkingRequestIfNeeded(context: Context, content: SignalServiceContent) {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
// Check that the request was sent by the user's master device
|
|
||||||
val masterDevicePublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return
|
|
||||||
val wasSentByMasterDevice = (content.sender == masterDevicePublicKey)
|
|
||||||
if (!wasSentByMasterDevice) { return }
|
|
||||||
// Ignore the request if we don't know about the device link in question
|
|
||||||
val masterDeviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(masterDevicePublicKey)
|
|
||||||
if (masterDeviceLinks.none {
|
|
||||||
it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
FileServerAPI.shared.getDeviceLinks(userPublicKey, true).success { slaveDeviceLinks ->
|
|
||||||
// Check that the device link IS present on the file server.
|
|
||||||
// Note that the device link as seen from the master device's perspective has been deleted at this point, but the
|
|
||||||
// device link as seen from the slave perspective hasn't.
|
|
||||||
if (slaveDeviceLinks.any {
|
|
||||||
it.masterPublicKey == masterDevicePublicKey && it.slavePublicKey == userPublicKey
|
|
||||||
}) {
|
|
||||||
for (slaveDeviceLink in slaveDeviceLinks) { // In theory there should only be one
|
|
||||||
FileServerAPI.shared.removeDeviceLink(slaveDeviceLink) // Attempt to clean up on the file server
|
|
||||||
}
|
|
||||||
TextSecurePreferences.setWasUnlinked(context, true)
|
|
||||||
ApplicationContext.getInstance(context).clearData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.loki.protocol.shelved
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import org.thoughtcrime.securesms.logging.Log
|
|
||||||
import androidx.annotation.WorkerThread
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData
|
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData
|
|
||||||
import org.thoughtcrime.securesms.database.Address
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager
|
|
||||||
import org.thoughtcrime.securesms.groups.GroupMessageProcessor
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
|
|
||||||
import org.thoughtcrime.securesms.loki.utilities.recipient
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceContent
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.ContactsMessage
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceContactsInputStream
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceGroupsInputStream
|
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
|
||||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation
|
|
||||||
|
|
||||||
object SyncMessagesProtocol {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun shouldIgnoreSyncMessage(context: Context, sender: Recipient): Boolean {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
return userPublicKey == sender.address.serialize() // return !MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey).contains(sender.address.serialize())
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun syncContact(context: Context, address: Address) {
|
|
||||||
ApplicationContext.getInstance(context).jobManager.add(MultiDeviceContactUpdateJob(context, address, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun syncAllContacts(context: Context) {
|
|
||||||
ApplicationContext.getInstance(context).jobManager.add(MultiDeviceContactUpdateJob(context, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getContactsToSync(context: Context): List<ContactData> {
|
|
||||||
val contacts = ContactUtilities.getAllContacts(context)
|
|
||||||
val result = mutableSetOf<ContactData>()
|
|
||||||
for (contact in contacts) {
|
|
||||||
val contactPublicKey = contact.recipient.address.serialize()
|
|
||||||
if (!shouldSyncContact(context, contactPublicKey)) { continue }
|
|
||||||
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient(context, contactPublicKey))
|
|
||||||
if (threadID < 0) { continue }
|
|
||||||
val displayName = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(contactPublicKey)
|
|
||||||
val contactData = ContactData(threadID, displayName)
|
|
||||||
contactData.numbers.add(NumberData("TextSecure", contactPublicKey))
|
|
||||||
result.add(contactData)
|
|
||||||
}
|
|
||||||
return result.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun shouldSyncContact(context: Context, publicKey: String): Boolean {
|
|
||||||
if (!PublicKeyValidation.isValid(publicKey)) { return false }
|
|
||||||
if (publicKey == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) { return false }
|
|
||||||
if (publicKey == TextSecurePreferences.getLocalNumber(context)) { return false }
|
|
||||||
if (MultiDeviceProtocol.shared.getSlaveDevices(publicKey).contains(publicKey)) { return false }
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun syncAllClosedGroups(context: Context) {
|
|
||||||
ApplicationContext.getInstance(context).jobManager.add(MultiDeviceGroupUpdateJob())
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun syncAllOpenGroups(context: Context) {
|
|
||||||
ApplicationContext.getInstance(context).jobManager.add(MultiDeviceOpenGroupUpdateJob())
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun shouldSyncReadReceipt(address: Address): Boolean {
|
|
||||||
return !address.isGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun handleContactSyncMessage(context: Context, content: SignalServiceContent, message: ContactsMessage) {
|
|
||||||
if (!message.contactsStream.isStream) { return }
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
|
|
||||||
if (!allUserDevices.contains(content.sender)) { return }
|
|
||||||
Log.d("Loki", "Received a contact sync message.")
|
|
||||||
val contactsInputStream = DeviceContactsInputStream(message.contactsStream.asStream().inputStream)
|
|
||||||
val contacts = contactsInputStream.readAll()
|
|
||||||
for (contact in contacts) {
|
|
||||||
val contactPublicKey = contact.number
|
|
||||||
if (contactPublicKey == userPublicKey || !PublicKeyValidation.isValid(contactPublicKey)) { return }
|
|
||||||
val applicationContext = context.applicationContext as ApplicationContext
|
|
||||||
applicationContext.sendSessionRequestIfNeeded(contactPublicKey)
|
|
||||||
DatabaseFactory.getRecipientDatabase(context).setBlocked(recipient(context, contactPublicKey), contact.isBlocked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun handleClosedGroupSyncMessage(context: Context, content: SignalServiceContent, message: SignalServiceAttachment) {
|
|
||||||
if (!message.isStream) { return }
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
|
|
||||||
if (!allUserDevices.contains(content.sender)) { return }
|
|
||||||
Log.d("Loki", "Received a closed group sync message.")
|
|
||||||
val closedGroupsInputStream = DeviceGroupsInputStream(message.asStream().inputStream)
|
|
||||||
val closedGroups = closedGroupsInputStream.readAll()
|
|
||||||
for (closedGroup in closedGroups) {
|
|
||||||
val signalServiceGroup = SignalServiceGroup(
|
|
||||||
SignalServiceGroup.Type.UPDATE,
|
|
||||||
closedGroup.id,
|
|
||||||
SignalServiceGroup.GroupType.SIGNAL,
|
|
||||||
closedGroup.name.orNull(),
|
|
||||||
closedGroup.members,
|
|
||||||
closedGroup.avatar.orNull(),
|
|
||||||
closedGroup.admins
|
|
||||||
)
|
|
||||||
val dataMessage = SignalServiceDataMessage(content.timestamp, signalServiceGroup, null, null)
|
|
||||||
// This establishes sessions internally
|
|
||||||
GroupMessageProcessor.process(context, content, dataMessage, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@WorkerThread
|
|
||||||
fun handleOpenGroupSyncMessage(context: Context, content: SignalServiceContent, openGroups: List<PublicChat>) {
|
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
|
||||||
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
|
|
||||||
if (!allUserDevices.contains(content.sender)) { return }
|
|
||||||
Log.d("Loki", "Received an open group sync message.")
|
|
||||||
for (openGroup in openGroups) {
|
|
||||||
val threadID: Long = GroupManager.getOpenGroupThreadID(openGroup.id, context)
|
|
||||||
if (threadID > -1) { continue } // Skip existing open groups
|
|
||||||
val url = openGroup.server
|
|
||||||
val channel = openGroup.channel
|
|
||||||
OpenGroupUtilities.addGroup(context, url, channel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun handleBlockedContactsSyncMessage(context: Context, content: SignalServiceContent, blockedContacts: BlockedListMessage) {
|
|
||||||
val recipientDB = DatabaseFactory.getRecipientDatabase(context)
|
|
||||||
val cursor = recipientDB.blocked
|
|
||||||
val blockedPublicKeys = blockedContacts.numbers.toSet()
|
|
||||||
val publicKeysToUnblock = mutableSetOf<String>()
|
|
||||||
fun addToUnblockListIfNeeded() {
|
|
||||||
val publicKey = cursor.getString(cursor.getColumnIndex(RecipientDatabase.ADDRESS)) ?: return
|
|
||||||
if (blockedPublicKeys.contains(publicKey)) { return }
|
|
||||||
publicKeysToUnblock.add(publicKey)
|
|
||||||
}
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
addToUnblockListIfNeeded()
|
|
||||||
}
|
|
||||||
publicKeysToUnblock.forEach {
|
|
||||||
recipientDB.setBlocked(recipient(context, it), false)
|
|
||||||
}
|
|
||||||
blockedPublicKeys.forEach {
|
|
||||||
recipientDB.setBlocked(recipient(context, it), true)
|
|
||||||
}
|
|
||||||
ApplicationContext.getInstance(context).broadcaster.broadcast("blockedContactsChanged")
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,11 +17,9 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol;
|
|
||||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||||
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
|
||||||
|
|
||||||
@ -76,15 +74,7 @@ public class MarkReadReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
for (MarkedMessageInfo messageInfo : markedReadMessages) {
|
for (MarkedMessageInfo messageInfo : markedReadMessages) {
|
||||||
scheduleDeletion(context, messageInfo.getExpirationInfo());
|
scheduleDeletion(context, messageInfo.getExpirationInfo());
|
||||||
|
|
||||||
if (SyncMessagesProtocol.shouldSyncReadReceipt(messageInfo.getSyncMessageId().getAddress())) {
|
|
||||||
syncMessageIds.add(messageInfo.getSyncMessageId());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceReadUpdateJob(syncMessageIds));
|
|
||||||
|
|
||||||
Map<Address, List<SyncMessageId>> addressMap = Stream.of(markedReadMessages)
|
Map<Address, List<SyncMessageId>> addressMap = Stream.of(markedReadMessages)
|
||||||
.map(MarkedMessageInfo::getSyncMessageId)
|
.map(MarkedMessageInfo::getSyncMessageId)
|
||||||
|
@ -12,20 +12,16 @@ import androidx.appcompat.app.AlertDialog;
|
|||||||
import androidx.preference.CheckBoxPreference;
|
import androidx.preference.CheckBoxPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
import org.session.libsignal.service.api.SignalServiceAccountManager;
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.PassphraseChangeActivity;
|
import org.thoughtcrime.securesms.PassphraseChangeActivity;
|
||||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||||
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
import org.thoughtcrime.securesms.dependencies.InjectableType;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import mobi.upod.timedurationpicker.TimeDurationPickerDialog;
|
import mobi.upod.timedurationpicker.TimeDurationPickerDialog;
|
||||||
import network.loki.messenger.R;
|
import network.loki.messenger.R;
|
||||||
|
|
||||||
@ -33,9 +29,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
|
|
||||||
private CheckBoxPreference disablePassphrase;
|
private CheckBoxPreference disablePassphrase;
|
||||||
|
|
||||||
@Inject
|
|
||||||
SignalServiceAccountManager accountManager;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Activity activity) {
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
@ -147,13 +140,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
boolean enabled = (boolean)newValue;
|
boolean enabled = (boolean)newValue;
|
||||||
ApplicationContext.getInstance(getContext())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceConfigurationUpdateJob(enabled,
|
|
||||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
|
|
||||||
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,13 +149,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
boolean enabled = (boolean)newValue;
|
boolean enabled = (boolean)newValue;
|
||||||
|
|
||||||
ApplicationContext.getInstance(getContext())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
|
|
||||||
enabled,
|
|
||||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
|
|
||||||
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
|
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().clear();
|
ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().clear();
|
||||||
}
|
}
|
||||||
@ -191,24 +170,11 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
|
|||||||
builder.setNegativeButton("Cancel", (dialog, which) -> {
|
builder.setNegativeButton("Cancel", (dialog, which) -> {
|
||||||
TextSecurePreferences.setLinkPreviewsEnabled(requireContext(), false);
|
TextSecurePreferences.setLinkPreviewsEnabled(requireContext(), false);
|
||||||
((SwitchPreferenceCompat)AppProtectionPreferenceFragment.this.findPreference(TextSecurePreferences.LINK_PREVIEWS)).setChecked(false);
|
((SwitchPreferenceCompat)AppProtectionPreferenceFragment.this.findPreference(TextSecurePreferences.LINK_PREVIEWS)).setChecked(false);
|
||||||
ApplicationContext.getInstance(requireContext())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()),
|
|
||||||
false));
|
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
});
|
});
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationContext.getInstance(requireContext())
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
|
||||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()),
|
|
||||||
enabled));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.service;
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
|
||||||
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class RotateSignedPreKeyListener extends PersistentAlarmManagerListener {
|
|
||||||
|
|
||||||
private static final long INTERVAL = TimeUnit.DAYS.toMillis(2);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long getNextScheduledExecutionTime(Context context) {
|
|
||||||
return TextSecurePreferences.getSignedPreKeyRotationTime(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long onAlarm(Context context, long scheduledTime) {
|
|
||||||
if (scheduledTime != 0 /*&& TextSecurePreferences.isPushRegistered(context)*/) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new RotateSignedPreKeyJob());
|
|
||||||
}
|
|
||||||
|
|
||||||
long nextTime = System.currentTimeMillis() + INTERVAL;
|
|
||||||
TextSecurePreferences.setSignedPreKeyRotationTime(context, nextTime);
|
|
||||||
|
|
||||||
return nextTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void schedule(Context context) {
|
|
||||||
new RotateSignedPreKeyListener().onReceive(context, new Intent());
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.database.StickerDatabase;
|
|||||||
import org.thoughtcrime.securesms.database.StickerDatabase.StickerPackRecordReader;
|
import org.thoughtcrime.securesms.database.StickerDatabase.StickerPackRecordReader;
|
||||||
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
|
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackOperationJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
@ -76,12 +75,6 @@ final class StickerManagementRepository {
|
|||||||
void uninstallStickerPack(@NonNull String packId, @NonNull String packKey) {
|
void uninstallStickerPack(@NonNull String packId, @NonNull String packKey) {
|
||||||
SignalExecutors.SERIAL.execute(() -> {
|
SignalExecutors.SERIAL.execute(() -> {
|
||||||
stickerDatabase.uninstallPack(packId);
|
stickerDatabase.uninstallPack(packId);
|
||||||
|
|
||||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getJobManager()
|
|
||||||
.add(new MultiDeviceStickerPackOperationJob(packId, packKey, MultiDeviceStickerPackOperationJob.Type.REMOVE));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,10 +87,6 @@ final class StickerManagementRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jobManager.add(new StickerPackDownloadJob(packId, packKey, false));
|
jobManager.add(new StickerPackDownloadJob(packId, packKey, false));
|
||||||
|
|
||||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
||||||
jobManager.add(new MultiDeviceStickerPackOperationJob(packId, packKey, MultiDeviceStickerPackOperationJob.Type.INSTALL));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,12 +11,19 @@ import org.session.libsignal.service.api.push.SignalServiceAddress
|
|||||||
import org.session.libsignal.service.internal.push.PushTransportDetails
|
import org.session.libsignal.service.internal.push.PushTransportDetails
|
||||||
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol
|
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol
|
||||||
|
|
||||||
class LokiServiceCipher(localAddress: SignalServiceAddress, private val signalProtocolStore: SignalProtocolStore, private val sskDatabase: SharedSenderKeysDatabaseProtocol, sessionResetProtocol: SessionResetProtocol, certificateValidator: CertificateValidator?) : SignalServiceCipher(localAddress, signalProtocolStore, sskDatabase, sessionResetProtocol, certificateValidator) {
|
class LokiServiceCipher(
|
||||||
|
localAddress: SignalServiceAddress,
|
||||||
|
private val signalProtocolStore: SignalProtocolStore,
|
||||||
|
private val sskDatabase: SharedSenderKeysDatabaseProtocol,
|
||||||
|
sessionResetProtocol: SessionResetProtocol,
|
||||||
|
certificateValidator: CertificateValidator?)
|
||||||
|
: SignalServiceCipher(localAddress, signalProtocolStore, sskDatabase, sessionResetProtocol, certificateValidator) {
|
||||||
|
|
||||||
private val userPrivateKey get() = signalProtocolStore.identityKeyPair.privateKey.serialize()
|
private val userPrivateKey get() = signalProtocolStore.identityKeyPair.privateKey.serialize()
|
||||||
|
|
||||||
override fun decrypt(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext {
|
override fun decrypt(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext {
|
||||||
return if (envelope.isFallbackMessage) decryptFallbackMessage(envelope, ciphertext) else super.decrypt(envelope, ciphertext)
|
// return if (envelope.isFallbackMessage) decryptFallbackMessage(envelope, ciphertext) else super.decrypt(envelope, ciphertext)
|
||||||
|
return decryptFallbackMessage(envelope, ciphertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decryptFallbackMessage(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext {
|
private fun decryptFallbackMessage(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext {
|
||||||
|
Loading…
Reference in New Issue
Block a user