clean up V1 closed group

This commit is contained in:
Ryan ZHAO 2021-02-17 16:09:36 +11:00
parent fde45f755b
commit 20ec889730
53 changed files with 168 additions and 748 deletions

View File

@ -76,7 +76,6 @@ import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase; import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation; import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation;
import org.thoughtcrime.securesms.loki.utilities.Broadcaster; import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
@ -139,8 +138,7 @@ import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
* *
* @author Moxie Marlinspike * @author Moxie Marlinspike
*/ */
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate, public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate {
SessionManagementProtocolDelegate, SharedSenderKeysImplementationDelegate {
private static final String TAG = ApplicationContext.class.getSimpleName(); private static final String TAG = ApplicationContext.class.getSimpleName();
private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB
@ -191,7 +189,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this); SharedSenderKeysDatabase sskDatabase = DatabaseFactory.getSSKDatabase(this);
String userPublicKey = TextSecurePreferences.getLocalNumber(this); String userPublicKey = TextSecurePreferences.getLocalNumber(this);
SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this); SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this);
SharedSenderKeysImplementation.Companion.configureIfNeeded(sskDatabase, this);
MessagingConfiguration.Companion.configure(this, MessagingConfiguration.Companion.configure(this,
DatabaseFactory.getStorage(this), DatabaseFactory.getStorage(this),
sskDatabase, sskDatabase,
@ -203,7 +200,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB); MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
} }
SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, sskDatabase, this);
setUpP2PAPIIfNeeded(); setUpP2PAPIIfNeeded();
PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG); PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG);
setUpStorageAPIIfNeeded(); setUpStorageAPIIfNeeded();
@ -542,7 +538,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this); String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this);
byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey); byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey);
try { try {
File profilePicture = AvatarHelper.getAvatarFile(this, Address.Companion.fromSerialized(userPublicKey)); File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey));
StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length()); StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length());
FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> { FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> {
TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime()); TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime());
@ -569,7 +565,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
String url = TextSecurePreferences.getProfilePictureURL(this); String url = TextSecurePreferences.getProfilePictureURL(this);
String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this); String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this);
if (userMasterDevice != null) { if (userMasterDevice != null) {
Recipient userMasterDeviceAsRecipient = Recipient.from(this, Address.Companion.fromSerialized(userMasterDevice), false).resolve(); Recipient userMasterDeviceAsRecipient = Recipient.from(this, Address.fromSerialized(userMasterDevice), false).resolve();
profileKey = userMasterDeviceAsRecipient.getProfileKey(); profileKey = userMasterDeviceAsRecipient.getProfileKey();
url = userMasterDeviceAsRecipient.getProfileAvatar(); url = userMasterDeviceAsRecipient.getProfileAvatar();
} }
@ -608,25 +604,5 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
Runtime.getRuntime().exit(0); Runtime.getRuntime().exit(0);
} }
// public boolean hasSentSessionRequestExpired(@NotNull String publicKey) {
// LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
// Long timestamp = apiDB.getSessionRequestSentTimestamp(publicKey);
// if (timestamp != null) {
// long expiration = timestamp + TTLUtilities.getTTL(TTLUtilities.MessageType.SessionRequest);
// return new Date().getTime() > expiration;
// } else {
// return false;
// }
// }
@Override
public void sendSessionRequestIfNeeded(@NotNull String publicKey) {
}
@Override
public void requestSenderKey(@NotNull String groupPublicKey, @NotNull String senderPublicKey) {
ClosedGroupsProtocol.requestSenderKey(this, groupPublicKey, senderPublicKey);
}
// endregion // endregion
} }

View File

@ -198,7 +198,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
if (address == null) throw new AssertionError(); if (address == null) throw new AssertionError();
if (locale == null) throw new AssertionError(); if (locale == null) throw new AssertionError();
this.recipient = Recipient.from(getContext(), Address.Companion.fromSerialized(address), true); this.recipient = Recipient.from(getContext(), Address.fromSerialized(address), true);
this.locale = locale; this.locale = locale;
getLoaderManager().initLoader(0, null, this); getLoaderManager().initLoader(0, null, this);

View File

@ -250,7 +250,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override @Override
public void onContactSelected(String number) { public void onContactSelected(String number) {
Recipient recipient = Recipient.from(this, Address.Companion.fromExternal(this, number), true); Recipient recipient = Recipient.from(this, Address.fromExternal(this, number), true);
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient); long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
createConversation(existingThread, recipient.getAddress(), ThreadDatabase.DistributionTypes.DEFAULT); createConversation(existingThread, recipient.getAddress(), ThreadDatabase.DistributionTypes.DEFAULT);
} }

View File

@ -43,7 +43,7 @@ public class ShortcutLauncherActivity extends AppCompatActivity {
return; return;
} }
Address address = Address.Companion.fromSerialized(serializedAddress); Address address = Address.fromSerialized(serializedAddress);
Recipient recipient = Recipient.from(this, address, true); Recipient recipient = Recipient.from(this, address, true);
TaskStackBuilder backStack = TaskStackBuilder.create(this) TaskStackBuilder backStack = TaskStackBuilder.create(this)
.addNextIntent(new Intent(this, HomeActivity.class)); .addNextIntent(new Intent(this, HomeActivity.class));

View File

@ -113,7 +113,7 @@ public class AvatarImageView extends AppCompatImageView {
} }
public void update(String hexEncodedPublicKey) { public void update(String hexEncodedPublicKey) {
Address address = Address.Companion.fromSerialized(hexEncodedPublicKey); Address address = Address.fromSerialized(hexEncodedPublicKey);
Recipient recipient = Recipient.from(getContext(), address, false); Recipient recipient = Recipient.from(getContext(), address, false);
updateAvatar(recipient); updateAvatar(recipient);
} }

View File

@ -85,6 +85,7 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode; import org.greenrobot.eventbus.ThreadMode;
import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.MediaTypes; import org.session.libsession.utilities.MediaTypes;
import org.session.libsignal.libsignal.InvalidMessageException; import org.session.libsignal.libsignal.InvalidMessageException;
import org.session.libsignal.libsignal.util.guava.Optional; import org.session.libsignal.libsignal.util.guava.Optional;
@ -142,7 +143,6 @@ import org.thoughtcrime.securesms.loki.api.PublicChatInfoUpdateWorker;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt; import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
@ -1100,7 +1100,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
String groupPublicKey; String groupPublicKey;
boolean isSSKBasedClosedGroup; boolean isSSKBasedClosedGroup;
try { try {
groupPublicKey = HexEncodingKt.toHexString(ClosedGroupsProtocol.doubleDecodeGroupID(groupRecipient.getAddress().toString())); groupPublicKey = HexEncodingKt.toHexString(GroupUtil.doubleDecodeGroupID(groupRecipient.getAddress().toString()));
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey); isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey);
} catch (IOException e) { } catch (IOException e) {
groupPublicKey = null; groupPublicKey = null;
@ -1110,8 +1110,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (isSSKBasedClosedGroup) { if (isSSKBasedClosedGroup) {
ClosedGroupsProtocolV2.explicitLeave(this, groupPublicKey); ClosedGroupsProtocolV2.explicitLeave(this, groupPublicKey);
initializeEnabledCheck(); initializeEnabledCheck();
} else if (ClosedGroupsProtocol.leaveLegacyGroup(this, groupRecipient)) {
initializeEnabledCheck();
} else { } else {
Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
} }
@ -2062,10 +2060,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
long result = MessageSender.send(context, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id)); long result = MessageSender.send(context, outgoingMessage, threadId, forceSms, () -> fragment.releaseOutgoingMessage(id));
if (!recipient.isGroupRecipient()) {
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
}
sendComplete(result); sendComplete(result);
future.set(null); future.set(null);
@ -2092,10 +2086,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
long result = MessageSender.send(context, message, threadId, false, () -> fragment.releaseOutgoingMessage(id)); long result = MessageSender.send(context, message, threadId, false, () -> fragment.releaseOutgoingMessage(id));
if (!recipient.isGroupRecipient()) {
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
}
sendComplete(result); sendComplete(result);
} }
@ -2517,7 +2507,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Recipient author; Recipient author;
if (messageRecord.isOutgoing()) { if (messageRecord.isOutgoing()) {
author = Recipient.from(this, Address.Companion.fromSerialized(TextSecurePreferences.getLocalNumber(this)), true); author = Recipient.from(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), true);
} else { } else {
author = messageRecord.getIndividualRecipient(); author = messageRecord.getIndividualRecipient();
} }

View File

