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.LokiUserDatabase;
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.SessionResetImplementation;
import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
@ -139,8 +138,7 @@ import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
*
* @author Moxie Marlinspike
*/
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate,
SessionManagementProtocolDelegate, SharedSenderKeysImplementationDelegate {
public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate {
private static final String TAG = ApplicationContext.class.getSimpleName();
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);
String userPublicKey = TextSecurePreferences.getLocalNumber(this);
SessionResetImplementation sessionResetImpl = new SessionResetImplementation(this);
SharedSenderKeysImplementation.Companion.configureIfNeeded(sskDatabase, this);
MessagingConfiguration.Companion.configure(this,
DatabaseFactory.getStorage(this),
sskDatabase,
@ -203,7 +200,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
}
SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, sskDatabase, this);
setUpP2PAPIIfNeeded();
PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG);
setUpStorageAPIIfNeeded();
@ -542,7 +538,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this);
byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey);
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());
FileServerAPI.shared.uploadProfilePicture(FileServerAPI.shared.getServer(), profileKey, stream, () -> {
TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime());
@ -569,7 +565,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
String url = TextSecurePreferences.getProfilePictureURL(this);
String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this);
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();
url = userMasterDeviceAsRecipient.getProfileAvatar();
}
@ -608,25 +604,5 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
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
}

View File

@ -198,7 +198,7 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
if (address == 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;
getLoaderManager().initLoader(0, null, this);

View File

@ -250,7 +250,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
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);
createConversation(existingThread, recipient.getAddress(), ThreadDatabase.DistributionTypes.DEFAULT);
}

View File

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

View File

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

View File

@ -85,6 +85,7 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.MediaTypes;
import org.session.libsignal.libsignal.InvalidMessageException;
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.LokiThreadDatabaseDelegate;
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.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
@ -1100,7 +1100,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
String groupPublicKey;
boolean isSSKBasedClosedGroup;
try {
groupPublicKey = HexEncodingKt.toHexString(ClosedGroupsProtocol.doubleDecodeGroupID(groupRecipient.getAddress().toString()));
groupPublicKey = HexEncodingKt.toHexString(GroupUtil.doubleDecodeGroupID(groupRecipient.getAddress().toString()));
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey);
} catch (IOException e) {
groupPublicKey = null;
@ -1110,8 +1110,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (isSSKBasedClosedGroup) {
ClosedGroupsProtocolV2.explicitLeave(this, groupPublicKey);
initializeEnabledCheck();
} else if (ClosedGroupsProtocol.leaveLegacyGroup(this, groupRecipient)) {
initializeEnabledCheck();
} else {
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));
if (!recipient.isGroupRecipient()) {
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
}
sendComplete(result);
future.set(null);
@ -2092,10 +2086,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
long result = MessageSender.send(context, message, threadId, false, () -> fragment.releaseOutgoingMessage(id));
if (!recipient.isGroupRecipient()) {
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(recipient.getAddress().serialize());
}
sendComplete(result);
}
@ -2517,7 +2507,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Recipient author;
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 {
author = messageRecord.getIndividualRecipient();
}

View File

