Pre key bundle removal pt1.

Device link functionality removed from app module.
This commit is contained in:
Anton Chekulaev 2020-12-09 18:23:05 +11:00
parent bb74cdf2a0
commit 3bc4338444
49 changed files with 117 additions and 3314 deletions

View File

@ -483,11 +483,6 @@
<action android:name="network.loki.securesms.RESTART" />
</intent-filter>
</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">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />

View File

@ -43,16 +43,13 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.FastJobStorage;
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.PushContentReceiveJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
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.LokiPushNotificationManager;
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.LokiThreadDatabase;
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.LocalBackupListener;
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -229,7 +224,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
initializeExpiringMessageManager();
initializeTypingStatusRepository();
initializeTypingStatusSender();
initializeSignedPreKeyCheck();
initializePeriodicTasks();
initializeWebRtc();
initializePendingMessages();
@ -241,7 +235,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
public void onStart(@NonNull LifecycleOwner owner) {
isAppVisible = true;
Log.i(TAG, "App is now visible.");
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
// Loki
if (poller != null) { poller.setCaughtUp(false); }
@ -365,14 +358,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
private void initializeDependencyInjection() {
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() {
this.expiringMessageManager = new ExpiringMessageManager(this);
@ -387,7 +375,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
}
private void initializePeriodicTasks() {
RotateSignedPreKeyListener.schedule(this);
LocalBackupListener.schedule(this);
RotateSenderCertificateListener.schedule(this);
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() {
if (TextSecurePreferences.getNeedsMessagePull(this)) {
Log.i(TAG, "Scheduling a message fetch.");

View File

@ -39,7 +39,6 @@ import org.thoughtcrime.securesms.database.MmsDatabase.Reader;
import org.thoughtcrime.securesms.database.PushDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.logging.Log;
@ -227,9 +226,9 @@ public class DatabaseUpgradeActivity extends BaseActivity {
}
if (params[0] < SIGNED_PREKEY_VERSION) {
ApplicationContext.getInstance(getApplicationContext())
.getJobManager()
.add(new CreateSignedPreKeyJob(context));
// ApplicationContext.getInstance(getApplicationContext())
// .getJobManager()
// .add(new CreateSignedPreKeyJob(context));
}
if (params[0] < NO_DECRYPT_QUEUE_VERSION) {

View File

@ -9,7 +9,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
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.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();
});

View File

@ -10,7 +10,6 @@ import android.view.View;
import android.view.ViewGroup;
import org.thoughtcrime.securesms.components.TypingIndicatorView;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import network.loki.messenger.R;
@ -60,12 +59,6 @@ public class TypingIndicatorIntroFragment extends Fragment {
private void onButtonClicked(boolean typingEnabled) {
TextSecurePreferences.setTypingIndicatorsEnabled(getContext(), typingEnabled);
ApplicationContext.getInstance(requireContext())
.getJobManager()
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
typingEnabled,
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
controller.onTypingIndicatorsFinished();
}

View File

@ -67,7 +67,6 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.qr.QrCode;
@ -188,6 +187,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
}
@ -602,13 +602,6 @@ public class VerifyIdentityActivity extends PassphraseRequiredActionBarActivity
VerifiedStatus.DEFAULT);
}
ApplicationContext.getInstance(getActivity())
.getJobManager()
.add(new MultiDeviceVerifiedUpdateJob(recipient.getAddress(),
remoteIdentity,
isChecked ? VerifiedStatus.VERIFIED :
VerifiedStatus.DEFAULT));
IdentityUtil.markIdentityVerified(getActivity(), recipient, isChecked, false);
}
return null;

View File

@ -140,7 +140,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.giph.ui.GiphyActivity;
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
@ -954,10 +953,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setBlocked(recipient, false);
ApplicationContext.getInstance(ConversationActivity.this)
.getJobManager()
.add(new MultiDeviceBlockedUpdateJob());
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -1016,10 +1011,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
DatabaseFactory.getRecipientDatabase(ConversationActivity.this)
.setBlocked(recipient, true);
ApplicationContext.getInstance(ConversationActivity.this)
.getJobManager()
.add(new MultiDeviceBlockedUpdateJob());
Util.runOnMain(() -> finish());
return null;

View File

@ -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
}