@ -29,7 +29,7 @@ public class TextSecureSessionStore implements SessionStore {
@Override @Override
public SessionRecord loadSession(@NonNull SignalProtocolAddress address) { public SessionRecord loadSession(@NonNull SignalProtocolAddress address) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.Companion.fromSerialized(address.getName()), address.getDeviceId()); SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId());
if (sessionRecord == null) { if (sessionRecord == null) {
Log.w(TAG, "No existing session information found."); Log.w(TAG, "No existing session information found.");
@ -43,14 +43,14 @@ public class TextSecureSessionStore implements SessionStore {
@Override @Override
public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) { public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
DatabaseFactory.getSessionDatabase(context).store(Address.Companion.fromSerialized(address.getName()), address.getDeviceId(), record); DatabaseFactory.getSessionDatabase(context).store(Address.fromSerialized(address.getName()), address.getDeviceId(), record);
} }
} }
@Override @Override
public boolean containsSession(SignalProtocolAddress address) { public boolean containsSession(SignalProtocolAddress address) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.Companion.fromSerialized(address.getName()), address.getDeviceId()); SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(Address.fromSerialized(address.getName()), address.getDeviceId());
return sessionRecord != null && return sessionRecord != null &&
sessionRecord.getSessionState().hasSenderChain() && sessionRecord.getSessionState().hasSenderChain() &&
@ -61,27 +61,27 @@ public class TextSecureSessionStore implements SessionStore {
@Override @Override
public void deleteSession(SignalProtocolAddress address) { public void deleteSession(SignalProtocolAddress address) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
DatabaseFactory.getSessionDatabase(context).delete(Address.Companion.fromSerialized(address.getName()), address.getDeviceId()); DatabaseFactory.getSessionDatabase(context).delete(Address.fromSerialized(address.getName()), address.getDeviceId());
} }
} }
@Override @Override
public void deleteAllSessions(String name) { public void deleteAllSessions(String name) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
DatabaseFactory.getSessionDatabase(context).deleteAllFor(Address.Companion.fromSerialized(name)); DatabaseFactory.getSessionDatabase(context).deleteAllFor(Address.fromSerialized(name));
} }
} }
@Override @Override
public List<Integer> getSubDeviceSessions(String name) { public List<Integer> getSubDeviceSessions(String name) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
return DatabaseFactory.getSessionDatabase(context).getSubDevices(Address.Companion.fromSerialized(name)); return DatabaseFactory.getSessionDatabase(context).getSubDevices(Address.fromSerialized(name));
} }
} }
public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) { public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) {
synchronized (FILE_LOCK) { synchronized (FILE_LOCK) {
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(Address.Companion.fromSerialized(address.getName())); List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(Address.fromSerialized(address.getName()));
for (SessionDatabase.SessionRow row : sessions) { for (SessionDatabase.SessionRow row : sessions) {
if (row.getDeviceId() != address.getDeviceId()) { if (row.getDeviceId() != address.getDeviceId()) {

View File

@ -129,7 +129,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID}, Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {GROUP_ID},
MEMBERS + " = ? AND " + MMS + " = ?", MEMBERS + " = ? AND " + MMS + " = ?",
new String[] {Address.Companion.toSerializedList(members, ','), "1"}, new String[] {Address.toSerializedList(members, ','), "1"},
null, null, null); null, null, null);
try { try {
if (cursor != null && cursor.moveToNext()) { if (cursor != null && cursor.moveToNext()) {
@ -179,7 +179,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
public boolean isClosedGroupMember(String hexEncodedPublicKey) { public boolean isClosedGroupMember(String hexEncodedPublicKey) {
try { try {
Address address = Address.Companion.fromSerialized(hexEncodedPublicKey); Address address = Address.fromSerialized(hexEncodedPublicKey);
Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups(); Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups();
GroupRecord record; GroupRecord record;
@ -203,7 +203,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(GROUP_ID, groupId); contentValues.put(GROUP_ID, groupId);
contentValues.put(TITLE, title); contentValues.put(TITLE, title);
contentValues.put(MEMBERS, Address.Companion.toSerializedList(members, ',')); contentValues.put(MEMBERS, Address.toSerializedList(members, ','));
if (avatar != null) { if (avatar != null) {
contentValues.put(AVATAR_ID, avatar.getId()); contentValues.put(AVATAR_ID, avatar.getId());
@ -219,12 +219,12 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
contentValues.put(MMS, GroupUtil.isMmsGroup(groupId)); contentValues.put(MMS, GroupUtil.isMmsGroup(groupId));
if (admins != null) { if (admins != null) {
contentValues.put(ADMINS, Address.Companion.toSerializedList(admins, ',')); contentValues.put(ADMINS, Address.toSerializedList(admins, ','));
} }
long threadId = databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues); long threadId = databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
Recipient.applyCached(Address.Companion.fromSerialized(groupId), recipient -> { Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setName(title); recipient.setName(title);
recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null);
recipient.setParticipants(Stream.of(members).map(memberAddress -> Recipient.from(context, memberAddress, true)).toList()); recipient.setParticipants(Stream.of(members).map(memberAddress -> Recipient.from(context, memberAddress, true)).toList());
@ -238,7 +238,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
int result = databaseHelper.getWritableDatabase().delete(TABLE_NAME, GROUP_ID + " = ?", new String[]{groupId}); int result = databaseHelper.getWritableDatabase().delete(TABLE_NAME, GROUP_ID + " = ?", new String[]{groupId});
if (result > 0) { if (result > 0) {
Recipient.removeCached(Address.Companion.fromSerialized(groupId)); Recipient.removeCached(Address.fromSerialized(groupId));
notifyConversationListListeners(); notifyConversationListListeners();
return true; return true;
} else { } else {
@ -262,7 +262,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
GROUP_ID + " = ?", GROUP_ID + " = ?",
new String[] {groupId}); new String[] {groupId});
Recipient.applyCached(Address.Companion.fromSerialized(groupId), recipient -> { Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setName(title); recipient.setName(title);
recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null); recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null);
}); });
@ -277,7 +277,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {groupID}); new String[] {groupID});
Recipient recipient = Recipient.from(context, Address.Companion.fromSerialized(groupID), false); Recipient recipient = Recipient.from(context, Address.fromSerialized(groupID), false);
recipient.setName(newValue); recipient.setName(newValue);
} }
@ -300,20 +300,20 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
new String[] {groupID}); new String[] {groupID});
Recipient.applyCached(Address.Companion.fromSerialized(groupID), recipient -> recipient.setGroupAvatarId(avatarId == 0 ? null : avatarId)); Recipient.applyCached(Address.fromSerialized(groupID), recipient -> recipient.setGroupAvatarId(avatarId == 0 ? null : avatarId));
} }
public void updateMembers(String groupId, List<Address> members) { public void updateMembers(String groupId, List<Address> members) {
Collections.sort(members); Collections.sort(members);
ContentValues contents = new ContentValues(); ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.Companion.toSerializedList(members, ',')); contents.put(MEMBERS, Address.toSerializedList(members, ','));
contents.put(ACTIVE, 1); contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {groupId}); new String[] {groupId});
Recipient.applyCached(Address.Companion.fromSerialized(groupId), recipient -> { Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setParticipants(Stream.of(members).map(a -> Recipient.from(context, a, false)).toList()); recipient.setParticipants(Stream.of(members).map(a -> Recipient.from(context, a, false)).toList());
}); });
} }
@ -322,7 +322,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
Collections.sort(admins); Collections.sort(admins);
ContentValues contents = new ContentValues(); ContentValues contents = new ContentValues();
contents.put(ADMINS, Address.Companion.toSerializedList(admins, ',')); contents.put(ADMINS, Address.toSerializedList(admins, ','));
contents.put(ACTIVE, 1); contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", new String[] {groupId}); databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", new String[] {groupId});
@ -333,12 +333,12 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
currentMembers.remove(source); currentMembers.remove(source);
ContentValues contents = new ContentValues(); ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.Companion.toSerializedList(currentMembers, ',')); contents.put(MEMBERS, Address.toSerializedList(currentMembers, ','));
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {groupId}); new String[] {groupId});
Recipient.applyCached(Address.Companion.fromSerialized(groupId), recipient -> { Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
List<Recipient> current = recipient.getParticipants(); List<Recipient> current = recipient.getParticipants();
Recipient removal = Recipient.from(context, source, false); Recipient removal = Recipient.from(context, source, false);
@ -358,7 +358,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)); String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS));
return Address.Companion.fromSerializedList(serializedMembers, ','); return Address.fromSerializedList(serializedMembers, ',');
} }
return new LinkedList<>(); return new LinkedList<>();

View File

@ -82,7 +82,7 @@ public class GroupReceiptDatabase extends Database {
try (Cursor cursor = db.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {String.valueOf(mmsId)}, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, null, MMS_ID + " = ?", new String[] {String.valueOf(mmsId)}, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
results.add(new GroupReceiptInfo(Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))), results.add(new GroupReceiptInfo(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))),
cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)), cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)),
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)), cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1)); cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1));

View File

@ -108,7 +108,7 @@ public class MediaDatabase extends Database {
Address address = null; Address address = null;
if (serializedAddress != null) { if (serializedAddress != null) {
address = Address.Companion.fromSerialized(serializedAddress); address = Address.fromSerialized(serializedAddress);
} }
long date; long date;

View File

@ -268,7 +268,7 @@ public class MmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) { if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX)))) {
Address theirAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress(); Address ourAddress = messageId.getAddress();
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT; String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
@ -333,7 +333,7 @@ public class MmsDatabase extends MessagingDatabase {
String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null String fromString = notification.getFrom() != null && notification.getFrom().getTextString() != null
? Util.toIsoString(notification.getFrom().getTextString()) ? Util.toIsoString(notification.getFrom().getTextString())
: ""; : "";
Recipient recipient = Recipient.from(context, Address.Companion.fromExternal(context, fromString), false); Recipient recipient = Recipient.from(context, Address.fromExternal(context, fromString), false);
return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); return DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
} }
@ -500,7 +500,7 @@ public class MmsDatabase extends MessagingDatabase {
while(cursor != null && cursor.moveToNext()) { while(cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(3))) { if (Types.isSecureType(cursor.getLong(3))) {
SyncMessageId syncMessageId = new SyncMessageId(Address.Companion.fromSerialized(cursor.getString(1)), cursor.getLong(2)); SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2));
ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), true); ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), true);
result.add(new MarkedMessageInfo(syncMessageId, expirationInfo)); result.add(new MarkedMessageInfo(syncMessageId, expirationInfo));
@ -529,7 +529,7 @@ public class MmsDatabase extends MessagingDatabase {
cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null); cursor = database.query(TABLE_NAME, new String[] {ID, THREAD_ID, MESSAGE_BOX, EXPIRES_IN, EXPIRE_STARTED, ADDRESS}, DATE_SENT + " = ?", new String[] {String.valueOf(messageId.getTimetamp())}, null, null, null, null);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
Address theirAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); Address theirAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
Address ourAddress = messageId.getAddress(); Address ourAddress = messageId.getAddress();
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) { if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
@ -642,13 +642,13 @@ public class MmsDatabase extends MessagingDatabase {
.filterNot(previewAttachments::contains) .filterNot(previewAttachments::contains)
.map(a -> (Attachment)a).toList(); .map(a -> (Attachment)a).toList();
Recipient recipient = Recipient.from(context, Address.Companion.fromSerialized(address), false); Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false);
List<NetworkFailure> networkFailures = new LinkedList<>(); List<NetworkFailure> networkFailures = new LinkedList<>();
List<IdentityKeyMismatch> mismatches = new LinkedList<>(); List<IdentityKeyMismatch> mismatches = new LinkedList<>();
QuoteModel quote = null; QuoteModel quote = null;
if (quoteId > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) { if (quoteId > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) {
quote = new QuoteModel(quoteId, Address.Companion.fromSerialized(quoteAuthor), quoteText, quoteMissing, quoteAttachments); quote = new QuoteModel(quoteId, Address.fromSerialized(quoteAuthor), quoteText, quoteMissing, quoteAttachments);
} }
if (!TextUtils.isEmpty(mismatchDocument)) { if (!TextUtils.isEmpty(mismatchDocument)) {
@ -955,7 +955,7 @@ public class MmsDatabase extends MessagingDatabase {
contentBuilder.add(MESSAGE_TYPE, notification.getMessageType()); contentBuilder.add(MESSAGE_TYPE, notification.getMessageType());
if (notification.getFrom() != null) { if (notification.getFrom() != null) {
contentValues.put(ADDRESS, Address.Companion.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())).serialize()); contentValues.put(ADDRESS, Address.fromExternal(context, Util.toIsoString(notification.getFrom().getTextString())).serialize());
} }
contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE); contentValues.put(MESSAGE_BOX, Types.BASE_INBOX_TYPE);
@ -1334,7 +1334,7 @@ public class MmsDatabase extends MessagingDatabase {
private final int subscriptionId; private final int subscriptionId;
MmsNotificationInfo(@Nullable String from, String contentLocation, String transactionId, int subscriptionId) { MmsNotificationInfo(@Nullable String from, String contentLocation, String transactionId, int subscriptionId) {
this.from = from == null ? null : Address.Companion.fromSerialized(from); this.from = from == null ? null : Address.fromSerialized(from);
this.contentLocation = contentLocation; this.contentLocation = contentLocation;
this.transactionId = transactionId; this.transactionId = transactionId;
this.subscriptionId = subscriptionId; this.subscriptionId = subscriptionId;
@ -1507,7 +1507,7 @@ public class MmsDatabase extends MessagingDatabase {
if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) { if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) {
address = Address.Companion.getUNKNOWN(); address = Address.Companion.getUNKNOWN();
} else { } else {
address = Address.Companion.fromSerialized(serialized); address = Address.fromSerialized(serialized);
} }
return Recipient.from(context, address, true); return Recipient.from(context, address, true);
@ -1554,7 +1554,7 @@ public class MmsDatabase extends MessagingDatabase {
SlideDeck quoteDeck = new SlideDeck(context, quoteAttachments); SlideDeck quoteDeck = new SlideDeck(context, quoteAttachments);
if (quoteId > 0 && !TextUtils.isEmpty(quoteAuthor)) { if (quoteId > 0 && !TextUtils.isEmpty(quoteAuthor)) {
return new Quote(quoteId, Address.Companion.fromExternal(context, quoteAuthor), quoteText, quoteMissing, quoteDeck); return new Quote(quoteId, Address.fromExternal(context, quoteAuthor), quoteText, quoteMissing, quoteDeck);
} else { } else {
return null; return null;
} }

View File

@ -330,7 +330,7 @@ public class RecipientDatabase extends Database {
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, null, null, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
results.add(Address.Companion.fromExternal(context, cursor.getString(0))); results.add(Address.fromExternal(context, cursor.getString(0)));
} }
} }
@ -370,7 +370,7 @@ public class RecipientDatabase extends Database {
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, REGISTERED + " = ?", new String[] {"1"}, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, REGISTERED + " = ?", new String[] {"1"}, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
results.add(Address.Companion.fromSerialized(cursor.getString(0))); results.add(Address.fromSerialized(cursor.getString(0)));
} }
} }
@ -383,7 +383,7 @@ public class RecipientDatabase extends Database {
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
results.add(Address.Companion.fromSerialized(cursor.getString(0))); results.add(Address.fromSerialized(cursor.getString(0)));
} }
} }
@ -397,7 +397,7 @@ public class RecipientDatabase extends Database {
db.beginTransaction(); db.beginTransaction();
try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) { try (Cursor cursor = db.query(TABLE_NAME, new String[] {ADDRESS, COLOR, SYSTEM_DISPLAY_NAME}, SYSTEM_DISPLAY_NAME + " IS NOT NULL AND " + SYSTEM_DISPLAY_NAME + " != \"\"", null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
Address address = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)), MaterialColor newColor = updater.update(cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(COLOR))); cursor.getString(cursor.getColumnIndexOrThrow(COLOR)));
@ -497,7 +497,7 @@ public class RecipientDatabase extends Database {
public @NonNull Recipient getCurrent() { public @NonNull Recipient getCurrent() {
String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)); String serialized = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
return Recipient.from(context, Address.Companion.fromSerialized(serialized), false); return Recipient.from(context, Address.fromSerialized(serialized), false);
} }
public @Nullable Recipient getNext() { public @Nullable Recipient getNext() {

View File

@ -102,7 +102,7 @@ public class SessionDatabase extends Database {
try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) { try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
try { try {
results.add(new SessionRow(Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))), results.add(new SessionRow(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))),
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)), cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)),
new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))))); new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD)))));
} catch (IOException e) { } catch (IOException e) {

View File

@ -377,7 +377,7 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
Address theirAddress = messageId.getAddress(); Address theirAddress = messageId.getAddress();
Address ourAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT; String columnName = deliveryReceipt ? DELIVERY_RECEIPT_COUNT : READ_RECEIPT_COUNT;
if (ourAddress.equals(theirAddress)) { if (ourAddress.equals(theirAddress)) {
@ -418,7 +418,7 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
Address theirAddress = messageId.getAddress(); Address theirAddress = messageId.getAddress();
Address ourAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); Address ourAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
if (ourAddress.equals(theirAddress)) { if (ourAddress.equals(theirAddress)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
@ -469,7 +469,7 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor != null && cursor.moveToNext()) { while (cursor != null && cursor.moveToNext()) {
if (Types.isSecureType(cursor.getLong(3))) { if (Types.isSecureType(cursor.getLong(3))) {
SyncMessageId syncMessageId = new SyncMessageId(Address.Companion.fromSerialized(cursor.getString(1)), cursor.getLong(2)); SyncMessageId syncMessageId = new SyncMessageId(Address.fromSerialized(cursor.getString(1)), cursor.getLong(2));
ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), false); ExpirationInfo expirationInfo = new ExpirationInfo(cursor.getLong(0), cursor.getLong(4), cursor.getLong(5), false);
results.add(new MarkedMessageInfo(syncMessageId, expirationInfo)); results.add(new MarkedMessageInfo(syncMessageId, expirationInfo));
@ -937,7 +937,7 @@ public class SmsDatabase extends MessagingDatabase {
public SmsMessageRecord getCurrent() { public SmsMessageRecord getCurrent() {
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID)); long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
Address address = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS))); Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID)); int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS_DEVICE_ID));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE)); long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED)); long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED));