@ -29,7 +29,7 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public SessionRecord loadSession(@NonNull SignalProtocolAddress address) {
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) {
Log.w(TAG, "No existing session information found.");
@ -43,14 +43,14 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) {
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
public boolean containsSession(SignalProtocolAddress address) {
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 &&
sessionRecord.getSessionState().hasSenderChain() &&
@ -61,27 +61,27 @@ public class TextSecureSessionStore implements SessionStore {
@Override
public void deleteSession(SignalProtocolAddress address) {
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
public void deleteAllSessions(String name) {
synchronized (FILE_LOCK) {
DatabaseFactory.getSessionDatabase(context).deleteAllFor(Address.Companion.fromSerialized(name));
DatabaseFactory.getSessionDatabase(context).deleteAllFor(Address.fromSerialized(name));
}
}
@Override
public List<Integer> getSubDeviceSessions(String name) {
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) {
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) {
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},
MEMBERS + " = ? AND " + MMS + " = ?",
new String[] {Address.Companion.toSerializedList(members, ','), "1"},
new String[] {Address.toSerializedList(members, ','), "1"},
null, null, null);
try {
if (cursor != null && cursor.moveToNext()) {
@ -179,7 +179,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
public boolean isClosedGroupMember(String hexEncodedPublicKey) {
try {
Address address = Address.Companion.fromSerialized(hexEncodedPublicKey);
Address address = Address.fromSerialized(hexEncodedPublicKey);
Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups();
GroupRecord record;
@ -203,7 +203,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
ContentValues contentValues = new ContentValues();
contentValues.put(GROUP_ID, groupId);
contentValues.put(TITLE, title);
contentValues.put(MEMBERS, Address.Companion.toSerializedList(members, ','));
contentValues.put(MEMBERS, Address.toSerializedList(members, ','));
if (avatar != null) {
contentValues.put(AVATAR_ID, avatar.getId());
@ -219,12 +219,12 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
contentValues.put(MMS, GroupUtil.isMmsGroup(groupId));
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);
Recipient.applyCached(Address.Companion.fromSerialized(groupId), recipient -> {
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setName(title);
recipient.setGroupAvatarId(avatar != null ? avatar.getId() : null);
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});
if (result > 0) {
Recipient.removeCached(Address.Companion.fromSerialized(groupId));
Recipient.removeCached(Address.fromSerialized(groupId));
notifyConversationListListeners();
return true;
} else {
@ -262,7 +262,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
GROUP_ID + " = ?",
new String[] {groupId});
Recipient.applyCached(Address.Companion.fromSerialized(groupId), recipient -> {
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
recipient.setName(title);
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 + " = ?",
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);
}
@ -300,20 +300,20 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
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) {
Collections.sort(members);
ContentValues contents = new ContentValues();
contents.put(MEMBERS, Address.Companion.toSerializedList(members, ','));
contents.put(MEMBERS, Address.toSerializedList(members, ','));
contents.put(ACTIVE, 1);
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
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());
});
}
@ -322,7 +322,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
Collections.sort(admins);
ContentValues contents = new ContentValues();
contents.put(ADMINS, Address.Companion.toSerializedList(admins, ','));
contents.put(ADMINS, Address.toSerializedList(admins, ','));
contents.put(ACTIVE, 1);
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);
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 + " = ?",
new String[] {groupId});
Recipient.applyCached(Address.Companion.fromSerialized(groupId), recipient -> {
Recipient.applyCached(Address.fromSerialized(groupId), recipient -> {
List<Recipient> current = recipient.getParticipants();
Recipient removal = Recipient.from(context, source, false);
@ -358,7 +358,7 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
if (cursor != null && cursor.moveToFirst()) {
String serializedMembers = cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS));
return Address.Companion.fromSerializedList(serializedMembers, ',');
return Address.fromSerializedList(serializedMembers, ',');
}
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)) {
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.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1));

View File

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

View File