View File

@ -19,14 +19,14 @@ import java.util.List;
public class SignalProtocolStoreImpl implements SignalProtocolStore {
private final PreKeyStore preKeyStore;
private final SignedPreKeyStore signedPreKeyStore;
// private final PreKeyStore preKeyStore;
// private final SignedPreKeyStore signedPreKeyStore;
private final IdentityKeyStore identityKeyStore;
private final SessionStore sessionStore;
public SignalProtocolStoreImpl(Context context) {
this.preKeyStore = new TextSecurePreKeyStore(context);
this.signedPreKeyStore = new TextSecurePreKeyStore(context);
// this.preKeyStore = new TextSecurePreKeyStore(context);
// this.signedPreKeyStore = new TextSecurePreKeyStore(context);
this.identityKeyStore = new TextSecureIdentityKeyStore(context);
this.sessionStore = new TextSecureSessionStore(context);
}
@ -58,22 +58,26 @@ public class SignalProtocolStoreImpl implements SignalProtocolStore {
@Override
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
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
public boolean containsPreKey(int preKeyId) {
return preKeyStore.containsPreKey(preKeyId);
throw new UnsupportedOperationException("This method will be removed with refactor.");
// return preKeyStore.containsPreKey(preKeyId);
}
@Override
public void removePreKey(int preKeyId) {
preKeyStore.removePreKey(preKeyId);
throw new UnsupportedOperationException("This method will be removed with refactor.");
// preKeyStore.removePreKey(preKeyId);
}
@Override
@ -108,26 +112,31 @@ public class SignalProtocolStoreImpl implements SignalProtocolStore {
@Override
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
public List<SignedPreKeyRecord> loadSignedPreKeys() {
return signedPreKeyStore.loadSignedPreKeys();
throw new UnsupportedOperationException("This method will be removed with refactor.");
// return signedPreKeyStore.loadSignedPreKeys();
}
@Override
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
public boolean containsSignedPreKey(int signedPreKeyId) {
return signedPreKeyStore.containsSignedPreKey(signedPreKeyId);
throw new UnsupportedOperationException("This method will be removed with refactor.");
// return signedPreKeyStore.containsSignedPreKey(signedPreKeyId);
}
@Override
public void removeSignedPreKey(int signedPreKeyId) {
signedPreKeyStore.removeSignedPreKey(signedPreKeyId);
throw new UnsupportedOperationException("This method will be removed with refactor.");
// signedPreKeyStore.removeSignedPreKey(signedPreKeyId);
}
}

View File

@ -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);
}
}

View File