View File

@ -90,7 +90,7 @@ public class SmsMigrator {
long threadId, SQLiteStatement statement) long threadId, SQLiteStatement statement)
{ {
String theirAddress = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS)); String theirAddress = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
statement.bindString(1, Address.Companion.fromExternal(context, theirAddress).serialize()); statement.bindString(1, Address.fromExternal(context, theirAddress).serialize());
addIntToStatement(statement, cursor, 2, SmsDatabase.PERSON); addIntToStatement(statement, cursor, 2, SmsDatabase.PERSON);
addIntToStatement(statement, cursor, 3, SmsDatabase.DATE_RECEIVED); addIntToStatement(statement, cursor, 3, SmsDatabase.DATE_RECEIVED);
@ -137,7 +137,7 @@ public class SmsMigrator {
String address = getTheirCanonicalAddress(context, theirRecipientId); String address = getTheirCanonicalAddress(context, theirRecipientId);
if (address != null) { if (address != null) {
recipientList.add(Recipient.from(context, Address.Companion.fromExternal(context, address), true)); recipientList.add(Recipient.from(context, Address.fromExternal(context, address), true));
} }
} }
@ -213,7 +213,7 @@ public class SmsMigrator {
long ourThreadId = threadDatabase.getOrCreateThreadIdFor(ourRecipients.iterator().next()); long ourThreadId = threadDatabase.getOrCreateThreadIdFor(ourRecipients.iterator().next());
migrateConversation(context, listener, progress, theirThreadId, ourThreadId); migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
} else if (ourRecipients.size() > 1) { } else if (ourRecipients.size() > 1) {
ourRecipients.add(Recipient.from(context, Address.Companion.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true)); ourRecipients.add(Recipient.from(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), true));
List<Address> memberAddresses = new LinkedList<>(); List<Address> memberAddresses = new LinkedList<>();
@ -222,7 +222,7 @@ public class SmsMigrator {
} }
String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(memberAddresses, null); String ourGroupId = DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(memberAddresses, null);
Recipient ourGroupRecipient = Recipient.from(context, Address.Companion.fromSerialized(ourGroupId), true); Recipient ourGroupRecipient = Recipient.from(context, Address.fromSerialized(ourGroupId), true);
long ourThreadId = threadDatabase.getOrCreateThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION); long ourThreadId = threadDatabase.getOrCreateThreadIdFor(ourGroupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
migrateConversation(context, listener, progress, theirThreadId, ourThreadId); migrateConversation(context, listener, progress, theirThreadId, ourThreadId);

View File

@ -556,7 +556,7 @@ public class ThreadDatabase extends Database {
cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null); cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
Address address = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))); Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS)));
addressCache.put(threadId, address); addressCache.put(threadId, address);
return Recipient.from(context, address, false); return Recipient.from(context, address, false);
} }
@ -735,7 +735,7 @@ public class ThreadDatabase extends Database {
public ThreadRecord getCurrent() { public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE)); int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
Address address = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS))); Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS)));
Optional<RecipientSettings> settings; Optional<RecipientSettings> settings;
Optional<GroupRecord> groupRecord; Optional<GroupRecord> groupRecord;

View File

@ -41,7 +41,7 @@ public class IdentityKeyMismatch {
@JsonIgnore @JsonIgnore
public Address getAddress() { public Address getAddress() {
return Address.Companion.fromSerialized(address); return Address.fromSerialized(address);
} }
public IdentityKey getIdentityKey() { public IdentityKey getIdentityKey() {

View File

@ -18,7 +18,7 @@ public class NetworkFailure {
@JsonIgnore @JsonIgnore
public Address getAddress() { public Address getAddress() {
return Address.Companion.fromSerialized(address); return Address.fromSerialized(address);
} }
@Override @Override

View File

@ -73,7 +73,7 @@ public class ConversationListLoader extends AbstractCursorLoader {
List<Address> addresses = new LinkedList<>(); List<Address> addresses = new LinkedList<>();
for (String number : numbers) { for (String number : numbers) {
addresses.add(Address.Companion.fromExternal(context, number)); addresses.add(Address.fromExternal(context, number));
} }
return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(addresses); return DatabaseFactory.getThreadDatabase(context).getFilteredConversationList(addresses);

View File

@ -44,7 +44,7 @@ public class GroupManager {
} }
public static long getThreadIDFromGroupID(String groupID, @NonNull Context context) { public static long getThreadIDFromGroupID(String groupID, @NonNull Context context) {
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupID), false); final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupID), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient); return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
} }
@ -69,14 +69,14 @@ public class GroupManager {
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final String groupId = GroupUtil.getEncodedClosedGroupID(id.getBytes()); // TODO: The group id is double encoded here 2 final String groupId = GroupUtil.getEncodedClosedGroupID(id.getBytes()); // TODO: The group id is double encoded here 2
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), false); final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
final Set<Address> memberAddresses = getMemberAddresses(members); final Set<Address> memberAddresses = getMemberAddresses(members);
final Set<Address> adminAddresses = getMemberAddresses(admins); final Set<Address> adminAddresses = getMemberAddresses(admins);
String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context); String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context); String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context);
memberAddresses.add(Address.Companion.fromSerialized(masterPublicKey)); memberAddresses.add(Address.fromSerialized(masterPublicKey));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses), System.currentTimeMillis()); groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses), System.currentTimeMillis());
groupDatabase.updateProfilePicture(groupId, avatarBytes); groupDatabase.updateProfilePicture(groupId, avatarBytes);
@ -100,10 +100,10 @@ public class GroupManager {
{ {
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), false); final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
final Set<Address> memberAddresses = new HashSet<>(); final Set<Address> memberAddresses = new HashSet<>();
memberAddresses.add(Address.Companion.fromSerialized(Objects.requireNonNull(TextSecurePreferences.getLocalNumber(context)))); memberAddresses.add(Address.fromSerialized(Objects.requireNonNull(TextSecurePreferences.getLocalNumber(context))));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(), System.currentTimeMillis()); groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(), System.currentTimeMillis());
groupDatabase.updateProfilePicture(groupId, avatarBytes); groupDatabase.updateProfilePicture(groupId, avatarBytes);
@ -118,7 +118,7 @@ public class GroupManager {
{ {
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
final ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); final ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
final Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), false); final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
if (!groupDatabase.getGroup(groupId).isPresent()) { if (!groupDatabase.getGroup(groupId).isPresent()) {
return false; return false;
@ -145,7 +145,7 @@ public class GroupManager {
final Set<Address> adminAddresses = getMemberAddresses(admins); final Set<Address> adminAddresses = getMemberAddresses(admins);
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar); final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
memberAddresses.add(Address.Companion.fromSerialized(TextSecurePreferences.getLocalNumber(context))); memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses)); groupDatabase.updateMembers(groupId, new LinkedList<>(memberAddresses));
groupDatabase.updateAdmins(groupId, new LinkedList<>(adminAddresses)); groupDatabase.updateAdmins(groupId, new LinkedList<>(adminAddresses));
groupDatabase.updateTitle(groupId, name); groupDatabase.updateTitle(groupId, name);
@ -154,7 +154,7 @@ public class GroupManager {
if (!GroupUtil.isMmsGroup(groupId)) { if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses); return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
} else { } else {
Recipient groupRecipient = Recipient.from(context, Address.Companion.fromSerialized(groupId), true); Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), true);
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient); long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(groupRecipient);
return new GroupActionResult(groupRecipient, threadId); return new GroupActionResult(groupRecipient, threadId);
} }
@ -168,7 +168,7 @@ public class GroupManager {
@NonNull Set<Address> admins) @NonNull Set<Address> admins)
{ {
Attachment avatarAttachment = null; Attachment avatarAttachment = null;
Address groupAddress = Address.Companion.fromSerialized(groupId); Address groupAddress = Address.fromSerialized(groupId);
Recipient groupRecipient = Recipient.from(context, groupAddress, false); Recipient groupRecipient = Recipient.from(context, groupAddress, false);
List<String> numbers = new LinkedList<>(); List<String> numbers = new LinkedList<>();

View File

@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob; import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.session.libsignal.utilities.logging.Log; import org.session.libsignal.utilities.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage; import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
@ -96,24 +95,20 @@ public class GroupMessageProcessor {
if (group.getMembers().isPresent()) { if (group.getMembers().isPresent()) {
for (String member : group.getMembers().get()) { for (String member : group.getMembers().get()) {
members.add(Address.Companion.fromExternal(context, member)); members.add(Address.fromExternal(context, member));
} }
} }
// Loki - Parse admins // Loki - Parse admins
if (group.getAdmins().isPresent()) { if (group.getAdmins().isPresent()) {
for (String admin : group.getAdmins().get()) { for (String admin : group.getAdmins().get()) {
admins.add(Address.Companion.fromExternal(context, admin)); admins.add(Address.fromExternal(context, admin));
} }
} }
database.create(id, group.getName().orNull(), members, database.create(id, group.getName().orNull(), members,
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins, formationTimestamp); avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins, formationTimestamp);
if (group.getMembers().isPresent()) {
ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
}
return storeMessage(context, content, group, builder.build(), outgoing); return storeMessage(context, content, group, builder.build(), outgoing);
} }
@ -127,7 +122,7 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context); GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
String id = GroupUtil.getEncodedId(group); String id = GroupUtil.getEncodedId(group);
Address address = Address.Companion.fromExternal(context, GroupUtil.getEncodedId(group)); Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group));
Recipient recipient = Recipient.from(context, address, false); Recipient recipient = Recipient.from(context, address, false);
String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
@ -140,13 +135,13 @@ public class GroupMessageProcessor {
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
// Loki - Only update the group if the group admin sent the message // Loki - Only update the group if the group admin sent the message
if (!groupRecord.getAdmins().contains(Address.Companion.fromSerialized(content.getSender()))) { if (!groupRecord.getAdmins().contains(Address.fromSerialized(content.getSender()))) {
Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring."); Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring.");
return null; return null;
} }
// Loki - Only process update messages if we're part of the group // Loki - Only process update messages if we're part of the group
Address userMasterDeviceAddress = Address.Companion.fromSerialized(userMasterDevice); Address userMasterDeviceAddress = Address.fromSerialized(userMasterDevice);
if (!groupRecord.getMembers().contains(userMasterDeviceAddress) && if (!groupRecord.getMembers().contains(userMasterDeviceAddress) &&
!group.getMembers().or(Collections.emptyList()).contains(userMasterDevice)) { !group.getMembers().or(Collections.emptyList()).contains(userMasterDevice)) {
Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring."); Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring.");
@ -159,7 +154,7 @@ public class GroupMessageProcessor {
Set<Address> newMembers = new HashSet<>(); Set<Address> newMembers = new HashSet<>();
for (String messageMember : group.getMembers().get()) { for (String messageMember : group.getMembers().get()) {
newMembers.add(Address.Companion.fromExternal(context, messageMember)); newMembers.add(Address.fromExternal(context, messageMember));
} }
// Added members are the members who are present in newMembers but not in currentMembers // Added members are the members who are present in newMembers but not in currentMembers
@ -198,14 +193,10 @@ public class GroupMessageProcessor {
} }
// If we were removed then we need to disable the chat // If we were removed then we need to disable the chat
if (removedMembers.contains(Address.Companion.fromSerialized(userMasterDevice))) { if (removedMembers.contains(Address.fromSerialized(userMasterDevice))) {
database.setActive(id, false); database.setActive(id, false);
} else { } else {
if (!groupRecord.isActive()) database.setActive(id, true); if (!groupRecord.isActive()) database.setActive(id, true);
if (group.getMembers().isPresent()) {
ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
}
} }
return storeMessage(context, content, group, builder.build(), outgoing); return storeMessage(context, content, group, builder.build(), outgoing);
@ -216,7 +207,7 @@ public class GroupMessageProcessor {
@NonNull SignalServiceGroup group, @NonNull SignalServiceGroup group,
@NonNull GroupRecord record) @NonNull GroupRecord record)
{ {
if (record.getMembers().contains(Address.Companion.fromSerialized(content.getSender()))) { if (record.getMembers().contains(Address.fromSerialized(content.getSender()))) {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId())); .add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
@ -237,8 +228,8 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group); GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT); builder.setType(GroupContext.Type.QUIT);
if (members.contains(Address.Companion.fromExternal(context, content.getSender()))) { if (members.contains(Address.fromExternal(context, content.getSender()))) {
database.removeMember(id, Address.Companion.fromExternal(context, content.getSender())); database.removeMember(id, Address.fromExternal(context, content.getSender()));
if (outgoing) database.setActive(id, false); if (outgoing) database.setActive(id, false);
return storeMessage(context, content, group, builder.build(), outgoing); return storeMessage(context, content, group, builder.build(), outgoing);
@ -262,11 +253,11 @@ public class GroupMessageProcessor {
try { try {
if (outgoing) { if (outgoing) {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
Address address = Address.Companion.fromExternal(context, GroupUtil.getEncodedId(group)); Address address = Address.fromExternal(context, GroupUtil.getEncodedId(group));
Recipient recipient = Recipient.from(context, address, false); Recipient recipient = Recipient.from(context, address, false);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, 0, null, Collections.emptyList(), Collections.emptyList()); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, 0, null, Collections.emptyList(), Collections.emptyList());
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
Address senderAddress = Address.Companion.fromExternal(context, content.getSender()); Address senderAddress = Address.fromExternal(context, content.getSender());
MessageRecord existingMessage = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(content.getTimestamp(), senderAddress); MessageRecord existingMessage = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(content.getTimestamp(), senderAddress);
long messageId; long messageId;
if (existingMessage != null) { if (existingMessage != null) {
@ -281,7 +272,7 @@ public class GroupMessageProcessor {
} else { } else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
String body = Base64.encodeBytes(storage.toByteArray()); String body = Base64.encodeBytes(storage.toByteArray());
IncomingTextMessage incoming = new IncomingTextMessage(Address.Companion.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt()); IncomingTextMessage incoming = new IncomingTextMessage(Address.fromExternal(context, content.getSender()), content.getSenderDevice(), content.getTimestamp(), body, Optional.of(group), 0, content.isNeedsReceipt());
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body);
Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage); Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(groupMessage);

View File

@ -163,7 +163,7 @@ public class AttachmentUploadJob extends BaseJob implements InjectableType {
public static final class Factory implements Job.Factory<AttachmentUploadJob> { public static final class Factory implements Job.Factory<AttachmentUploadJob> {
@Override @Override
public @NonNull AttachmentUploadJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull AttachmentUploadJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)), Address.Companion.fromSerialized(data.getString(KEY_DESTINATION))); return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)), Address.fromSerialized(data.getString(KEY_DESTINATION)));
} }
} }
} }