@ -268,7 +268,7 @@ public class MmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) {
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();
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
? 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);
}
@ -500,7 +500,7 @@ public class MmsDatabase extends MessagingDatabase {
while(cursor != null && cursor.moveToNext()) {
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);
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);
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();
if (ourAddress.equals(theirAddress) || theirAddress.isGroup()) {
@ -642,13 +642,13 @@ public class MmsDatabase extends MessagingDatabase {
.filterNot(previewAttachments::contains)
.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<IdentityKeyMismatch> mismatches = new LinkedList<>();
QuoteModel quote = null;
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)) {
@ -955,7 +955,7 @@ public class MmsDatabase extends MessagingDatabase {
contentBuilder.add(MESSAGE_TYPE, notification.getMessageType());
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);
@ -1334,7 +1334,7 @@ public class MmsDatabase extends MessagingDatabase {
private final 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.transactionId = transactionId;
this.subscriptionId = subscriptionId;
@ -1507,7 +1507,7 @@ public class MmsDatabase extends MessagingDatabase {
if (TextUtils.isEmpty(serialized) || "insert-address-token".equals(serialized)) {
address = Address.Companion.getUNKNOWN();
} else {
address = Address.Companion.fromSerialized(serialized);
address = Address.fromSerialized(serialized);
}
return Recipient.from(context, address, true);
@ -1554,7 +1554,7 @@ public class MmsDatabase extends MessagingDatabase {
SlideDeck quoteDeck = new SlideDeck(context, quoteAttachments);
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 {
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)) {
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)) {
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)) {
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();
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()) {
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)),
cursor.getString(cursor.getColumnIndexOrThrow(COLOR)));
@ -497,7 +497,7 @@ public class RecipientDatabase extends Database {
public @NonNull Recipient getCurrent() {
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() {

View File

@ -102,7 +102,7 @@ public class SessionDatabase extends Database {
try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
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)),
new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD)))));
} catch (IOException e) {

View File

@ -377,7 +377,7 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
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;
if (ourAddress.equals(theirAddress)) {
@ -418,7 +418,7 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor.moveToNext()) {
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)) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
@ -469,7 +469,7 @@ public class SmsDatabase extends MessagingDatabase {
while (cursor != null && cursor.moveToNext()) {
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);
results.add(new MarkedMessageInfo(syncMessageId, expirationInfo));
@ -937,7 +937,7 @@ public class SmsDatabase extends MessagingDatabase {
public SmsMessageRecord getCurrent() {
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));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_RECEIVED));

View File