@ -33,8 +33,6 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
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.LokiPreKeyBundleDatabase;
import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
@ -68,8 +66,8 @@ public class DatabaseFactory {
// Loki
private final LokiAPIDatabase lokiAPIDatabase;
private final LokiPreKeyRecordDatabase lokiContactPreKeyDatabase;
private final LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase;
// private final LokiPreKeyRecordDatabase lokiContactPreKeyDatabase;
// private final LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase;
private final LokiMessageDatabase lokiMessageDatabase;
private final LokiThreadDatabase lokiThreadDatabase;
private final LokiUserDatabase lokiUserDatabase;
@ -166,13 +164,13 @@ public class DatabaseFactory {
return getInstance(context).lokiAPIDatabase;
}
public static LokiPreKeyRecordDatabase getLokiPreKeyRecordDatabase(Context context) {
return getInstance(context).lokiContactPreKeyDatabase;
}
// public static LokiPreKeyRecordDatabase getLokiPreKeyRecordDatabase(Context context) {
// return getInstance(context).lokiContactPreKeyDatabase;
// }
public static LokiPreKeyBundleDatabase getLokiPreKeyBundleDatabase(Context context) {
return getInstance(context).lokiPreKeyBundleDatabase;
}
// public static LokiPreKeyBundleDatabase getLokiPreKeyBundleDatabase(Context context) {
// return getInstance(context).lokiPreKeyBundleDatabase;
// }
public static LokiMessageDatabase getLokiMessageDatabase(Context context) {
return getInstance(context).lokiMessageDatabase;
@ -226,8 +224,8 @@ public class DatabaseFactory {
this.jobDatabase = new JobDatabase(context, databaseHelper);
this.stickerDatabase = new StickerDatabase(context, databaseHelper, attachmentSecret);
this.lokiAPIDatabase = new LokiAPIDatabase(context, databaseHelper);
this.lokiContactPreKeyDatabase = new LokiPreKeyRecordDatabase(context, databaseHelper);
this.lokiPreKeyBundleDatabase = new LokiPreKeyBundleDatabase(context, databaseHelper);
// this.lokiContactPreKeyDatabase = new LokiPreKeyRecordDatabase(context, databaseHelper);
// this.lokiPreKeyBundleDatabase = new LokiPreKeyBundleDatabase(context, databaseHelper);
this.lokiMessageDatabase = new LokiMessageDatabase(context, databaseHelper);
this.lokiThreadDatabase = new LokiThreadDatabase(context, databaseHelper);
this.lokiUserDatabase = new LokiUserDatabase(context, databaseHelper);

View File

@ -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() {}
}
}

View File

@ -14,7 +14,7 @@ import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
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.MasterSecret;
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.IdentityDatabase;
import org.thoughtcrime.securesms.database.JobDatabase;
import org.thoughtcrime.securesms.loki.database.LokiBackupFilesDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase;
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.StickerDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.logging.Log;
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.LokiPreKeyBundleDatabase;
import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
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.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
import java.io.File;
import java.util.Arrays;
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
@ -161,8 +156,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreateSessionRequestProcessedTimestampTableCommand());
db.execSQL(LokiAPIDatabase.getCreateOpenGroupPublicKeyTableCommand());
db.execSQL(LokiAPIDatabase.getCreateOpenGroupProfilePictureTableCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageIDTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageToThreadMappingTableCommand());
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
@ -195,12 +188,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
if (masterSecret != null) SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret, legacyDb, db, null);
else TextSecurePreferences.setNeedsSqlCipherMigration(context, true);
if (!PreKeyMigrationHelper.migratePreKeys(context, db)) {
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
}
SessionStoreMigrationHelper.migrateSessions(context, db);
PreKeyMigrationHelper.cleanUpPreKeys(context);
}
}
@ -229,10 +217,6 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
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 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) {
@ -671,7 +655,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
}
if (oldVersion < MIGRATE_PREKEYS_VERSION) {
PreKeyMigrationHelper.cleanUpPreKeys(context);
// PreKeyMigrationHelper.cleanUpPreKeys(context);
}
}

View File

@ -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();
}
}

View File