View File

@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
import org.thoughtcrime.securesms.loki.api.PrepareAttachmentAudioExtrasJob; import org.thoughtcrime.securesms.loki.api.PrepareAttachmentAudioExtrasJob;
import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob; import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJobV2; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJobV2;
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob; import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
@ -32,7 +31,6 @@ public final class JobManagerFactories {
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory()); put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory()); put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory()); put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
put(ClosedGroupUpdateMessageSendJob.KEY, new ClosedGroupUpdateMessageSendJob.Factory());
put(ClosedGroupUpdateMessageSendJobV2.KEY, new ClosedGroupUpdateMessageSendJobV2.Factory()); put(ClosedGroupUpdateMessageSendJobV2.KEY, new ClosedGroupUpdateMessageSendJobV2.Factory());
put(LocalBackupJob.KEY, new LocalBackupJob.Factory()); put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory()); put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());

View File

@ -206,7 +206,7 @@ public class MmsDownloadJob extends BaseJob {
Address from; Address from;
if (retrieved.getFrom() != null) { if (retrieved.getFrom() != null) {
from = Address.Companion.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString())); from = Address.fromExternal(context, Util.toIsoString(retrieved.getFrom().getTextString()));
} else if (notificationFrom != null) { } else if (notificationFrom != null) {
from = notificationFrom; from = notificationFrom;
} else { } else {
@ -215,18 +215,18 @@ public class MmsDownloadJob extends BaseJob {
if (retrieved.getTo() != null) { if (retrieved.getTo() != null) {
for (EncodedStringValue toValue : retrieved.getTo()) { for (EncodedStringValue toValue : retrieved.getTo()) {
members.add(Address.Companion.fromExternal(context, Util.toIsoString(toValue.getTextString()))); members.add(Address.fromExternal(context, Util.toIsoString(toValue.getTextString())));
} }
} }
if (retrieved.getCc() != null) { if (retrieved.getCc() != null) {
for (EncodedStringValue ccValue : retrieved.getCc()) { for (EncodedStringValue ccValue : retrieved.getCc()) {
members.add(Address.Companion.fromExternal(context, Util.toIsoString(ccValue.getTextString()))); members.add(Address.fromExternal(context, Util.toIsoString(ccValue.getTextString())));
} }
} }
members.add(from); members.add(from);
members.add(Address.Companion.fromExternal(context, TextSecurePreferences.getLocalNumber(context))); members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context)));
if (retrieved.getBody() != null) { if (retrieved.getBody() != null) {
body = PartParser.getMessageText(retrieved.getBody()); body = PartParser.getMessageText(retrieved.getBody());
@ -249,7 +249,7 @@ public class MmsDownloadJob extends BaseJob {
} }
if (members.size() > 2) { if (members.size() > 2) {
group = Optional.of(Address.Companion.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), new LinkedList<>()))); group = Optional.of(Address.fromSerialized(DatabaseFactory.getGroupDatabase(context).getOrCreateGroupForMembers(new LinkedList<>(members), new LinkedList<>())));
} }
IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false); IncomingMediaMessage message = new IncomingMediaMessage(from, group, body, retrieved.getDate() * 1000L, attachments, subscriptionId, 0, false, false);

View File

@ -102,7 +102,7 @@ public class MmsReceiveJob extends BaseJob {
private boolean isBlocked(GenericPdu pdu) { private boolean isBlocked(GenericPdu pdu) {
if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) { if (pdu.getFrom() != null && pdu.getFrom().getTextString() != null) {
Recipient recipients = Recipient.from(context, Address.Companion.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString())), false); Recipient recipients = Recipient.from(context, Address.fromExternal(context, Util.toIsoString(pdu.getFrom().getTextString())), false);
return recipients.isBlocked(); return recipients.isBlocked();
} }

View File