@ -90,7 +90,7 @@ public class SmsMigrator {
long threadId, SQLiteStatement statement)
{
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, 3, SmsDatabase.DATE_RECEIVED);
@ -137,7 +137,7 @@ public class SmsMigrator {
String address = getTheirCanonicalAddress(context, theirRecipientId);
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());
migrateConversation(context, listener, progress, theirThreadId, ourThreadId);
} 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<>();
@ -222,7 +222,7 @@ public class SmsMigrator {
}
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);
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);
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);
return Recipient.from(context, address, false);
}
@ -735,7 +735,7 @@ public class ThreadDatabase extends Database {
public ThreadRecord getCurrent() {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
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<GroupRecord> groupRecord;

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ public class GroupManager {
}
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);
}
@ -69,14 +69,14 @@ public class GroupManager {
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
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> adminAddresses = getMemberAddresses(admins);
String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(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.updateProfilePicture(groupId, avatarBytes);
@ -100,10 +100,10 @@ public class GroupManager {
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
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<>();
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.updateProfilePicture(groupId, avatarBytes);
@ -118,7 +118,7 @@ public class GroupManager {
{
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(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()) {
return false;
@ -145,7 +145,7 @@ public class GroupManager {
final Set<Address> adminAddresses = getMemberAddresses(admins);
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.updateAdmins(groupId, new LinkedList<>(adminAddresses));
groupDatabase.updateTitle(groupId, name);
@ -154,7 +154,7 @@ public class GroupManager {
if (!GroupUtil.isMmsGroup(groupId)) {
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes, adminAddresses);
} 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);
return new GroupActionResult(groupRecipient, threadId);
}
@ -168,7 +168,7 @@ public class GroupManager {
@NonNull Set<Address> admins)
{
Attachment avatarAttachment = null;
Address groupAddress = Address.Companion.fromSerialized(groupId);
Address groupAddress = Address.fromSerialized(groupId);
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
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.PushGroupUpdateJob;
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.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
@ -96,24 +95,20 @@ public class GroupMessageProcessor {
if (group.getMembers().isPresent()) {
for (String member : group.getMembers().get()) {
members.add(Address.Companion.fromExternal(context, member));
members.add(Address.fromExternal(context, member));
}
}
// Loki - Parse admins
if (group.getAdmins().isPresent()) {
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,
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);
}
@ -127,7 +122,7 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
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);
String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
@ -140,13 +135,13 @@ public class GroupMessageProcessor {
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
// 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.");
return null;
}
// 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) &&
!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.");
@ -159,7 +154,7 @@ public class GroupMessageProcessor {
Set<Address> newMembers = new HashSet<>();
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
@ -198,14 +193,10 @@ public class GroupMessageProcessor {
}
// 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);
} else {
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);
@ -216,7 +207,7 @@ public class GroupMessageProcessor {
@NonNull SignalServiceGroup group,
@NonNull GroupRecord record)
{
if (record.getMembers().contains(Address.Companion.fromSerialized(content.getSender()))) {
if (record.getMembers().contains(Address.fromSerialized(content.getSender()))) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
@ -237,8 +228,8 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT);
if (members.contains(Address.Companion.fromExternal(context, content.getSender()))) {
database.removeMember(id, Address.Companion.fromExternal(context, content.getSender()));
if (members.contains(Address.fromExternal(context, content.getSender()))) {
database.removeMember(id, Address.fromExternal(context, content.getSender()));
if (outgoing) database.setActive(id, false);
return storeMessage(context, content, group, builder.build(), outgoing);
@ -262,11 +253,11 @@ public class GroupMessageProcessor {
try {
if (outgoing) {
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);
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, 0, null, Collections.emptyList(), Collections.emptyList());
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);
long messageId;
if (existingMessage != null) {
@ -281,7 +272,7 @@ public class GroupMessageProcessor {
} else {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
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);
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> {
@Override
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.loki.api.PrepareAttachmentAudioExtrasJob;
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.NullMessageSendJob;
@ -32,7 +31,6 @@ public final class JobManagerFactories {
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
put(ClosedGroupUpdateMessageSendJob.KEY, new ClosedGroupUpdateMessageSendJob.Factory());
put(ClosedGroupUpdateMessageSendJobV2.KEY, new ClosedGroupUpdateMessageSendJobV2.Factory());
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
put(MmsDownloadJob.KEY, new MmsDownloadJob.Factory());

View File

@ -206,7 +206,7 @@ public class MmsDownloadJob extends BaseJob {
Address from;
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) {
from = notificationFrom;
} else {
@ -215,18 +215,18 @@ public class MmsDownloadJob extends BaseJob {
if (retrieved.getTo() != null) {
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) {
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(Address.Companion.fromExternal(context, TextSecurePreferences.getLocalNumber(context)));
members.add(Address.fromExternal(context, TextSecurePreferences.getLocalNumber(context)));
if (retrieved.getBody() != null) {
body = PartParser.getMessageText(retrieved.getBody());
@ -249,7 +249,7 @@ public class MmsDownloadJob extends BaseJob {
}
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);

View File

@ -102,7 +102,7 @@ public class MmsReceiveJob extends BaseJob {
private boolean isBlocked(GenericPdu pdu) {
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();
}

View File

@ -68,7 +68,6 @@ import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.linkpreview.Link;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
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.LokiMessageDatabase;
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.MultiDeviceProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
@ -292,7 +290,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SessionMetaProtocol.handleProfileKeyUpdate(context, content);
}
if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.Companion.fromSerialized(content.getSender()))) {
if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) {
handleNeedsDeliveryReceipt(content, message);
}
} else if (content.getSyncMessage().isPresent()) {
@ -308,7 +306,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
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()) {
// ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
@ -351,7 +349,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull Optional<Long> smsMessageId)
{
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.getTimestamp(),
"", 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())) {
Address targetAddress = masterRecipient.getAddress();
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()) {
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())) {
Address targetAddress = masterRecipient.getAddress();
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()) {
targetAddress = Address.fromSerialized(syncTarget);
}
@ -924,7 +922,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
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);
long threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
threadDB.addSessionRestoreDevice(threadID, sender);
@ -965,9 +963,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
smsDatabase.markAsNoSession(smsMessageId.get());
}
}
// Attempt to recover automatically
ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(sender);
}
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
@ -1008,7 +1003,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{
ApplicationContext.getInstance(context)
.getJobManager()
.add(new SendDeliveryReceiptJob(Address.Companion.fromSerialized(content.getSender()), message.getTimestamp()));
.add(new SendDeliveryReceiptJob(Address.fromSerialized(content.getSender()), message.getTimestamp()));
}
@SuppressLint("DefaultLocale")
@ -1016,7 +1011,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull SignalServiceReceiptMessage message)
{
// Redirect message to master device conversation
Address masterAddress = Address.Companion.fromSerialized(content.getSender());
Address masterAddress = Address.fromSerialized(content.getSender());
if (masterAddress.isContact()) {
Recipient masterRecipient = getMessageMasterDestination(content.getSender());
@ -1037,7 +1032,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
// Redirect message to master device conversation
Address masterAddress = Address.Companion.fromSerialized(content.getSender());
Address masterAddress = Address.fromSerialized(content.getSender());
if (masterAddress.isContact()) {
Recipient masterRecipient = getMessageMasterDestination(content.getSender());
@ -1060,13 +1055,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return;
}
Recipient author = Recipient.from(context, Address.Companion.fromSerialized(content.getSender()), false);
Recipient author = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
long threadId;
if (typingMessage.getGroupId().isPresent()) {
// 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);
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
@ -1103,7 +1098,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
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);
if (message != null) {
@ -1218,49 +1213,49 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
private Recipient getSyncMessageDestination(SentTranscriptMessage message) {
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 {
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) {
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 {
String publicKey = message.getDestination().get();
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
if (publicKey.equals(userPublicKey)) {
return Recipient.from(context, Address.Companion.fromSerialized(userPublicKey), false);
return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
} 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) {
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 {
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) {
if (!PublicKeyValidation.isValid(publicKey)) {
return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false);
return Recipient.from(context, Address.fromSerialized(publicKey), false);
} else {
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
if (publicKey.equals(userPublicKey)) {
return Recipient.from(context, Address.Companion.fromSerialized(userPublicKey), false);
return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
} 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) {
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);
if (threadId > 0) {
@ -1286,7 +1281,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
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()) {
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 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) || (isContentMessage && shouldIgnoreContentMessage);
return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage);
} else {
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.impl.NetworkConstraint;
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.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
@ -157,14 +156,14 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
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 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<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.Companion.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();
Set<Address> successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.Companion.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet());
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.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList();
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<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).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) {
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.Companion.fromSerialized(success.getAddress().getNumber()),
DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.fromSerialized(success.getAddress().getNumber()),
messageId,
success.getSuccess().isUnidentified());
}
@ -232,19 +231,11 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
private List<SendMessageResult> deliver(OutgoingMediaMessage message, @NonNull List<Address> destinations)
throws IOException, UntrustedIdentityException, UndeliverableMessageException {
// Loki - The user shouldn't be able to message RSS feeds
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<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(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
.toList();
@ -306,7 +297,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Override
public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
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);
}

View File

@ -116,7 +116,7 @@ public class PushGroupUpdateJob extends BaseJob implements InjectableType {
.withName(record.get().getTitle())
.build();
Address groupAddress = Address.Companion.fromSerialized(GroupUtil.getEncodedClosedGroupID(groupId));
Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedClosedGroupID(groupId));
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
@ -126,7 +126,7 @@ public class PushGroupUpdateJob extends BaseJob implements InjectableType {
.build();
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);
}

View File

@ -212,7 +212,7 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
} catch (UntrustedIdentityException uie) {
warn(TAG, "Failure", uie);
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);
}
} 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) {
long templateMessageID = data.getLong(KEY_TEMPLATE_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);
}
}