@ -16,17 +16,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
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.PushGroupSendJob;
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.PushTextSendJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
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.logging.Log;
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.push.MessageSenderEventListener;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
@ -64,34 +50,23 @@ import dagger.Module;
import dagger.Provides;
import network.loki.messenger.BuildConfig;
@Module(complete = false, injects = {CleanPreKeysJob.class,
CreateSignedPreKeyJob.class,
PushGroupSendJob.class,
@Module(complete = false, injects = {PushGroupSendJob.class,
PushTextSendJob.class,
PushMediaSendJob.class,
AttachmentDownloadJob.class,
RefreshPreKeysJob.class,
IncomingMessageObserver.class,
PushNotificationReceiveJob.class,
MultiDeviceContactUpdateJob.class,
MultiDeviceGroupUpdateJob.class,
MultiDeviceReadUpdateJob.class,
MultiDeviceBlockedUpdateJob.class,
RefreshAttributesJob.class,
RequestGroupInfoJob.class,
PushGroupUpdateJob.class,
AvatarDownloadJob.class,
RotateSignedPreKeyJob.class,
RetrieveProfileJob.class,
MultiDeviceVerifiedUpdateJob.class,
RetrieveProfileAvatarJob.class,
MultiDeviceProfileKeyUpdateJob.class,
SendReadReceiptJob.class,
AppProtectionPreferenceFragment.class,
RotateCertificateJob.class,
SendDeliveryReceiptJob.class,
RotateProfileKeyJob.class,
MultiDeviceConfigurationUpdateJob.class,
RefreshUnidentifiedDeliveryAbilityJob.class,
TypingSendJob.class,
AttachmentUploadJob.class,
@ -100,10 +75,7 @@ import network.loki.messenger.BuildConfig;
StickerPackPreviewRepository.class,
StickerRemoteUriLoader.Factory.class,
StickerPackDownloadJob.class,
MultiDeviceStickerPackOperationJob.class,
MultiDeviceStickerPackSyncJob.class,
LinkPreviewRepository.class,
MultiDeviceOpenGroupUpdateJob.class})
LinkPreviewRepository.class})
public class SignalCommunicationModule {
@ -149,7 +121,8 @@ public class SignalCommunicationModule {
DatabaseFactory.getSSKDatabase(context),
DatabaseFactory.getLokiThreadDatabase(context),
DatabaseFactory.getLokiMessageDatabase(context),
DatabaseFactory.getLokiPreKeyBundleDatabase(context),
// DatabaseFactory.getLokiPreKeyBundleDatabase(context),
null,
new SessionResetImplementation(context),
DatabaseFactory.getLokiUserDatabase(context),
DatabaseFactory.getGroupDatabase(context),

View File

@ -6,19 +6,10 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
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.MmsDownloadJob;
import org.thoughtcrime.securesms.jobs.MmsReceiveJob;
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.PushDecryptJob;
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.PushTextSendJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
import org.thoughtcrime.securesms.jobs.RotateSignedPreKeyJob;
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
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.NullMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
import java.util.HashMap;
import java.util.Map;
@ -59,21 +47,11 @@ public class WorkManagerFactoryMappings {
put(AttachmentDownloadJob.class.getName(), AttachmentDownloadJob.KEY);
put(AttachmentUploadJob.class.getName(), AttachmentUploadJob.KEY);
put(AvatarDownloadJob.class.getName(), AvatarDownloadJob.KEY);
put(CleanPreKeysJob.class.getName(), CleanPreKeysJob.KEY);
put(ClosedGroupUpdateMessageSendJob.class.getName(), ClosedGroupUpdateMessageSendJob.KEY);
put(CreateSignedPreKeyJob.class.getName(), CreateSignedPreKeyJob.KEY);
put(LocalBackupJob.class.getName(), LocalBackupJob.KEY);
put(MmsDownloadJob.class.getName(), MmsDownloadJob.KEY);
put(MmsReceiveJob.class.getName(), MmsReceiveJob.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(PushContentReceiveJob.class.getName(), PushContentReceiveJob.KEY);
put(PushDecryptJob.class.getName(), PushDecryptJob.KEY);
@ -83,14 +61,12 @@ public class WorkManagerFactoryMappings {
put(PushNotificationReceiveJob.class.getName(), PushNotificationReceiveJob.KEY);
put(PushTextSendJob.class.getName(), PushTextSendJob.KEY);
put(RefreshAttributesJob.class.getName(), RefreshAttributesJob.KEY);
put(RefreshPreKeysJob.class.getName(), RefreshPreKeysJob.KEY);
put(RefreshUnidentifiedDeliveryAbilityJob.class.getName(), RefreshUnidentifiedDeliveryAbilityJob.KEY);
put(RequestGroupInfoJob.class.getName(), RequestGroupInfoJob.KEY);
put(RetrieveProfileAvatarJob.class.getName(), RetrieveProfileAvatarJob.KEY);
put(RetrieveProfileJob.class.getName(), RetrieveProfileJob.KEY);
put(RotateCertificateJob.class.getName(), RotateCertificateJob.KEY);
put(RotateProfileKeyJob.class.getName(), RotateProfileKeyJob.KEY);
put(RotateSignedPreKeyJob.class.getName(), RotateSignedPreKeyJob.KEY);
put(SendDeliveryReceiptJob.class.getName(), SendDeliveryReceiptJob.KEY);
put(SendReadReceiptJob.class.getName(), SendReadReceiptJob.KEY);
put(SessionRequestMessageSendJob.class.getName(), SessionRequestMessageSendJob.KEY);

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -19,7 +19,6 @@ import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
import java.util.Arrays;
import java.util.HashMap;
@ -33,23 +32,11 @@ public final class JobManagerFactories {
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
put(ClosedGroupUpdateMessageSendJob.KEY, new ClosedGroupUpdateMessageSendJob.Factory());
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());
put(MmsReceiveJob.KEY, new MmsReceiveJob.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(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory());
put(PushDecryptJob.KEY, new PushDecryptJob.Factory());
@ -59,14 +46,12 @@ public final class JobManagerFactories {
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
put(PushTextSendJob.KEY, new PushTextSendJob.Factory());
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory());
put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application));
put(RetrieveProfileJob.KEY, new RetrieveProfileJob.Factory(application));
put(RotateCertificateJob.KEY, new RotateCertificateJob.Factory());
put(RotateProfileKeyJob.KEY, new RotateProfileKeyJob.Factory());
put(RotateSignedPreKeyJob.KEY, new RotateSignedPreKeyJob.Factory());
put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory());
put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory());
put(SessionRequestMessageSendJob.KEY, new SessionRequestMessageSendJob.Factory());

View File

@ -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);
}
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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)));
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -70,8 +70,6 @@ import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
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.PromiseUtilities;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
@ -265,13 +263,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
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()) {
SignalServiceDataMessage message = content.getDataMessage().get();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
if (message.isDeviceUnlinkingRequest()) {
MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
throw new UnsupportedOperationException("Device link operations are not supported!");
} else {
if (message.getClosedGroupUpdate().isPresent()) {
@ -303,20 +301,22 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
}
} else if (content.getSyncMessage().isPresent()) {
TextSecurePreferences.setMultiDevice(context, true);
throw new UnsupportedOperationException("Device link operations are not supported!");
SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get());
else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get());
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...");
// TextSecurePreferences.setMultiDevice(context, true);
//
// SignalServiceSyncMessage syncMessage = content.getSyncMessage().get();
//
// if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get());
// else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get());
// else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
// else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
// else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
// else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get());
// else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get());
// 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()) {
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));
if (envelope.isPreKeySignalMessage()) {
ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
}
// if (envelope.isPreKeySignalMessage()) {
// ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
// }
} catch (ProtocolInvalidVersionException e) {
Log.w(TAG, e);
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,
@NonNull SignalServiceDataMessage message,
@NonNull Optional<Long> smsMessageId,
@ -1413,7 +1351,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return sender.isBlocked();
}
} else if (content.getSyncMessage().isPresent()) {
return SyncMessagesProtocol.shouldIgnoreSyncMessage(context, sender);
throw new UnsupportedOperationException("Device link operations are not supported!");
}
return false;