@ -68,7 +68,6 @@ import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor; import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.linkpreview.Link; import org.thoughtcrime.securesms.linkpreview.Link;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.session.libsignal.utilities.logging.Log; import org.session.libsignal.utilities.logging.Log;
@ -77,7 +76,6 @@ import org.thoughtcrime.securesms.loki.api.SessionProtocolImpl;
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2; import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2;
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol; import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol; import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
@ -292,7 +290,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SessionMetaProtocol.handleProfileKeyUpdate(context, content); SessionMetaProtocol.handleProfileKeyUpdate(context, content);
} }
if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.Companion.fromSerialized(content.getSender()))) { if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) {
handleNeedsDeliveryReceipt(content, message); handleNeedsDeliveryReceipt(content, message);
} }
} else if (content.getSyncMessage().isPresent()) { } else if (content.getSyncMessage().isPresent()) {
@ -308,7 +306,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
Log.w(TAG, "Got unrecognized message..."); Log.w(TAG, "Got unrecognized message...");
} }
resetRecipientToPush(Recipient.from(context, Address.Companion.fromSerialized(content.getSender()), false)); resetRecipientToPush(Recipient.from(context, Address.fromSerialized(content.getSender()), false));
// if (envelope.isPreKeySignalMessage()) { // if (envelope.isPreKeySignalMessage()) {
// ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); // ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
@ -351,7 +349,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull Optional<Long> smsMessageId) @NonNull Optional<Long> smsMessageId)
{ {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.Companion.fromSerialized(content.getSender()), IncomingTextMessage incomingTextMessage = new IncomingTextMessage(Address.fromSerialized(content.getSender()),
content.getSenderDevice(), content.getSenderDevice(),
content.getTimestamp(), content.getTimestamp(),
"", Optional.absent(), 0, "", Optional.absent(), 0,
@ -462,7 +460,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (syncTarget != null && !syncTarget.isEmpty() || TextSecurePreferences.getLocalNumber(context).equals(content.getSender())) { if (syncTarget != null && !syncTarget.isEmpty() || TextSecurePreferences.getLocalNumber(context).equals(content.getSender())) {
Address targetAddress = masterRecipient.getAddress(); Address targetAddress = masterRecipient.getAddress();
if (message.getGroupInfo().isPresent()) { if (message.getGroupInfo().isPresent()) {
targetAddress = Address.Companion.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get())); targetAddress = Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get()));
} else if (syncTarget != null && !syncTarget.isEmpty()) { } else if (syncTarget != null && !syncTarget.isEmpty()) {
targetAddress = Address.fromSerialized(syncTarget); targetAddress = Address.fromSerialized(syncTarget);
} }
@ -723,7 +721,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} else if (syncTarget != null && !syncTarget.isEmpty() || TextSecurePreferences.getLocalNumber(context).equals(content.getSender())) { } else if (syncTarget != null && !syncTarget.isEmpty() || TextSecurePreferences.getLocalNumber(context).equals(content.getSender())) {
Address targetAddress = masterRecipient.getAddress(); Address targetAddress = masterRecipient.getAddress();
if (message.getGroupInfo().isPresent()) { if (message.getGroupInfo().isPresent()) {
targetAddress = Address.Companion.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get())); targetAddress = Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get()));
} else if (syncTarget != null && !syncTarget.isEmpty()) { } else if (syncTarget != null && !syncTarget.isEmpty()) {
targetAddress = Address.fromSerialized(syncTarget); targetAddress = Address.fromSerialized(syncTarget);
} }
@ -924,7 +922,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
if (canRecoverAutomatically(e)) { if (canRecoverAutomatically(e)) {
Recipient recipient = Recipient.from(context, Address.Companion.fromSerialized(sender), false); Recipient recipient = Recipient.from(context, Address.fromSerialized(sender), false);
LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context); LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(context);
long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient); long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
threadDB.addSessionRestoreDevice(threadID, sender); threadDB.addSessionRestoreDevice(threadID, sender);
@ -965,9 +963,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
smsDatabase.markAsNoSession(smsMessageId.get()); smsDatabase.markAsNoSession(smsMessageId.get());
} }
} }
// Attempt to recover automatically
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(sender);
} }
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp, private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
@ -1008,7 +1003,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{ {
ApplicationContext.getInstance(context) ApplicationContext.getInstance(context)
.getJobManager() .getJobManager()
.add(new SendDeliveryReceiptJob(Address.Companion.fromSerialized(content.getSender()), message.getTimestamp())); .add(new SendDeliveryReceiptJob(Address.fromSerialized(content.getSender()), message.getTimestamp()));
} }
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
@ -1016,7 +1011,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull SignalServiceReceiptMessage message) @NonNull SignalServiceReceiptMessage message)
{ {
// Redirect message to master device conversation // Redirect message to master device conversation
Address masterAddress = Address.Companion.fromSerialized(content.getSender()); Address masterAddress = Address.fromSerialized(content.getSender());
if (masterAddress.isContact()) { if (masterAddress.isContact()) {
Recipient masterRecipient = getMessageMasterDestination(content.getSender()); Recipient masterRecipient = getMessageMasterDestination(content.getSender());
@ -1037,7 +1032,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (TextSecurePreferences.isReadReceiptsEnabled(context)) { if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
// Redirect message to master device conversation // Redirect message to master device conversation
Address masterAddress = Address.Companion.fromSerialized(content.getSender()); Address masterAddress = Address.fromSerialized(content.getSender());
if (masterAddress.isContact()) { if (masterAddress.isContact()) {
Recipient masterRecipient = getMessageMasterDestination(content.getSender()); Recipient masterRecipient = getMessageMasterDestination(content.getSender());
@ -1060,13 +1055,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return; return;
} }
Recipient author = Recipient.from(context, Address.Companion.fromSerialized(content.getSender()), false); Recipient author = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
long threadId; long threadId;
if (typingMessage.getGroupId().isPresent()) { if (typingMessage.getGroupId().isPresent()) {
// Typing messages should only apply to closed groups, thus we use `getEncodedId` // Typing messages should only apply to closed groups, thus we use `getEncodedId`
Address groupAddress = Address.Companion.fromSerialized(GroupUtil.getEncodedClosedGroupID(typingMessage.getGroupId().get())); Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedClosedGroupID(typingMessage.getGroupId().get()));
Recipient groupRecipient = Recipient.from(context, groupAddress, false); Recipient groupRecipient = Recipient.from(context, groupAddress, false);
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient); threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
@ -1103,7 +1098,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return Optional.absent(); return Optional.absent();
} }
Address author = Address.Companion.fromSerialized(quote.get().getAuthor().getNumber()); Address author = Address.fromSerialized(quote.get().getAuthor().getNumber());
MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author); MessageRecord message = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(quote.get().getId(), author);
if (message != null) { if (message != null) {
@ -1218,49 +1213,49 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
private Recipient getSyncMessageDestination(SentTranscriptMessage message) { private Recipient getSyncMessageDestination(SentTranscriptMessage message) {
if (message.getMessage().isGroupMessage()) { if (message.getMessage().isGroupMessage()) {
return Recipient.from(context, Address.Companion.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false);
} else { } else {
return Recipient.from(context, Address.Companion.fromSerialized(message.getDestination().get()), false); return Recipient.from(context, Address.fromSerialized(message.getDestination().get()), false);
} }
} }
private Recipient getSyncMessageMasterDestination(SentTranscriptMessage message) { private Recipient getSyncMessageMasterDestination(SentTranscriptMessage message) {
if (message.getMessage().isGroupMessage()) { if (message.getMessage().isGroupMessage()) {
return Recipient.from(context, Address.Companion.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false); return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false);
} else { } else {
String publicKey = message.getDestination().get(); String publicKey = message.getDestination().get();
String userPublicKey = TextSecurePreferences.getLocalNumber(context); String userPublicKey = TextSecurePreferences.getLocalNumber(context);
if (publicKey.equals(userPublicKey)) { if (publicKey.equals(userPublicKey)) {
return Recipient.from(context, Address.Companion.fromSerialized(userPublicKey), false); return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
} else { } else {
return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); return Recipient.from(context, Address.fromSerialized(publicKey), false);
} }
} }
} }
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) { private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
if (message.getGroupInfo().isPresent()) { if (message.getGroupInfo().isPresent()) {
return Recipient.from(context, Address.Companion.fromExternal(context, GroupUtil.getEncodedClosedGroupID(message.getGroupInfo().get().getGroupId())), false); return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedClosedGroupID(message.getGroupInfo().get().getGroupId())), false);
} else { } else {
return Recipient.from(context, Address.Companion.fromExternal(context, content.getSender()), false); return Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
} }
} }
private Recipient getMessageMasterDestination(String publicKey) { private Recipient getMessageMasterDestination(String publicKey) {
if (!PublicKeyValidation.isValid(publicKey)) { if (!PublicKeyValidation.isValid(publicKey)) {
return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); return Recipient.from(context, Address.fromSerialized(publicKey), false);
} else { } else {
String userPublicKey = TextSecurePreferences.getLocalNumber(context); String userPublicKey = TextSecurePreferences.getLocalNumber(context);
if (publicKey.equals(userPublicKey)) { if (publicKey.equals(userPublicKey)) {
return Recipient.from(context, Address.Companion.fromSerialized(userPublicKey), false); return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
} else { } else {
return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); return Recipient.from(context, Address.fromSerialized(publicKey), false);
} }
} }
} }
private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) { private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull String sender, int device) {
Recipient author = Recipient.from(context, Address.Companion.fromSerialized(sender), false); Recipient author = Recipient.from(context, Address.fromSerialized(sender), false);
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(conversationRecipient); long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(conversationRecipient);
if (threadId > 0) { if (threadId > 0) {
@ -1286,7 +1281,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return true; return true;
} }
Recipient sender = Recipient.from(context, Address.Companion.fromSerialized(content.getSender()), false); Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
if (content.getDataMessage().isPresent()) { if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get(); SignalServiceDataMessage message = content.getDataMessage().get();
@ -1310,8 +1305,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get()); boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get());
boolean isLeaveMessage = message.getGroupInfo().isPresent() && message.getGroupInfo().get().getType() == SignalServiceGroup.Type.QUIT; boolean isLeaveMessage = message.getGroupInfo().isPresent() && message.getGroupInfo().get().getType() == SignalServiceGroup.Type.QUIT;
boolean shouldIgnoreContentMessage = ClosedGroupsProtocol.shouldIgnoreContentMessage(context, conversation.getAddress(), groupId.orNull(), content.getSender()); return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage);
return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage) || (isContentMessage && shouldIgnoreContentMessage);
} else { } else {
return sender.isBlocked(); return sender.isBlocked();
} }

View File

@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.session.libsignal.utilities.logging.Log; import org.session.libsignal.utilities.logging.Log;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
@ -157,14 +156,14 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
List<Address> targets; List<Address> targets;
if (filterAddress != null) targets = Collections.singletonList(Address.Companion.fromSerialized(filterAddress)); if (filterAddress != null) targets = Collections.singletonList(Address.fromSerialized(filterAddress));
else if (!existingNetworkFailures.isEmpty()) targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList(); else if (!existingNetworkFailures.isEmpty()) targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList();
else targets = ClosedGroupsProtocol.getMessageDestinations(context, message.getRecipient().getAddress().toGroupString()); else targets = Collections.singletonList(Address.fromSerialized(message.getRecipient().getAddress().toGroupString()));
List<SendMessageResult> results = deliver(message, targets); List<SendMessageResult> results = deliver(message, targets);
List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.Companion.fromSerialized(result.getAddress().getNumber()))).toList(); List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList();
List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.Companion.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList(); List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList();
Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.Companion.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet()); Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet());
List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successAddresses.contains(failure.getAddress())).toList();
List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).toList();
List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList(); List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
@ -188,7 +187,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
} }
for (SendMessageResult success : successes) { for (SendMessageResult success : successes) {
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.Companion.fromSerialized(success.getAddress().getNumber()), DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.fromSerialized(success.getAddress().getNumber()),
messageId, messageId,
success.getSuccess().isUnidentified()); success.getSuccess().isUnidentified());
} }
@ -232,19 +231,11 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull List<Address> destinations) private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull List<Address> destinations)
throws IOException, UntrustedIdentityException, UndeliverableMessageException { throws IOException, UntrustedIdentityException, UndeliverableMessageException {
// Loki - The user shouldn't be able to message RSS feeds
Address address = message.getRecipient().getAddress(); Address address = message.getRecipient().getAddress();
// if (address.isRSSFeed()) {
// List<SendMessageResult> results = new ArrayList<>();
// for (Address destination : destinations) {
// results.add(SendMessageResult.networkFailure(new SignalServiceAddress(destination.toPhoneString())));
// }
// return results;
// }
List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList(); List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList();
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses) List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses)
.map(a -> Address.Companion.fromSerialized(a.getNumber())) .map(a -> Address.fromSerialized(a.getNumber()))
.map(a -> Recipient.from(context, a, false)) .map(a -> Recipient.from(context, a, false))
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)) .map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
.toList(); .toList();
@ -306,7 +297,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Override @Override
public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
String address = data.getString(KEY_FILTER_ADDRESS); String address = data.getString(KEY_FILTER_ADDRESS);
Address filter = address != null ? Address.Companion.fromSerialized(data.getString(KEY_FILTER_ADDRESS)) : null; Address filter = address != null ? Address.fromSerialized(data.getString(KEY_FILTER_ADDRESS)) : null;
return new PushGroupSendJob(parameters, data.getLong(KEY_MESSAGE_ID), filter); return new PushGroupSendJob(parameters, data.getLong(KEY_MESSAGE_ID), filter);
} }

View File

@ -116,7 +116,7 @@ public class PushGroupUpdateJob extends BaseJob implements InjectableType {
.withName(record.get().getTitle()) .withName(record.get().getTitle())
.build(); .build();
Address groupAddress = Address.Companion.fromSerialized(GroupUtil.getEncodedClosedGroupID(groupId)); Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedClosedGroupID(groupId));
Recipient groupRecipient = Recipient.from(context, groupAddress, false); Recipient groupRecipient = Recipient.from(context, groupAddress, false);
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
@ -126,7 +126,7 @@ public class PushGroupUpdateJob extends BaseJob implements InjectableType {
.build(); .build();
messageSender.sendMessage(0, new SignalServiceAddress(source), messageSender.sendMessage(0, new SignalServiceAddress(source),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.Companion.fromSerialized(source), false)), UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(source), false)),
message); message);
} }

View File

@ -212,7 +212,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
} catch (UntrustedIdentityException uie) { } catch (UntrustedIdentityException uie) {
warn(TAG, "Failure", uie); warn(TAG, "Failure", uie);
if (messageId >= 0) { if (messageId >= 0) {
database.addMismatchedIdentity(messageId, Address.Companion.fromSerialized(uie.getE164Number()), uie.getIdentityKey()); database.addMismatchedIdentity(messageId, Address.fromSerialized(uie.getE164Number()), uie.getIdentityKey());
database.markAsSentFailed(messageId); database.markAsSentFailed(messageId);
} }
} catch (SnodeAPI.Error e) { } catch (SnodeAPI.Error e) {
@ -331,7 +331,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
public @NonNull PushMediaSendJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull PushMediaSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
long messageID = data.getLong(KEY_MESSAGE_ID); long messageID = data.getLong(KEY_MESSAGE_ID);
Address destination = Address.Companion.fromSerialized(data.getString(KEY_DESTINATION)); Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
return new PushMediaSendJob(parameters, templateMessageID, messageID, destination); return new PushMediaSendJob(parameters, templateMessageID, messageID, destination);
} }
} }

View File

@ -25,7 +25,7 @@ public abstract class PushReceivedJob extends BaseJob {
synchronized (RECEIVE_LOCK) { synchronized (RECEIVE_LOCK) {
try { try {
if (envelope.hasSource()) { if (envelope.hasSource()) {
Address source = Address.Companion.fromExternal(context, envelope.getSource()); Address source = Address.fromExternal(context, envelope.getSource());
Recipient recipient = Recipient.from(context, source, false); Recipient recipient = Recipient.from(context, source, false);
if (!isActiveNumber(recipient)) { if (!isActiveNumber(recipient)) {
@ -54,7 +54,7 @@ public abstract class PushReceivedJob extends BaseJob {
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
private void handleReceipt(SignalServiceEnvelope envelope) { private void handleReceipt(SignalServiceEnvelope envelope) {
Log.i(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp())); Log.i(TAG, String.format("Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.Companion.fromExternal(context, envelope.getSource()), DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(new SyncMessageId(Address.fromExternal(context, envelope.getSource()),
envelope.getTimestamp()), System.currentTimeMillis()); envelope.getTimestamp()), System.currentTimeMillis());
} }

View File

@ -155,7 +155,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
} catch (UntrustedIdentityException e) { } catch (UntrustedIdentityException e) {
warn(TAG, "Couldn't send message due to error: ", e); warn(TAG, "Couldn't send message due to error: ", e);
if (messageId >= 0) { if (messageId >= 0) {
database.addMismatchedIdentity(record.getId(), Address.Companion.fromSerialized(e.getE164Number()), e.getIdentityKey()); database.addMismatchedIdentity(record.getId(), Address.fromSerialized(e.getE164Number()), e.getIdentityKey());
database.markAsSentFailed(record.getId()); database.markAsSentFailed(record.getId());
database.markAsPush(record.getId()); database.markAsPush(record.getId());
} }
@ -266,7 +266,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
public @NonNull PushTextSendJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull PushTextSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
long messageID = data.getLong(KEY_MESSAGE_ID); long messageID = data.getLong(KEY_MESSAGE_ID);
Address destination = Address.Companion.fromSerialized(data.getString(KEY_DESTINATION)); Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
return new PushTextSendJob(parameters, templateMessageID, messageID, destination); return new PushTextSendJob(parameters, templateMessageID, messageID, destination);
} }
} }

View File

@ -82,7 +82,7 @@ public class RequestGroupInfoJob extends BaseJob implements InjectableType {
.build(); .build();
messageSender.sendMessage(0, new SignalServiceAddress(source), messageSender.sendMessage(0, new SignalServiceAddress(source),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.Companion.fromExternal(context, source), false)), UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromExternal(context, source), false)),
message); message);
} }