View File

@ -25,7 +25,7 @@ public abstract class PushReceivedJob extends BaseJob {
synchronized (RECEIVE_LOCK) {
try {
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);
if (!isActiveNumber(recipient)) {
@ -54,7 +54,7 @@ public abstract class PushReceivedJob extends BaseJob {
@SuppressLint("DefaultLocale")
private void handleReceipt(SignalServiceEnvelope envelope) {
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());
}

View File

@ -155,7 +155,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
} catch (UntrustedIdentityException e) {
warn(TAG, "Couldn't send message due to error: ", e);
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.markAsPush(record.getId());
}
@ -266,7 +266,7 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
public @NonNull PushTextSendJob create(@NonNull Parameters parameters, @NonNull Data data) {
long templateMessageID = data.getLong(KEY_TEMPLATE_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);
}
}

View File

@ -82,7 +82,7 @@ public class RequestGroupInfoJob extends BaseJob implements InjectableType {
.build();
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);
}

View File

@ -134,7 +134,7 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType
@Override
public @NonNull RetrieveProfileAvatarJob create(@NonNull Parameters parameters, @NonNull Data data) {
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));
}
}

View File

@ -86,7 +86,7 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType {
timestamp);
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);
}
@ -105,7 +105,7 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType {
@Override
public @NonNull SendDeliveryReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) {
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_TIMESTAMP));
}