View File

@ -71,13 +71,13 @@ public abstract class PushSendJob extends SendJob {
@Override
protected final void onSend() throws Exception {
if (TextSecurePreferences.getSignedPreKeyFailureCount(context) > 5) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new RotateSignedPreKeyJob());
throw new TextSecureExpiredException("Too many signed prekey rotation failures");
}
// if (TextSecurePreferences.getSignedPreKeyFailureCount(context) > 5) {
// ApplicationContext.getInstance(context)
// .getJobManager()
// .add(new RotateSignedPreKeyJob());
//
// throw new TextSecureExpiredException("Too many signed prekey rotation failures");
// }
onPushSend();
}

View File

@ -197,17 +197,17 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
PreKeyBundle preKeyBundle = null;
if (message.isEndSession()) {
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
}
// PreKeyBundle preKeyBundle = null;
// if (message.isEndSession()) {
// preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(destination.serialize());
// }
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
.withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull())
.withPreKeyBundle(preKeyBundle)
// .withPreKeyBundle(preKeyBundle)
.asEndSessionMessage(message.isEndSession())
.build();

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -7,7 +7,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.database.Cursor
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
@ -32,10 +31,7 @@ import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.ConversationActivity
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.ThreadDatabase
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.dialogs.ConversationOptionsBottomSheet
import org.thoughtcrime.securesms.loki.dialogs.LightThemeFeatureIntroBottomSheet
@ -329,7 +325,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
.setPositiveButton(R.string.RecipientPreferenceActivity_block) { dialog, _ ->
Thread {
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, true)
ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob())
Util.runOnMain {
recyclerView.adapter!!.notifyDataSetChanged()
dialog.dismiss()
@ -346,7 +341,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock) { dialog, _ ->
Thread {
DatabaseFactory.getRecipientDatabase(this).setBlocked(thread.recipient, false)
ApplicationContext.getInstance(this).jobManager.add(MultiDeviceBlockedUpdateJob())
Util.runOnMain {
recyclerView.adapter!!.notifyDataSetChanged()
dialog.dismiss()

View File

@ -3,14 +3,14 @@ package org.thoughtcrime.securesms.loki.activities
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
import android.util.Patterns
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_join_public_chat.*
import kotlinx.android.synthetic.main.fragment_enter_chat_url.*
@ -18,15 +18,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
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.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragment
import org.thoughtcrime.securesms.loki.fragments.ScanQRCodeWrapperFragmentDelegate
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol
import org.thoughtcrime.securesms.loki.utilities.OpenGroupUtilities
import java.lang.Exception
class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
private val adapter = JoinPublicChatActivityAdapter(this)
@ -83,7 +79,6 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
}
return@launch
}
SyncMessagesProtocol.syncAllOpenGroups(this@JoinPublicChatActivity)
withContext(Dispatchers.Main) { finish() }
}
}