View File

@ -134,7 +134,7 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType
@Override @Override
public @NonNull RetrieveProfileAvatarJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull RetrieveProfileAvatarJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new RetrieveProfileAvatarJob(parameters, return new RetrieveProfileAvatarJob(parameters,
Recipient.from(application, Address.Companion.fromSerialized(data.getString(KEY_ADDRESS)), true), Recipient.from(application, Address.fromSerialized(data.getString(KEY_ADDRESS)), true),
data.getString(KEY_PROFILE_AVATAR)); data.getString(KEY_PROFILE_AVATAR));
} }
} }

View File

@ -86,7 +86,7 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType {
timestamp); timestamp);
messageSender.sendReceipt(remoteAddress, messageSender.sendReceipt(remoteAddress,
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.Companion.fromSerialized(address), false)), UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
receiptMessage); receiptMessage);
} }
@ -105,7 +105,7 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType {
@Override @Override
public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new SendDeliveryReceiptJob(parameters, return new SendDeliveryReceiptJob(parameters,
Address.Companion.fromSerialized(data.getString(KEY_ADDRESS)), Address.fromSerialized(data.getString(KEY_ADDRESS)),
data.getLong(KEY_MESSAGE_ID), data.getLong(KEY_MESSAGE_ID),
data.getLong(KEY_TIMESTAMP)); data.getLong(KEY_TIMESTAMP));
} }

View File

@ -91,7 +91,7 @@ public class SendReadReceiptJob extends BaseJob implements InjectableType {
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp); SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
messageSender.sendReceipt(remoteAddress, messageSender.sendReceipt(remoteAddress,
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.Companion.fromSerialized(address), false)), UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
receiptMessage); receiptMessage);
} }
@ -109,7 +109,7 @@ public class SendReadReceiptJob extends BaseJob implements InjectableType {
public static final class Factory implements Job.Factory<SendReadReceiptJob> { public static final class Factory implements Job.Factory<SendReadReceiptJob> {
@Override @Override
public @NonNull SendReadReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull SendReadReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) {
Address address = Address.Companion.fromSerialized(data.getString(KEY_ADDRESS)); Address address = Address.fromSerialized(data.getString(KEY_ADDRESS));
long timestamp = data.getLong(KEY_TIMESTAMP); long timestamp = data.getLong(KEY_TIMESTAMP);
long[] ids = data.hasLongArray(KEY_MESSAGE_IDS) ? data.getLongArray(KEY_MESSAGE_IDS) : new long[0]; long[] ids = data.hasLongArray(KEY_MESSAGE_IDS) ? data.getLongArray(KEY_MESSAGE_IDS) : new long[0];
List<Long> messageIds = new ArrayList<>(ids.length); List<Long> messageIds = new ArrayList<>(ids.length);

View File

@ -19,20 +19,20 @@ import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.task import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.failUi import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi import nl.komponents.kovenant.ui.successUi
import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.loki.dialogs.ClosedGroupEditingOptionsBottomSheet import org.thoughtcrime.securesms.loki.dialogs.ClosedGroupEditingOptionsBottomSheet
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2 import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2
import org.thoughtcrime.securesms.loki.utilities.fadeIn import org.thoughtcrime.securesms.loki.utilities.fadeIn
import org.thoughtcrime.securesms.loki.utilities.fadeOut import org.thoughtcrime.securesms.loki.utilities.fadeOut
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.ThemeUtil import org.session.libsession.utilities.ThemeUtil
import org.session.libsignal.service.loki.utilities.toHexString
import java.io.IOException import java.io.IOException
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
@ -249,7 +249,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
var isSSKBasedClosedGroup: Boolean var isSSKBasedClosedGroup: Boolean
var groupPublicKey: String? var groupPublicKey: String?
try { try {
groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(groupID).toHexString() groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey) isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey)
} catch (e: IOException) { } catch (e: IOException) {
groupPublicKey = null groupPublicKey = null
@ -299,7 +299,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
isLoading = false isLoading = false
finish() finish()
}.failUi { exception -> }.failUi { exception ->
val message = if (exception is ClosedGroupsProtocol.Error) exception.description else "An error occurred" val message = if (exception is ClosedGroupsProtocolV2.Error) exception.description else "An error occurred"
Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show() Toast.makeText(this@EditClosedGroupActivity, message, Toast.LENGTH_LONG).show()
loader.fadeOut() loader.fadeOut()
isLoading = false isLoading = false

View File

@ -29,10 +29,10 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.ConversationActivity import org.thoughtcrime.securesms.conversation.ConversationActivity
import org.session.libsession.messaging.threads.Address import org.session.libsession.messaging.threads.Address
import org.session.libsession.utilities.GroupUtil
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob import org.thoughtcrime.securesms.loki.api.ResetThreadSessionJob
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol
import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation import org.thoughtcrime.securesms.loki.protocol.SessionResetImplementation
import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.utilities.*
import org.thoughtcrime.securesms.loki.views.ConversationView import org.thoughtcrime.securesms.loki.views.ConversationView
@ -46,7 +46,6 @@ import org.session.libsession.utilities.TextSecurePreferences.setBooleanPreferen
import org.session.libsession.utilities.Util import org.session.libsession.utilities.Util
import org.session.libsignal.service.loki.protocol.mentions.MentionsManager import org.session.libsignal.service.loki.protocol.mentions.MentionsManager
import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol
import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.toHexString import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.loki.dialogs.* import org.thoughtcrime.securesms.loki.dialogs.*
@ -176,7 +175,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey) SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
application.publicChatManager.startPollersIfNeeded() application.publicChatManager.startPollersIfNeeded()
} }
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application)
IP2Country.configureIfNeeded(this) IP2Country.configureIfNeeded(this)
application.registerForFCMIfNeeded(false) application.registerForFCMIfNeeded(false)
// Observe blocked contacts changed events // Observe blocked contacts changed events
@ -339,7 +337,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
var isSSKBasedClosedGroup: Boolean var isSSKBasedClosedGroup: Boolean
var groupPublicKey: String? var groupPublicKey: String?
try { try {
groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(recipient.address.toString()).toHexString() groupPublicKey = GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString()
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey) isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)
} catch (e: IOException) { } catch (e: IOException) {
groupPublicKey = null groupPublicKey = null
@ -347,7 +345,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
} }
if (isSSKBasedClosedGroup) { if (isSSKBasedClosedGroup) {
ClosedGroupsProtocolV2.explicitLeave(context, groupPublicKey!!) ClosedGroupsProtocolV2.explicitLeave(context, groupPublicKey!!)
} else if (!ClosedGroupsProtocol.leaveLegacyGroup(context, recipient)) { } else {
Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show() Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show()
return@launch return@launch
} }

View File

@ -1,180 +0,0 @@
package org.thoughtcrime.securesms.loki.protocol
import com.google.protobuf.ByteString
import org.session.libsession.messaging.jobs.Data
import org.session.libsignal.libsignal.util.guava.Optional
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.BaseJob
import org.session.libsignal.utilities.logging.Log
import org.thoughtcrime.securesms.loki.utilities.recipient
import org.session.libsignal.utilities.Hex
import org.session.libsignal.service.api.push.SignalServiceAddress
import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupSenderKey
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities
import org.session.libsignal.service.loki.utilities.toHexString
import java.util.*
import java.util.concurrent.TimeUnit
class ClosedGroupUpdateMessageSendJob private constructor(parameters: Parameters, private val destination: String, private val kind: Kind) : BaseJob(parameters) {
sealed class Kind {
data class New(val groupPublicKey: ByteArray, val name: String, val groupPrivateKey: ByteArray, val senderKeys: Collection<ClosedGroupSenderKey>, val members: Collection<ByteArray>, val admins: Collection<ByteArray>) : Kind()
data class Info(val groupPublicKey: ByteArray, val name: String, val senderKeys: Collection<ClosedGroupSenderKey>, val members: Collection<ByteArray>, val admins: Collection<ByteArray>) : Kind()
data class SenderKeyRequest(val groupPublicKey: ByteArray) : Kind()
data class SenderKey(val groupPublicKey: ByteArray, val senderKey: ClosedGroupSenderKey) : Kind()
}
companion object {
const val KEY = "ClosedGroupUpdateMessageSendJob"
}
constructor(destination: String, kind: Kind) : this(Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setQueue(KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(1)
.build(),
destination,
kind)
override fun getFactoryKey(): String { return KEY }
override fun serialize(): Data {
val builder = Data.Builder()
builder.putString("destination", destination)
when (kind) {
is Kind.New -> {
builder.putString("kind", "New")
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
builder.putString("name", kind.name)
builder.putByteArray("groupPrivateKey", kind.groupPrivateKey)
val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() }
builder.putString("senderKeys", senderKeys)
val members = kind.members.joinToString(" - ") { it.toHexString() }
builder.putString("members", members)
val admins = kind.admins.joinToString(" - ") { it.toHexString() }
builder.putString("admins", admins)
}
is Kind.Info -> {
builder.putString("kind", "Info")
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
builder.putString("name", kind.name)
val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() }
builder.putString("senderKeys", senderKeys)
val members = kind.members.joinToString(" - ") { it.toHexString() }
builder.putString("members", members)
val admins = kind.admins.joinToString(" - ") { it.toHexString() }
builder.putString("admins", admins)
}
is Kind.SenderKeyRequest -> {
builder.putString("kind", "SenderKeyRequest")
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
}
is Kind.SenderKey -> {
builder.putString("kind", "SenderKey")
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
builder.putString("senderKey", kind.senderKey.toJSON())
}
}
return builder.build()
}
public override fun onRun() {
val contentMessage = SignalServiceProtos.Content.newBuilder()
val dataMessage = SignalServiceProtos.DataMessage.newBuilder()
val closedGroupUpdate = SignalServiceProtos.ClosedGroupUpdate.newBuilder()
when (kind) {
is Kind.New -> {
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.NEW
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
closedGroupUpdate.name = kind.name
closedGroupUpdate.groupPrivateKey = ByteString.copyFrom(kind.groupPrivateKey)
closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() })
closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) })
closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) })
}
is Kind.Info -> {
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.INFO
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
closedGroupUpdate.name = kind.name
closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() })
closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) })
closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) })
}
is Kind.SenderKeyRequest -> {
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
}
is Kind.SenderKey -> {
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
closedGroupUpdate.addAllSenderKeys(listOf( kind.senderKey.toProto() ))
}
}
dataMessage.closedGroupUpdate = closedGroupUpdate.build()
contentMessage.dataMessage = dataMessage.build()
val serializedContentMessage = contentMessage.build().toByteArray()
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
val address = SignalServiceAddress(destination)
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 = 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,
Date().time, serializedContentMessage, false, ttl, false,
useFallbackEncryption, false, false, Optional.absent())
} catch (e: Exception) {
Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.")
}
}
public override fun onShouldRetry(e: Exception): Boolean {
// Disable since we have our own retrying
return false
}
override fun onCanceled() { }
class Factory : Job.Factory<ClosedGroupUpdateMessageSendJob> {
override fun create(parameters: Parameters, data: Data): ClosedGroupUpdateMessageSendJob {
val destination = data.getString("destination")
val rawKind = data.getString("kind")
val groupPublicKey = data.getByteArray("groupPublicKey")
val kind: Kind
when (rawKind) {
"New" -> {
val name = data.getString("name")
val groupPrivateKey = data.getByteArray("groupPrivateKey")
val senderKeys = data.getStringOrDefault("senderKeys", "").split(" - ").mapNotNull { ClosedGroupSenderKey.fromJSON(it) } // Can be empty
val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) }
val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) }
kind = Kind.New(groupPublicKey, name, groupPrivateKey, senderKeys, members, admins)
}
"Info" -> {
val name = data.getString("name")
val senderKeys = data.getStringOrDefault("senderKeys", "").split(" - ").mapNotNull { ClosedGroupSenderKey.fromJSON(it) } // Can be empty
val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) }
val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) }
kind = Kind.Info(groupPublicKey, name, senderKeys, members, admins)
}
"SenderKeyRequest" -> {
kind = Kind.SenderKeyRequest(groupPublicKey)
}
"SenderKey" -> {
val senderKey = ClosedGroupSenderKey.fromJSON(data.getString("senderKey"))!!
kind = Kind.SenderKey(groupPublicKey, senderKey)
}
else -> throw Exception("Invalid closed group update message kind: $rawKind.")
}
return ClosedGroupUpdateMessageSendJob(parameters, destination, kind)
}
}
}