View File

@ -91,7 +91,7 @@ public class SendReadReceiptJob extends BaseJob implements InjectableType {
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
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);
}
@ -109,7 +109,7 @@ public class SendReadReceiptJob extends BaseJob implements InjectableType {
public static final class Factory implements Job.Factory<SendReadReceiptJob> {
@Override
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[] ids = data.hasLongArray(KEY_MESSAGE_IDS) ? data.getLongArray(KEY_MESSAGE_IDS) : new long[0];
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.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.session.libsession.messaging.threads.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager
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.utilities.fadeIn
import org.thoughtcrime.securesms.loki.utilities.fadeOut
import org.thoughtcrime.securesms.mms.GlideApp
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.ThemeUtil
import org.session.libsignal.service.loki.utilities.toHexString
import java.io.IOException
class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
@ -249,7 +249,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
var isSSKBasedClosedGroup: Boolean
var groupPublicKey: String?
try {
groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(groupID).toHexString()
groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey)
} catch (e: IOException) {
groupPublicKey = null
@ -299,7 +299,7 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() {
isLoading = false
finish()
}.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()
loader.fadeOut()
isLoading = false

View File

@ -29,10 +29,10 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.ConversationActivity
import org.session.libsession.messaging.threads.Address
import org.session.libsession.utilities.GroupUtil
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.model.ThreadRecord
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.utilities.*
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.libsignal.service.loki.protocol.mentions.MentionsManager
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.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.loki.dialogs.*
@ -176,7 +175,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
application.publicChatManager.startPollersIfNeeded()
}
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application)
IP2Country.configureIfNeeded(this)
application.registerForFCMIfNeeded(false)
// Observe blocked contacts changed events
@ -339,7 +337,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
var isSSKBasedClosedGroup: Boolean
var groupPublicKey: String?
try {
groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(recipient.address.toString()).toHexString()
groupPublicKey = GroupUtil.doubleDecodeGroupID(recipient.address.toString()).toHexString()
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)
} catch (e: IOException) {
groupPublicKey = null
@ -347,7 +345,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
}
if (isSSKBasedClosedGroup) {
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()
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 {
// isClosedGroup can always be false as it's only used in the context of legacy closed groups
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
sentTime, serializedContentMessage, false, ttl, false,
sentTime, serializedContentMessage, false, ttl,
true, false, false, Optional.absent())
} catch (e: Exception) {
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)
try {
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
Date().time, serializedContentMessage, false, ttl, false,
Date().time, serializedContentMessage, false, ttl,
false, false, false, Optional.absent())
} catch (e: Exception) {
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) {
val recipient = recipient(context, publicKey)
if (recipient.isGroupRecipient) { return }
if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) {
return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey)
}
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
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);
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));