View File

@ -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()
}
}
}

View File

@ -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
}
}

View File

@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.loki.protocol
import com.google.protobuf.ByteString
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl
import org.thoughtcrime.securesms.jobmanager.Data
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
@ -124,7 +123,8 @@ class ClosedGroupUpdateMessageSendJob private constructor(parameters: Parameters
val recipient = recipient(context, destination)
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
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 {
// isClosedGroup can always be false as it's only used in the context of legacy closed groups
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,

View File

@ -3,12 +3,9 @@ package org.thoughtcrime.securesms.loki.protocol
import android.content.Context
import org.thoughtcrime.securesms.logging.Log
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.storage.TextSecureSessionStore
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage
@ -41,18 +38,18 @@ object SessionManagementProtocol {
lokiThreadDB.removeAllSessionRestoreDevices(threadID)
}
@JvmStatic
fun refreshSignedPreKey(context: Context) {
if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
Log.d("Loki", "Skipping signed pre key refresh; using existing signed pre key.")
} else {
Log.d("Loki", "Signed pre key refreshed successfully.")
val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true)
TextSecurePreferences.setSignedPreKeyRegistered(context, true)
ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob())
}
}
// @JvmStatic
// fun refreshSignedPreKey(context: Context) {
// if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
// Log.d("Loki", "Skipping signed pre key refresh; using existing signed pre key.")
// } else {
// Log.d("Loki", "Signed pre key refreshed successfully.")
// val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
// PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true)
// TextSecurePreferences.setSignedPreKeyRegistered(context, true)
// ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob())
// }
// }
@JvmStatic
fun shouldProcessSessionRequest(context: Context, publicKey: String, timestamp: Long): Boolean {
@ -74,9 +71,9 @@ object SessionManagementProtocol {
return
}
val registrationID = TextSecurePreferences.getLocalRegistrationId(context)
val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
// val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
lokiPreKeyBundleDatabase.setPreKeyBundle(publicKey, preKeyBundle)
// lokiPreKeyBundleDatabase.setPreKeyBundle(publicKey, preKeyBundle)
DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestProcessedTimestamp(publicKey, Date().time)
val job = NullMessageSendJob(publicKey)
ApplicationContext.getInstance(context).jobManager.add(job)

View File

@ -44,16 +44,16 @@ class SessionRequestMessageSendJob private constructor(parameters: Parameters, p
// Prepare
val contentMessage = SignalServiceProtos.Content.newBuilder()
// Attach the pre key bundle message
val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return
preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize())
preKeyBundleMessage.deviceId = preKeyBundle.deviceId
preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId
preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId
preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
contentMessage.preKeyBundleMessage = preKeyBundleMessage.build()
// val preKeyBundleMessage = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
// val preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(publicKey) ?: return
// preKeyBundleMessage.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.serialize())
// preKeyBundleMessage.deviceId = preKeyBundle.deviceId
// preKeyBundleMessage.preKeyId = preKeyBundle.preKeyId
// preKeyBundleMessage.signedKeyId = preKeyBundle.signedPreKeyId
// preKeyBundleMessage.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
// preKeyBundleMessage.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
// preKeyBundleMessage.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
// contentMessage.preKeyBundleMessage = preKeyBundleMessage.build()
// Attach the null message
val nullMessage = SignalServiceProtos.NullMessage.newBuilder()
val sr = SecureRandom()

View File

@ -36,8 +36,8 @@ class SessionResetImplementation(private val context: Context) : SessionResetPro
}
override fun validatePreKeySignalMessage(publicKey: String, message: PreKeySignalMessage) {
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
check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
// 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
// check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
}
}