View File

@ -224,7 +224,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete
try { try {
// isClosedGroup can always be false as it's only used in the context of legacy closed groups // isClosedGroup can always be false as it's only used in the context of legacy closed groups
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
sentTime, serializedContentMessage, false, ttl, false, sentTime, serializedContentMessage, false, ttl,
true, false, false, Optional.absent()) true, false, false, Optional.absent())
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.") Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.")

View File

@ -1,329 +0,0 @@
package org.thoughtcrime.securesms.loki.protocol
import android.content.Context
import androidx.annotation.WorkerThread
import com.google.protobuf.ByteString
import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.deferred
import org.session.libsession.messaging.threads.Address
import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupRatchetCollectionType
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupSenderKey
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysImplementation
import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.session.libsignal.utilities.logging.Log
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager
import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager.ClosedGroupOperation
import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage
import org.thoughtcrime.securesms.sms.MessageSender
import org.session.libsignal.utilities.Hex
import java.io.IOException
import java.util.*
object ClosedGroupsProtocol {
sealed class Error(val description: String) : Exception() {
object NoThread : Error("Couldn't find a thread associated with the given group public key")
object NoPrivateKey : Error("Couldn't find a private key associated with the given group public key.")
object InvalidUpdate : Error("Invalid group update.")
}
@JvmStatic
fun leave(context: Context, groupPublicKey: String) {
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
val groupDB = DatabaseFactory.getGroupDatabase(context)
val groupID = doubleEncodeGroupID(groupPublicKey)
val group = groupDB.getGroup(groupID).orNull()
if (group == null) {
Log.d("Loki", "Can't leave nonexistent closed group.")
return
}
val name = group.title
val oldMembers = group.members.map { it.serialize() }.toSet()
val newMembers = oldMembers.minus(userPublicKey)
return update(context, groupPublicKey, newMembers, name).get()
}
fun update(context: Context, groupPublicKey: String, members: Collection<String>, name: String): Promise<Unit, Exception> {
val deferred = deferred<Unit, Exception>()
Thread {
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
val sskDatabase = DatabaseFactory.getSSKDatabase(context)
val groupDB = DatabaseFactory.getGroupDatabase(context)
val groupID = doubleEncodeGroupID(groupPublicKey)
val group = groupDB.getGroup(groupID).orNull()
if (group == null) {
Log.d("Loki", "Can't update nonexistent closed group.")
return@Thread deferred.reject(Error.NoThread)
}
val oldMembers = group.members.map { it.serialize() }.toSet()
val newMembers = members.minus(oldMembers)
val membersAsData = members.map { Hex.fromStringCondensed(it) }
val admins = group.admins.map { it.serialize() }
val adminsAsData = admins.map { Hex.fromStringCondensed(it) }
val groupPrivateKey = DatabaseFactory.getSSKDatabase(context).getClosedGroupPrivateKey(groupPublicKey)
if (groupPrivateKey == null) {
Log.d("Loki", "Couldn't get private key for closed group.")
return@Thread deferred.reject(Error.NoPrivateKey)
}
val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet()
val removedMembers = oldMembers.minus(members)
val isUserLeaving = removedMembers.contains(userPublicKey)
var newSenderKeys = listOf<ClosedGroupSenderKey>()
if (wasAnyUserRemoved) {
if (isUserLeaving && removedMembers.count() != 1) {
Log.d("Loki", "Can't remove self and others simultaneously.")
return@Thread deferred.reject(Error.InvalidUpdate)
}
// Establish sessions if needed
establishSessionsWithMembersIfNeeded(context, members)
// Send the update to the existing members using established channels (don't include new ratchets as everyone should regenerate new ratchets individually)
for (member in oldMembers) {
@Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey),
name, setOf(), membersAsData, adminsAsData)
@Suppress("NAME_SHADOWING")
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
job.setContext(context)
job.onRun() // Run the job immediately
}
val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current)
for (pair in allOldRatchets) {
val senderPublicKey = pair.first
val ratchet = pair.second
val collection = ClosedGroupRatchetCollectionType.Old
sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection)
}
// Delete all ratchets (it's important that this happens * after * sending out the update)
sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current)
// Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and
// send it out to all members (minus the removed ones) using established channels.
if (isUserLeaving) {
sskDatabase.removeClosedGroupPrivateKey(groupPublicKey)
groupDB.setActive(groupID, false)
groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey))
// Notify the PN server
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey)
} else {
// Send closed group update messages to any new members using established channels
for (member in newMembers) {
@Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name,
Hex.fromStringCondensed(groupPrivateKey), listOf(), membersAsData, adminsAsData)
@Suppress("NAME_SHADOWING")
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
// Send out the user's new ratchet to all members (minus the removed ones) using established channels
val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey)
val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey))
for (member in members) {
if (member == userPublicKey) { continue }
@Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey)
@Suppress("NAME_SHADOWING")
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
}
} else if (newMembers.isNotEmpty()) {
// Generate ratchets for any new members
newSenderKeys = newMembers.map { publicKey ->
val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey)
ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey))
}
// Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group)
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name,
newSenderKeys, membersAsData, adminsAsData)
val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
// Establish sessions if needed
establishSessionsWithMembersIfNeeded(context, newMembers)
// Send closed group update messages to the new members using established channels
var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current)
allSenderKeys = allSenderKeys.union(newSenderKeys)
for (member in newMembers) {
@Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name,
Hex.fromStringCondensed(groupPrivateKey), allSenderKeys, membersAsData, adminsAsData)
@Suppress("NAME_SHADOWING")
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
} else {
val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current)
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name,
allSenderKeys, membersAsData, adminsAsData)
val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
// Update the group
groupDB.updateTitle(groupID, name)
if (!isUserLeaving) {
// The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead
groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) })
}
// Notify the user
val infoType = if (isUserLeaving) GroupContext.Type.QUIT else GroupContext.Type.UPDATE
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false))
insertOutgoingInfoMessage(context, groupID, infoType, name, members, admins, threadID)
deferred.resolve(Unit)
}.start()
return deferred.promise
}
@JvmStatic
fun requestSenderKey(context: Context, groupPublicKey: String, senderPublicKey: String) {
Log.d("Loki", "Requesting sender key for group public key: $groupPublicKey, sender public key: $senderPublicKey.")
// Establish session if needed
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey)
// Send the request
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKeyRequest(Hex.fromStringCondensed(groupPublicKey))
val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
@JvmStatic
fun shouldIgnoreContentMessage(context: Context, address: Address, groupID: String?, senderPublicKey: String): Boolean {
if (!address.isClosedGroup || groupID == null) { return false }
/*
FileServerAPI.shared.getDeviceLinks(senderPublicKey).timeout(6000).get()
val senderMasterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(senderPublicKey)
val publicKeyToCheckFor = senderMasterPublicKey ?: senderPublicKey
*/
val members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, true)
return !members.contains(recipient(context, senderPublicKey))
}
@JvmStatic
fun getMessageDestinations(context: Context, groupID: String): List<Address> {
if (GroupUtil.isOpenGroup(groupID)) {
return listOf(Address.fromSerialized(groupID))
} else {
var groupPublicKey: String? = null
try {
groupPublicKey = doubleDecodeGroupID(groupID).toHexString()
} catch (exception: Exception) {
// Do nothing
}
if (groupPublicKey != null && DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) {
return listOf(Address.fromSerialized(groupPublicKey))
} else {
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupID, false).map { it.address }
}
/*
return FileServerAPI.shared.getDeviceLinks(members.map { it.address.serialize() }.toSet()).map {
val result = members.flatMap { member ->
MultiDeviceProtocol.shared.getAllLinkedDevices(member.address.serialize()).map { Address.fromSerialized(it) }
}.toMutableSet()
val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
if (userMasterPublicKey != null && result.contains(Address.fromSerialized(userMasterPublicKey))) {
result.remove(Address.fromSerialized(userMasterPublicKey))
}
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
if (userPublicKey != null && result.contains(Address.fromSerialized(userPublicKey))) {
result.remove(Address.fromSerialized(userPublicKey))
}
result.toList()
}
*/
}
}
@JvmStatic
fun leaveLegacyGroup(context: Context, recipient: Recipient): Boolean {
if (!recipient.address.isClosedGroup) { return true }
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
val message = createGroupLeaveMessage(context, recipient)
if (threadID < 0 || message == null) { return false }
MessageSender.send(context, message, threadID, false, null)
/*
val masterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
val publicKeyToRemove = masterPublicKey ?: TextSecurePreferences.getLocalNumber(context)
*/
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
val groupID = recipient.address.toGroupString()
groupDatabase.setActive(groupID, false)
groupDatabase.removeMember(groupID, Address.fromSerialized(userPublicKey))
return true
}
@JvmStatic
fun establishSessionsWithMembersIfNeeded(context: Context, members: Collection<String>) {
@Suppress("NAME_SHADOWING") val members = members.toMutableSet()
/*
val allDevices = members.flatMap { member ->
MultiDeviceProtocol.shared.getAllLinkedDevices(member)
}.toMutableSet()
val userMasterPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
if (userMasterPublicKey != null && allDevices.contains(userMasterPublicKey)) {
allDevices.remove(userMasterPublicKey)
}
*/
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
if (userPublicKey != null && members.contains(userPublicKey)) {
members.remove(userPublicKey)
}
for (member in members) {
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(member)
}
}
private fun insertOutgoingInfoMessage(context: Context, groupID: String, type: GroupContext.Type, name: String,
members: Collection<String>, admins: Collection<String>, threadID: Long) {
val recipient = Recipient.from(context, Address.fromSerialized(groupID), false)
val groupContextBuilder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupID)))
.setType(type)
.setName(name)
.addAllMembers(members)
.addAllAdmins(admins)
val infoMessage = OutgoingGroupMediaMessage(recipient, groupContextBuilder.build(), null, 0, null, listOf(), listOf())
val mmsDB = DatabaseFactory.getMmsDatabase(context)
val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null)
mmsDB.markAsSent(infoMessageID, true)
}
// NOTE: Signal group ID handling is weird. The ID is double encoded in the database, but not in a `GroupContext`.
@JvmStatic
@Throws(IOException::class)
fun doubleEncodeGroupID(groupPublicKey: String): String {
return GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray())
}
@JvmStatic
@Throws(IOException::class)
fun doubleDecodeGroupID(groupID: String): ByteArray {
return GroupUtil.getDecodedGroupIDAsData(GroupUtil.getDecodedGroupID(groupID))
}
@WorkerThread
fun createGroupLeaveMessage(context: Context, groupRecipient: Recipient): OutgoingGroupMediaMessage? {
val encodedGroupId = groupRecipient.address.toGroupString()
val groupDatabase = DatabaseFactory.getGroupDatabase(context)
if (!groupDatabase.isActive(encodedGroupId)) {
Log.w("Loki", "Group has already been left.")
return null
}
val decodedGroupId: ByteString
try {
decodedGroupId = ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(encodedGroupId))
} catch (e: IOException) {
Log.w("Loki", "Failed to decode group ID.", e)
return null
}
val groupContext = GroupContext.newBuilder()
.setId(decodedGroupId)
.setType(GroupContext.Type.QUIT)
.build()
return OutgoingGroupMediaMessage(groupRecipient, groupContext, null, 0, null, emptyList(), emptyList())
}
}

View File