View File

@ -83,7 +83,7 @@ public class IncomingMediaMessage {
this.quote = quote.orNull();
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;
this.attachments.addAll(PointerAttachment.forPointers(attachments));

View File

@ -50,7 +50,7 @@ public class QuoteId {
public static @Nullable QuoteId deserialize(@NonNull String serialized) {
try {
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) {
Log.e(TAG, "Failed to deserialize from json", e);
return null;

View File

@ -128,7 +128,7 @@ public class SearchRepository {
private CursorList<ThreadRecord> queryConversations(@NonNull String 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);
return conversations != null ? new CursorList<>(conversations, new ThreadModelBuilder(threadDatabase))
@ -179,7 +179,7 @@ public class SearchRepository {
@Override
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);
}
}
@ -208,8 +208,8 @@ public class SearchRepository {
@Override
public MessageResult build(@NonNull Cursor cursor) {
Address conversationAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndex(SearchDatabase.CONVERSATION_ADDRESS)));
Address messageAddress = Address.Companion.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SearchDatabase.MESSAGE_ADDRESS)));
Address conversationAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndex(SearchDatabase.CONVERSATION_ADDRESS)));
Address messageAddress = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(SearchDatabase.MESSAGE_ADDRESS)));
Recipient conversationRecipient = Recipient.from(context, conversationAddress, false);
Recipient messageRecipient = Recipient.from(context, messageAddress, false);
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) {
try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
Address address = Address.Companion.fromSerialized(senderPublicKey);
Address address = Address.fromSerialized(senderPublicKey);
Recipient recipient = Recipient.from(context, address, false);
Optional<SignalServiceGroup> groupInfo = Optional.absent();
if (content.getDataMessage().hasGroup()) {

View File

@ -47,7 +47,7 @@ public class QuickResponseService extends IntentService {
number = URLDecoder.decode(number);
}
Address address = Address.Companion.fromExternal(this, number);
Address address = Address.fromExternal(this, number);
Recipient recipient = Recipient.from(this, address, false);
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
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) {
this.message = message.getDisplayMessageBody();
this.sender = Address.Companion.fromSerialized(message.getDisplayOriginatingAddress());
this.sender = Address.fromSerialized(message.getDisplayOriginatingAddress());
this.senderDeviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
this.protocol = message.getProtocolIdentifier();
this.serviceCenterAddress = message.getServiceCenterAddress();
@ -79,7 +79,7 @@ public class IncomingTextMessage implements Parcelable {
this.unidentified = unidentified;
if (group.isPresent()) {
this.groupId = Address.Companion.fromSerialized(GroupUtil.getEncodedId(group.get()));
this.groupId = Address.fromSerialized(GroupUtil.getEncodedId(group.get()));
} else {
this.groupId = null;
}

View File

@ -222,7 +222,7 @@ public class MessageSender {
MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
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()) {
attachmentDatabase.markAttachmentUploaded(messageId, attachment);
@ -249,7 +249,7 @@ public class MessageSender {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
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.markUnidentified(messageId, true);

View File

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

View File

@ -216,7 +216,7 @@ public class SignalServiceMessageSender {
SignalServiceReceiptMessage message)
throws IOException {
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);
}
@ -244,9 +244,8 @@ public class SignalServiceMessageSender {
{
byte[] content = createMessageContent(message, recipient);
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;
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
// boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent();
@ -854,8 +853,7 @@ public class SignalServiceMessageSender {
SignalServiceAddress recipient = recipientIterator.next();
try {
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store);
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer, Optional.absent());
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, true, isClosedGroup, notifyPNServer, Optional.absent());
results.add(result);
} catch (UnregisteredUserException e) {
Log.w(TAG, e);

View File

@ -25,14 +25,6 @@ public class SessionManagementProtocol(private val sessionResetImpl: SessionRese
// endregion
// 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.
*/