View File

@ -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)
}
}
}

View File

@ -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()
}
}
}
}

View File

@ -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")
}
}

View File

@ -17,11 +17,9 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
import org.thoughtcrime.securesms.loki.protocol.shelved.SyncMessagesProtocol;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
@ -76,16 +74,8 @@ public class MarkReadReceiver extends BroadcastReceiver {
for (MarkedMessageInfo messageInfo : markedReadMessages) {
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(MarkedMessageInfo::getSyncMessageId)
.collect(Collectors.groupingBy(SyncMessageId::getAddress));

View File

@ -12,20 +12,16 @@ import androidx.appcompat.app.AlertDialog;
import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference;
import org.session.libsignal.service.api.SignalServiceAccountManager;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.PassphraseChangeActivity;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import mobi.upod.timedurationpicker.TimeDurationPickerDialog;
import network.loki.messenger.R;
@ -33,9 +29,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
private CheckBoxPreference disablePassphrase;
@Inject
SignalServiceAccountManager accountManager;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
@ -147,13 +140,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean enabled = (boolean)newValue;
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new MultiDeviceConfigurationUpdateJob(enabled,
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
return true;
}
}
@ -163,13 +149,6 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean enabled = (boolean)newValue;
ApplicationContext.getInstance(getContext())
.getJobManager()
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
enabled,
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
if (!enabled) {
ApplicationContext.getInstance(requireContext()).getTypingStatusRepository().clear();
}
@ -191,24 +170,11 @@ public class AppProtectionPreferenceFragment extends CorrectedPreferenceFragment
builder.setNegativeButton("Cancel", (dialog, which) -> {
TextSecurePreferences.setLinkPreviewsEnabled(requireContext(), 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();
});
builder.create().show();
}
ApplicationContext.getInstance(requireContext())
.getJobManager()
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()),
enabled));
return true;
}
}

View File

@ -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());
}
}

View File

@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.StickerDatabase.StickerPackRecordReader;
import org.thoughtcrime.securesms.database.model.StickerPackRecord;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackOperationJob;
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
@ -76,12 +75,6 @@ final class StickerManagementRepository {
void uninstallStickerPack(@NonNull String packId, @NonNull String packKey) {
SignalExecutors.SERIAL.execute(() -> {
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));
if (TextSecurePreferences.isMultiDevice(context)) {
jobManager.add(new MultiDeviceStickerPackOperationJob(packId, packKey, MultiDeviceStickerPackOperationJob.Type.INSTALL));
}
});
}

View File

@ -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.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()
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 {