@ -56,7 +56,7 @@ class NullMessageSendJob private constructor(parameters: Parameters, private val
val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.Ephemeral) val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.Ephemeral)
try { try {
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
Date().time, serializedContentMessage, false, ttl, false, Date().time, serializedContentMessage, false, ttl,
false, false, false, Optional.absent()) false, false, false, Optional.absent())
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.") Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.")

View File

@ -96,9 +96,6 @@ object SessionManagementProtocol {
fun triggerSessionRestorationUI(context: Context, publicKey: String, errorTimestamp: Long) { fun triggerSessionRestorationUI(context: Context, publicKey: String, errorTimestamp: Long) {
val recipient = recipient(context, publicKey) val recipient = recipient(context, publicKey)
if (recipient.isGroupRecipient) { return } if (recipient.isGroupRecipient) { return }
if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) {
return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey)
}
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient) val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey) DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey)
} }

View File

@ -125,7 +125,7 @@ public class MediaSendActivity extends PassphraseRequiredActionBarActivity imple
cameraButton = findViewById(R.id.mediasend_camera_button); cameraButton = findViewById(R.id.mediasend_camera_button);
viewModel = new ViewModelProvider(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class); viewModel = new ViewModelProvider(this, new MediaSendViewModel.Factory(getApplication(), new MediaRepository())).get(MediaSendViewModel.class);
recipient = Recipient.from(this, Address.Companion.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true); recipient = Recipient.from(this, Address.fromSerialized(getIntent().getStringExtra(KEY_ADDRESS)), true);
viewModel.onBodyChanged(getIntent().getStringExtra(KEY_BODY)); viewModel.onBodyChanged(getIntent().getStringExtra(KEY_BODY));

View File

@ -83,7 +83,7 @@ public class IncomingMediaMessage {
this.quote = quote.orNull(); this.quote = quote.orNull();
this.unidentified = unidentified; this.unidentified = unidentified;
if (group.isPresent()) this.groupId = Address.Companion.fromSerialized(GroupUtil.INSTANCE.getEncodedId(group.get())); if (group.isPresent()) this.groupId = Address.fromSerialized(GroupUtil.INSTANCE.getEncodedId(group.get()));
else this.groupId = null; else this.groupId = null;
this.attachments.addAll(PointerAttachment.forPointers(attachments)); this.attachments.addAll(PointerAttachment.forPointers(attachments));

View File

@ -50,7 +50,7 @@ public class QuoteId {
public static @Nullable QuoteId deserialize(@NonNull String serialized) { public static @Nullable QuoteId deserialize(@NonNull String serialized) {
try { try {
JSONObject json = new JSONObject(serialized); JSONObject json = new JSONObject(serialized);
return new QuoteId(json.getLong(ID), Address.Companion.fromSerialized(json.getString(AUTHOR))); return new QuoteId(json.getLong(ID), Address.fromSerialized(json.getString(AUTHOR)));
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, "Failed to deserialize from json", e); Log.e(TAG, "Failed to deserialize from json", e);
return null; return null;

View File

@ -128,7 +128,7 @@ public class SearchRepository {
private CursorList<ThreadRecord> queryConversations(@NonNull String query) { private CursorList<ThreadRecord> queryConversations(@NonNull String query) {
List<String> numbers = contactAccessor.getNumbersForThreadSearchFilter(context, query); List<String> numbers = contactAccessor.getNumbersForThreadSearchFilter(context, query);
List<Address> addresses = Stream.of(numbers).map(number -> Address.Companion.fromExternal(context, number)).toList(); List<Address> addresses = Stream.of(numbers).map(number -> Address.fromExternal(context, number)).toList();
Cursor conversations = threadDatabase.getFilteredConversationList(addresses); Cursor conversations = threadDatabase.getFilteredConversationList(addresses);
return conversations != null ? new CursorList<>(conversations, new ThreadModelBuilder(threadDatabase)) return conversations != null ? new CursorList<>(conversations, new ThreadModelBuilder(threadDatabase))
@ -179,7 +179,7 @@ public class SearchRepository {
@Override @Override
public Recipient build(@NonNull Cursor cursor) { public Recipient build(@NonNull Cursor cursor) {
Address address = Address.Companion.fromExternal(context, cursor.getString(1)); Address address = Address.fromExternal(context, cursor.getString(1));
return Recipient.from(context, address, false); return Recipient.from(context, address, false);
} }
} }
@ -208,8 +208,8 @@ public class SearchRepository {
@Override @Override
public MessageResult build(@NonNull Cursor cursor) { public MessageResult build(@NonNull Cursor cursor) {
Address conversationAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndex(SearchDatabase.CONVERSATION_ADDRESS))); Address conversationAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndex(SearchDatabase.CONVERSATION_ADDRESS)));
Address messageAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SearchDatabase.MESSAGE_ADDRESS))); Address messageAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SearchDatabase.MESSAGE_ADDRESS)));
Recipient conversationRecipient = Recipient.from(context, conversationAddress, false); Recipient conversationRecipient = Recipient.from(context, conversationAddress, false);
Recipient messageRecipient = Recipient.from(context, messageAddress, false); Recipient messageRecipient = Recipient.from(context, messageAddress, false);
String body = cursor.getString(cursor.getColumnIndexOrThrow(SearchDatabase.SNIPPET)); String body = cursor.getString(cursor.getColumnIndexOrThrow(SearchDatabase.SNIPPET));

View File

@ -68,7 +68,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
public void setExpirationTimer(@Nullable Long messageID, int duration, @NotNull String senderPublicKey, @NotNull SignalServiceProtos.Content content) { public void setExpirationTimer(@Nullable Long messageID, int duration, @NotNull String senderPublicKey, @NotNull SignalServiceProtos.Content content) {
try { try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Address address = Address.Companion.fromSerialized(senderPublicKey); Address address = Address.fromSerialized(senderPublicKey);
Recipient recipient = Recipient.from(context, address, false); Recipient recipient = Recipient.from(context, address, false);
Optional<SignalServiceGroup> groupInfo = Optional.absent(); Optional<SignalServiceGroup> groupInfo = Optional.absent();
if (content.getDataMessage().hasGroup()) { if (content.getDataMessage().hasGroup()) {

View File

@ -47,7 +47,7 @@ public class QuickResponseService extends IntentService {
number = URLDecoder.decode(number); number = URLDecoder.decode(number);
} }
Address address = Address.Companion.fromExternal(this, number); Address address = Address.fromExternal(this, number);
Recipient recipient = Recipient.from(this, address, false); Recipient recipient = Recipient.from(this, address, false);
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1); int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
long expiresIn = recipient.getExpireMessages() * 1000L; long expiresIn = recipient.getExpireMessages() * 1000L;

View File

@ -47,7 +47,7 @@ public class IncomingTextMessage implements Parcelable {
public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) { public IncomingTextMessage(@NonNull Context context, @NonNull SmsMessage message, int subscriptionId) {
this.message = message.getDisplayMessageBody(); this.message = message.getDisplayMessageBody();
this.sender = Address.Companion.fromSerialized(message.getDisplayOriginatingAddress()); this.sender = Address.fromSerialized(message.getDisplayOriginatingAddress());
this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
this.protocol = message.getProtocolIdentifier(); this.protocol = message.getProtocolIdentifier();
this.serviceCenterAddress = message.getServiceCenterAddress(); this.serviceCenterAddress = message.getServiceCenterAddress();
@ -79,7 +79,7 @@ public class IncomingTextMessage implements Parcelable {
this.unidentified = unidentified; this.unidentified = unidentified;
if (group.isPresent()) { if (group.isPresent()) {
this.groupId = Address.Companion.fromSerialized(GroupUtil.getEncodedId(group.get())); this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get()));
} else { } else {
this.groupId = null; this.groupId = null;
} }

View File

@ -222,7 +222,7 @@ public class MessageSender {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
OutgoingMediaMessage message = mmsDatabase.getOutgoingMessage(messageId); OutgoingMediaMessage message = mmsDatabase.getOutgoingMessage(messageId);
SyncMessageId syncId = new SyncMessageId(Address.Companion.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getSentTimeMillis()); SyncMessageId syncId = new SyncMessageId(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getSentTimeMillis());
for (Attachment attachment : message.getAttachments()) { for (Attachment attachment : message.getAttachments()) {
attachmentDatabase.markAttachmentUploaded(messageId, attachment); attachmentDatabase.markAttachmentUploaded(messageId, attachment);
@ -249,7 +249,7 @@ public class MessageSender {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
SmsMessageRecord message = smsDatabase.getMessage(messageId); SmsMessageRecord message = smsDatabase.getMessage(messageId);
SyncMessageId syncId = new SyncMessageId(Address.Companion.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getDateSent()); SyncMessageId syncId = new SyncMessageId(Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), message.getDateSent());
smsDatabase.markAsSent(messageId, true); smsDatabase.markAsSent(messageId, true);
smsDatabase.markUnidentified(messageId, true); smsDatabase.markUnidentified(messageId, true);

View File

@ -152,15 +152,18 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
val UNKNOWN = Address("Unknown") val UNKNOWN = Address("Unknown")
private val TAG = Address::class.java.simpleName private val TAG = Address::class.java.simpleName
private val cachedFormatter = AtomicReference<Pair<String, ExternalAddressFormatter>>() private val cachedFormatter = AtomicReference<Pair<String, ExternalAddressFormatter>>()
@JvmStatic @JvmStatic
fun fromSerialized(serialized: String): Address { fun fromSerialized(serialized: String): Address {
return Address(serialized) return Address(serialized)
} }
@JvmStatic
fun fromExternal(context: Context, external: String?): Address { fun fromExternal(context: Context, external: String?): Address {
return fromSerialized(external!!) return fromSerialized(external!!)
} }
@JvmStatic
fun fromSerializedList(serialized: String, delimiter: Char): List<Address> { fun fromSerializedList(serialized: String, delimiter: Char): List<Address> {
val escapedAddresses = DelimiterUtil.split(serialized, delimiter) val escapedAddresses = DelimiterUtil.split(serialized, delimiter)
val addresses: MutableList<Address> = LinkedList() val addresses: MutableList<Address> = LinkedList()
@ -170,6 +173,7 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
return addresses return addresses
} }
@JvmStatic
fun toSerializedList(addresses: List<Address>, delimiter: Char): String { fun toSerializedList(addresses: List<Address>, delimiter: Char): String {
Collections.sort(addresses) Collections.sort(addresses)
val escapedAddresses: MutableList<String> = LinkedList() val escapedAddresses: MutableList<String> = LinkedList()

View File

@ -216,7 +216,7 @@ public class SignalServiceMessageSender {
SignalServiceReceiptMessage message) SignalServiceReceiptMessage message)
throws IOException { throws IOException {
byte[] content = createReceiptContent(message); byte[] content = createReceiptContent(message);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); boolean useFallbackEncryption = true;
sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption); sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption);
} }
@ -244,9 +244,8 @@ public class SignalServiceMessageSender {
{ {
byte[] content = createMessageContent(message, recipient); byte[] content = createMessageContent(message, recipient);
long timestamp = message.getTimestamp(); long timestamp = message.getTimestamp();
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store);
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), useFallbackEncryption, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget()); SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), true, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
// // Loki - This shouldn't get invoked for note to self // // Loki - This shouldn't get invoked for note to self
// boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent(); // boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent();
@ -854,8 +853,7 @@ public class SignalServiceMessageSender {
SignalServiceAddress recipient = recipientIterator.next(); SignalServiceAddress recipient = recipientIterator.next();
try { try {
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store); SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, true, isClosedGroup, notifyPNServer, Optional.absent());
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer, Optional.absent());
results.add(result); results.add(result);
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
Log.w(TAG, e); Log.w(TAG, e);

View File

@ -25,14 +25,6 @@ public class SessionManagementProtocol(private val sessionResetImpl: SessionRese
// endregion // endregion
// region Sending // region Sending
public fun shouldMessageUseFallbackEncryption(message: Any, publicKey: String, store: SignalProtocolStore): Boolean {
// if (sskDatabase.isSSKBasedClosedGroup(publicKey)) { return true } // We don't actually use fallback encryption but this indicates that we don't need a session
// if (message is SignalServiceDataMessage && message.preKeyBundle.isPresent) { return true; } // This covers session requests as well as end session messages
// val recipient = SignalProtocolAddress(publicKey, SignalServiceAddress.DEFAULT_DEVICE_ID)
// return !store.containsSession(recipient)
return true;
}
/** /**
* Called after an end session message is sent. * Called after an end session message is sent.
*/ */