feat: add self sending syncTarget messages

This commit is contained in:
jubb 2021-02-08 16:57:12 +11:00
parent e4a1de24f5
commit 57d532f4b8
17 changed files with 314 additions and 296 deletions

View File

@ -686,6 +686,15 @@ public class SmsDatabase extends MessagingDatabase {
return insertMessageInbox(message, Types.BASE_INBOX_TYPE, serverTimestamp); return insertMessageInbox(message, Types.BASE_INBOX_TYPE, serverTimestamp);
} }
public Optional<InsertResult> insertMessageOutbox(long threadId, OutgoingTextMessage message, long serverTimestamp) {
long messageId = insertMessageOutbox(threadId, message, false, serverTimestamp, null);
if (messageId == -1) {
return Optional.absent();
}
markAsSent(messageId, true);
return Optional.fromNullable(new InsertResult(messageId, threadId));
}
public long insertMessageOutbox(long threadId, OutgoingTextMessage message, public long insertMessageOutbox(long threadId, OutgoingTextMessage message,
boolean forceSms, long date, InsertListener insertListener) boolean forceSms, long date, InsertListener insertListener)
{ {
@ -716,9 +725,17 @@ public class SmsDatabase extends MessagingDatabase {
contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum());
contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum()); contentValues.put(READ_RECEIPT_COUNT, Stream.of(earlyReadReceipts.values()).mapToLong(Long::longValue).sum());
SQLiteDatabase readDb = databaseHelper.getReadableDatabase();
Cursor existingRecord = readDb.query(TABLE_NAME, null, String.format("%s = ? AND %s = ? AND %s = ?",ADDRESS, THREAD_ID, DATE_SENT),
new String[] { address.serialize(), Long.toString(threadId), Long.toString(date) }, null, null, null);
int existingRecordCount = existingRecord.getCount();
if (existingRecordCount > 0) {
// return -1 because record exists from Address to ThreadID with the same date sent (probably sent from us)
return -1;
}
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues); long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
if (insertListener != null) { if (insertListener != null) {
insertListener.onComplete(); insertListener.onComplete();
} }

View File

@ -28,6 +28,7 @@ import org.session.libsignal.metadata.ProtocolNoSessionException;
import org.session.libsignal.metadata.ProtocolUntrustedIdentityException; import org.session.libsignal.metadata.ProtocolUntrustedIdentityException;
import org.session.libsignal.metadata.SelfSendException; import org.session.libsignal.metadata.SelfSendException;
import org.session.libsignal.service.loki.api.crypto.SessionProtocol; import org.session.libsignal.service.loki.api.crypto.SessionProtocol;
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
import org.session.libsignal.utilities.PromiseUtilities; import org.session.libsignal.utilities.PromiseUtilities;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
@ -568,6 +569,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{ {
Recipient originalRecipient = getMessageDestination(content, message); Recipient originalRecipient = getMessageDestination(content, message);
Recipient masterRecipient = getMessageMasterDestination(content.getSender()); Recipient masterRecipient = getMessageMasterDestination(content.getSender());
String syncTarget = message.getSyncTarget().orNull();
notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice()); notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice());
@ -582,75 +585,79 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
masterAddress = getMessageMasterDestination(content.getSender()).getAddress(); masterAddress = getMessageMasterDestination(content.getSender()).getAddress();
} }
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1, if (syncTarget != null && !syncTarget.isEmpty()) {
message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(), // OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(masterAddress, message.getTimestamp(), -1,
quote, sharedContacts, linkPreviews, sticker); // message.getExpiresInSeconds() * 1000L, false, )
} else {
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
quote, sharedContacts, linkPreviews, sticker);
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
database.beginTransaction();
MmsDatabase database = DatabaseFactory.getMmsDatabase(context); // Ignore message if it has no body and no attachments
database.beginTransaction(); if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
return;
}
// Ignore message if it has no body and no attachments Optional<InsertResult> insertResult;
if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
return;
}
Optional<InsertResult> insertResult; try {
if (message.isGroupMessage()) {
insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1, content.getTimestamp());
} else {
insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
}
try { if (insertResult.isPresent()) {
if (message.isGroupMessage()) { List<DatabaseAttachment> allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId());
insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1, content.getTimestamp()); List<DatabaseAttachment> stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList();
} else { List<DatabaseAttachment> attachments = Stream.of(allAttachments).filterNot(Attachment::isSticker).toList();
insertResult = database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
forceStickerDownloadIfNecessary(stickerAttachments);
for (DatabaseAttachment attachment : attachments) {
ApplicationContext.getInstance(context).getJobManager().add(new AttachmentDownloadJob(insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
}
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
}
database.setTransactionSuccessful();
}
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
} finally {
database.endTransaction();
} }
if (insertResult.isPresent()) { if (insertResult.isPresent()) {
List<DatabaseAttachment> allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(insertResult.get().getMessageId()); messageNotifier.updateNotification(context, insertResult.get().getThreadId());
List<DatabaseAttachment> stickerAttachments = Stream.of(allAttachments).filter(Attachment::isSticker).toList();
List<DatabaseAttachment> attachments = Stream.of(allAttachments).filterNot(Attachment::isSticker).toList();
forceStickerDownloadIfNecessary(stickerAttachments);
for (DatabaseAttachment attachment : attachments) {
ApplicationContext.getInstance(context).getJobManager().add(new AttachmentDownloadJob(insertResult.get().getMessageId(), attachment.getAttachmentId(), false));
}
if (smsMessageId.isPresent()) {
DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get());
}
database.setTransactionSuccessful();
}
} catch (MmsException e) {
throw new StorageFailedException(e, content.getSender(), content.getSenderDevice());
} finally {
database.endTransaction();
}
if (insertResult.isPresent()) {
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
if (insertResult.isPresent()) {
InsertResult result = insertResult.get();
// Loki - Cache the user hex encoded public key (for mentions)
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context);
MentionsManager.shared.cache(content.getSender(), result.getThreadId());
// Loki - Store message open group server ID if needed
if (messageServerIDOrNull.isPresent()) {
long messageID = result.getMessageId();
long messageServerID = messageServerIDOrNull.get();
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
lokiMessageDatabase.setServerID(messageID, messageServerID);
} }
// Loki - Update mapping of message ID to original thread ID if (insertResult.isPresent()) {
if (result.getMessageId() > -1) { InsertResult result = insertResult.get();
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); // Loki - Cache the user hex encoded public key (for mentions)
long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient); MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context);
lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId); MentionsManager.shared.cache(content.getSender(), result.getThreadId());
// Loki - Store message open group server ID if needed
if (messageServerIDOrNull.isPresent()) {
long messageID = result.getMessageId();
long messageServerID = messageServerIDOrNull.get();
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
lokiMessageDatabase.setServerID(messageID, messageServerID);
}
// Loki - Update mapping of message ID to original thread ID
if (result.getMessageId() > -1) {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
long originalThreadId = threadDatabase.getOrCreateThreadIdFor(originalRecipient);
lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId);
}
} }
} }
} }
@ -769,6 +776,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
String body = message.getBody().isPresent() ? message.getBody().get() : ""; String body = message.getBody().isPresent() ? message.getBody().get() : "";
Recipient originalRecipient = getMessageDestination(content, message); Recipient originalRecipient = getMessageDestination(content, message);
Recipient masterRecipient = getMessageMasterDestination(content.getSender()); Recipient masterRecipient = getMessageMasterDestination(content.getSender());
String syncTarget = message.getSyncTarget().orNull();
if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) { if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) {
handleExpirationUpdate(content, message, Optional.absent()); handleExpirationUpdate(content, message, Optional.absent());
@ -778,15 +786,46 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) { if (smsMessageId.isPresent() && !message.getGroupInfo().isPresent()) {
threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second; threadId = database.updateBundleMessageBody(smsMessageId.get(), body).second;
} else if (syncTarget != null && !syncTarget.isEmpty()) {
Address targetAddress = Address.fromSerialized(syncTarget);
OutgoingTextMessage tm = new OutgoingTextMessage(Recipient.from(context, targetAddress, false),
body, message.getExpiresInSeconds(), -1);
// Ignore the message if it has no body
if (tm.getMessageBody().length() == 0) { return; }
// Check if we have the thread already
long threadID = DatabaseFactory.getLokiThreadDatabase(context).getThreadID(syncTarget);
// Insert the message into the database
Optional<InsertResult> insertResult;
insertResult = database.insertMessageOutbox(threadID, tm, content.getTimestamp());
if (insertResult.isPresent()) {
threadId = insertResult.get().getThreadId();
}
if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get());
if (threadId != null) {
messageNotifier.updateNotification(context, threadId);
}
if (insertResult.isPresent()) {
InsertResult result = insertResult.get();
// Loki - Cache the user hex encoded public key (for mentions)
MentionManagerUtilities.INSTANCE.populateUserPublicKeyCacheIfNeeded(result.getThreadId(), context);
MentionsManager.shared.cache(content.getSender(), result.getThreadId());
}
} else { } else {
notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice()); notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice());
Address masterAddress = masterRecipient.getAddress(); Address masterAddress = masterRecipient.getAddress();
if (message.isGroupMessage()) {
masterAddress = getMessageMasterDestination(content.getSender()).getAddress();
}
IncomingTextMessage tm = new IncomingTextMessage(masterAddress, IncomingTextMessage tm = new IncomingTextMessage(masterAddress,
content.getSenderDevice(), content.getSenderDevice(),
message.getTimestamp(), body, message.getTimestamp(), body,

View File

@ -15,6 +15,7 @@ import org.session.libsession.messaging.threads.recipients.Recipient;
import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.Address;
import org.session.libsession.utilities.GroupUtil; import org.session.libsession.utilities.GroupUtil;
import org.session.libsession.utilities.TextSecurePreferences;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
@ -143,6 +144,9 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
List<NetworkFailure> existingNetworkFailures = message.getNetworkFailures(); List<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
List<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches(); List<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
SignalServiceAddress localAddress = new SignalServiceAddress(userPublicKey);
if (database.isSent(messageId)) { if (database.isSent(messageId)) {
log(TAG, "Message " + messageId + " was already sent. Ignoring."); log(TAG, "Message " + messageId + " was already sent. Ignoring.");
return; return;
@ -190,6 +194,22 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
} }
if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) { if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) {
Address address = message.getRecipient().getAddress();
if (!address.isOpenGroup()) {
try {
SignalServiceDataMessage selfSend = getDataMessage(address, message)
.withSyncTarget(address.toGroupString())
.build();
// send to ourselves to sync multi-device
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, selfSend);
if (selfSendResult.getLokiAPIError() != null) {
throw selfSendResult.getLokiAPIError();
}
} catch (Exception e) {
Log.e("Loki", "Error sending message to ourselves", e);
}
}
database.markAsSent(messageId, true); database.markAsSent(messageId, true);
markAttachmentsUploaded(messageId, message.getAttachments()); markAttachmentsUploaded(messageId, message.getAttachments());
@ -238,25 +258,18 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
// return results; // return results;
// } // }
String groupId = address.toGroupString();
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
Optional<Quote> quote = getQuoteFor(message);
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
List<SharedContact> sharedContacts = getSharedContactsFor(message);
List<Preview> previews = getPreviewsFor(message);
List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList(); List<SignalServiceAddress> addresses = Stream.of(destinations).map(this::getPushAddress).toList();
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses) List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(addresses)
.map(a -> Address.Companion.fromSerialized(a.getNumber())) .map(a -> Address.Companion.fromSerialized(a.getNumber()))
.map(a -> Recipient.from(context, a, false)) .map(a -> Recipient.from(context, a, false))
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)) .map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
.toList(); .toList();
SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL;
if (message.isGroup() && address.isClosedGroup()) { if (message.isGroup() && address.isClosedGroup()) {
SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL;
String groupId = address.toGroupString();
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
// Loki - Only send GroupUpdate or GroupQuit messages to closed groups // Loki - Only send GroupUpdate or GroupQuit messages to closed groups
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message; OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
GroupContext groupContext = groupMessage.getGroupContext(); GroupContext groupContext = groupMessage.getGroupContext();
@ -271,25 +284,40 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupDataMessage); return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupDataMessage);
} else { } else {
SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedGroupIDAsData(groupId), groupType); SignalServiceDataMessage groupMessage = getDataMessage(address, message).build();
SignalServiceDataMessage groupMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.asGroupMessage(group)
.withAttachments(attachmentPointers)
.withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000))
.asExpirationUpdate(message.isExpirationUpdate())
.withProfileKey(profileKey.orNull())
.withQuote(quote.orNull())
.withSticker(sticker.orNull())
.withSharedContacts(sharedContacts)
.withPreviews(previews)
.build();
return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupMessage); return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupMessage);
} }
} }
public SignalServiceDataMessage.Builder getDataMessage(Address address, OutgoingMediaMessage message) {
SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL;
String groupId = address.toGroupString();
Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
Optional<Quote> quote = getQuoteFor(message);
Optional<SignalServiceDataMessage.Sticker> sticker = getStickerFor(message);
List<SharedContact> sharedContacts = getSharedContactsFor(message);
List<Preview> previews = getPreviewsFor(message);
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> attachmentPointers = getAttachmentPointersFor(attachments);
SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedGroupIDAsData(groupId), groupType);
return SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.asGroupMessage(group)
.withAttachments(attachmentPointers)
.withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000))
.asExpirationUpdate(message.isExpirationUpdate())
.withProfileKey(profileKey.orNull())
.withQuote(quote.orNull())
.withSticker(sticker.orNull())
.withSharedContacts(sharedContacts)
.withPreviews(previews);
}
public static class Factory implements Job.Factory<PushGroupSendJob> { public static class Factory implements Job.Factory<PushGroupSendJob> {
@Override @Override
public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull Data data) { public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull Data data) {

View File

@ -245,7 +245,9 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
{ {
try { try {
Recipient recipient = Recipient.from(context, destination, false); Recipient recipient = Recipient.from(context, destination, false);
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
SignalServiceAddress address = getPushAddress(recipient.getAddress()); SignalServiceAddress address = getPushAddress(recipient.getAddress());
SignalServiceAddress localAddress = new SignalServiceAddress(userPublicKey);
List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList(); List<Attachment> attachments = Stream.of(message.getAttachments()).filterNot(Attachment::isSticker).toList();
List<SignalServiceAttachment> serviceAttachments = getAttachmentPointersFor(attachments); List<SignalServiceAttachment> serviceAttachments = getAttachmentPointersFor(attachments);
Optional<byte[]> profileKey = getProfileKey(message.getRecipient()); Optional<byte[]> profileKey = getProfileKey(message.getRecipient());
@ -254,6 +256,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
List<SharedContact> sharedContacts = getSharedContactsFor(message); List<SharedContact> sharedContacts = getSharedContactsFor(message);
List<Preview> previews = getPreviewsFor(message); List<Preview> previews = getPreviewsFor(message);
Optional<UnidentifiedAccessPair> unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(context, recipient);
SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder()
.withBody(message.getBody()) .withBody(message.getBody())
.withAttachments(serviceAttachments) .withAttachments(serviceAttachments)
@ -267,6 +271,20 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
.asExpirationUpdate(message.isExpirationUpdate()) .asExpirationUpdate(message.isExpirationUpdate())
.build(); .build();
SignalServiceDataMessage mediaSelfSendMessage = SignalServiceDataMessage.newBuilder()
.withBody(message.getBody())
.withAttachments(serviceAttachments)
.withTimestamp(message.getSentTimeMillis())
.withSyncTarget(destination.serialize())
.withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull())
.withQuote(quote.orNull())
.withSticker(sticker.orNull())
.withSharedContacts(sharedContacts)
.withPreviews(previews)
.asExpirationUpdate(message.isExpirationUpdate())
.build();
if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
// Loki - Device link messages don't go through here // Loki - Device link messages don't go through here
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
@ -275,11 +293,24 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
messageSender.sendMessage(syncMessage, syncAccess); messageSender.sendMessage(syncMessage, syncAccess);
return syncAccess.isPresent(); return syncAccess.isPresent();
} else { } else {
SendMessageResult result = messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage); SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccessPair, mediaMessage);
if (result.getLokiAPIError() != null) { if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError(); throw result.getLokiAPIError();
} else { } else {
return result.getSuccess().isUnidentified(); boolean isUnidentified = result.getSuccess().isUnidentified();
try {
// send to ourselves to sync multi-device
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, mediaSelfSendMessage);
if (selfSendResult.getLokiAPIError() != null) {
throw selfSendResult.getLokiAPIError();
}
} catch (Exception e) {
Log.e("Loki", "Error sending message to ourselves", e);
}
return isUnidentified;
} }
} }
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {

View File

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.session.libsession.messaging.jobs.Data; import org.session.libsession.messaging.jobs.Data;
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
import org.session.libsignal.utilities.logging.Log; import org.session.libsignal.utilities.logging.Log;
import org.session.libsession.messaging.threads.Address; import org.session.libsession.messaging.threads.Address;
@ -192,8 +193,10 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException, SnodeAPI.Error throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException, SnodeAPI.Error
{ {
try { try {
String userPublicKey = TextSecurePreferences.getLocalNumber(context);
Recipient recipient = Recipient.from(context, destination, false); Recipient recipient = Recipient.from(context, destination, false);
SignalServiceAddress address = getPushAddress(recipient.getAddress()); SignalServiceAddress address = getPushAddress(recipient.getAddress());
SignalServiceAddress localAddress = new SignalServiceAddress(userPublicKey);
Optional<byte[]> profileKey = getProfileKey(recipient); Optional<byte[]> profileKey = getProfileKey(recipient);
Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient); Optional<UnidentifiedAccessPair> unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient);
@ -205,13 +208,21 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
// } // }
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent()) .withTimestamp(message.getDateSent())
.withBody(message.getBody()) .withBody(message.getBody())
.withExpiration((int)(message.getExpiresIn() / 1000)) .withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull()) .withProfileKey(profileKey.orNull())
// .withPreKeyBundle(preKeyBundle) .asEndSessionMessage(message.isEndSession())
.asEndSessionMessage(message.isEndSession()) .build();
.build();
SignalServiceDataMessage textSecureSelfSendMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
.withBody(message.getBody())
.withSyncTarget(destination.serialize())
.withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull())
.asEndSessionMessage(message.isEndSession())
.build();
if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) { if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
// Loki - Device link messages don't go through here // Loki - Device link messages don't go through here
@ -225,7 +236,19 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
if (result.getLokiAPIError() != null) { if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError(); throw result.getLokiAPIError();
} else { } else {
return result.getSuccess().isUnidentified(); boolean isUnidentified = result.getSuccess().isUnidentified();
try {
// send to ourselves to sync multi-device
Optional<UnidentifiedAccessPair> syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, textSecureSelfSendMessage);
if (selfSendResult.getLokiAPIError() != null) {
throw selfSendResult.getLokiAPIError();
}
} catch (Exception e) {
Log.e("Loki", "Error sending message to ourselves", e);
}
return isUnidentified;
} }
} }
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {

View File

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki.protocol
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import org.session.libsession.messaging.jobs.Data import org.session.libsession.messaging.jobs.Data
import org.session.libsignal.libsignal.util.guava.Optional
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.Job
@ -128,7 +129,7 @@ class ClosedGroupUpdateMessageSendJob private constructor(parameters: Parameters
// isClosedGroup can always be false as it's only used in the context of legacy closed groups // isClosedGroup can always be false as it's only used in the context of legacy closed groups
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
Date().time, serializedContentMessage, false, ttl, false, Date().time, serializedContentMessage, false, ttl, false,
useFallbackEncryption, false, false, false) useFallbackEncryption, false, false, Optional.absent())
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.") Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.")
} }

View File

@ -9,6 +9,7 @@ import org.session.libsession.messaging.jobs.Data
import org.session.libsignal.libsignal.ecc.DjbECPrivateKey import org.session.libsignal.libsignal.ecc.DjbECPrivateKey
import org.session.libsignal.libsignal.ecc.DjbECPublicKey import org.session.libsignal.libsignal.ecc.DjbECPublicKey
import org.session.libsignal.libsignal.ecc.ECKeyPair import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.libsignal.util.guava.Optional
import org.session.libsignal.service.api.push.SignalServiceAddress import org.session.libsignal.service.api.push.SignalServiceAddress
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities import org.session.libsignal.service.loki.protocol.meta.TTLUtilities
@ -221,7 +222,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete
// isClosedGroup can always be false as it's only used in the context of legacy closed groups // isClosedGroup can always be false as it's only used in the context of legacy closed groups
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
Date().time, serializedContentMessage, false, ttl, false, Date().time, serializedContentMessage, false, ttl, false,
true, false, false, false) true, false, false, Optional.absent())
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.") Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.")
} }

View File

@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.BaseJob import org.thoughtcrime.securesms.jobs.BaseJob
import org.session.libsignal.utilities.logging.Log import org.session.libsignal.utilities.logging.Log
import org.session.libsession.messaging.threads.recipients.Recipient import org.session.libsession.messaging.threads.recipients.Recipient
import org.session.libsignal.libsignal.util.guava.Optional
import org.session.libsignal.service.api.push.SignalServiceAddress import org.session.libsignal.service.api.push.SignalServiceAddress
import org.session.libsignal.service.internal.push.SignalServiceProtos import org.session.libsignal.service.internal.push.SignalServiceProtos
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities import org.session.libsignal.service.loki.protocol.meta.TTLUtilities
@ -56,7 +57,7 @@ class NullMessageSendJob private constructor(parameters: Parameters, private val
try { try {
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess, messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
Date().time, serializedContentMessage, false, ttl, false, Date().time, serializedContentMessage, false, ttl, false,
false, false, false, false) false, false, false, Optional.absent())
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.") Log.d("Loki", "Failed to send null message to: $publicKey due to error: $e.")
throw e throw e

View File

@ -6,12 +6,7 @@ public class IncomingEncryptedMessage extends IncomingTextMessage {
super(base, newBody); super(base, newBody);
} }
@Override @Override
public IncomingTextMessage withMessageBody(String body) {
return new IncomingEncryptedMessage(this, body);
}
@Override
public boolean isSecureMessage() { public boolean isSecureMessage() {
return true; return true;
} }

View File

@ -10,12 +10,7 @@ public class IncomingEndSessionMessage extends IncomingTextMessage {
super(base, newBody); super(base, newBody);
} }
@Override @Override
public IncomingEndSessionMessage withMessageBody(String messageBody) {
return new IncomingEndSessionMessage(this, messageBody);
}
@Override
public boolean isEndSession() { public boolean isEndSession() {
return true; return true;
} }

View File

@ -11,12 +11,7 @@ public class IncomingGroupMessage extends IncomingTextMessage {
this.groupContext = groupContext; this.groupContext = groupContext;
} }
@Override @Override
public IncomingGroupMessage withMessageBody(String body) {
return new IncomingGroupMessage(this, groupContext, body);
}
@Override
public boolean isGroup() { public boolean isGroup() {
return true; return true;
} }

View File

@ -9,12 +9,7 @@ public class IncomingPreKeyBundleMessage extends IncomingTextMessage {
this.legacy = legacy; this.legacy = legacy;
} }
@Override @Override
public IncomingPreKeyBundleMessage withMessageBody(String messageBody) {
return new IncomingPreKeyBundleMessage(this, messageBody, legacy);
}
@Override
public boolean isLegacyPreKeyBundle() { public boolean isLegacyPreKeyBundle() {
return legacy; return legacy;
} }

View File

@ -175,10 +175,6 @@ public class IncomingTextMessage implements Parcelable {
return message; return message;
} }
public IncomingTextMessage withMessageBody(String message) {
return new IncomingTextMessage(this, message);
}
public Address getSender() { public Address getSender() {
return sender; return sender;
} }
@ -250,7 +246,6 @@ public class IncomingTextMessage implements Parcelable {
public boolean isUnidentified() { public boolean isUnidentified() {
return unidentified; return unidentified;
} }
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;

View File

@ -152,6 +152,7 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
val UNKNOWN = Address("Unknown") val UNKNOWN = Address("Unknown")
private val TAG = Address::class.java.simpleName private val TAG = Address::class.java.simpleName
private val cachedFormatter = AtomicReference<Pair<String, ExternalAddressFormatter>>() private val cachedFormatter = AtomicReference<Pair<String, ExternalAddressFormatter>>()
@JvmStatic
fun fromSerialized(serialized: String): Address { fun fromSerialized(serialized: String): Address {
return Address(serialized) return Address(serialized)
} }

View File

@ -26,10 +26,6 @@ import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
import org.session.libsignal.service.api.messages.SignalServiceGroup; import org.session.libsignal.service.api.messages.SignalServiceGroup;
import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage;
import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; import org.session.libsignal.service.api.messages.SignalServiceTypingMessage;
import org.session.libsignal.service.api.messages.calls.AnswerMessage;
import org.session.libsignal.service.api.messages.calls.IceUpdateMessage;
import org.session.libsignal.service.api.messages.calls.OfferMessage;
import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage;
import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage; import org.session.libsignal.service.api.messages.multidevice.BlockedListMessage;
import org.session.libsignal.service.api.messages.multidevice.ConfigurationMessage; import org.session.libsignal.service.api.messages.multidevice.ConfigurationMessage;
import org.session.libsignal.service.api.messages.multidevice.ReadMessage; import org.session.libsignal.service.api.messages.multidevice.ReadMessage;
@ -51,7 +47,6 @@ import org.session.libsignal.service.internal.push.PushServiceSocket;
import org.session.libsignal.service.internal.push.PushTransportDetails; import org.session.libsignal.service.internal.push.PushTransportDetails;
import org.session.libsignal.service.internal.push.SignalServiceProtos; import org.session.libsignal.service.internal.push.SignalServiceProtos;
import org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer; import org.session.libsignal.service.internal.push.SignalServiceProtos.AttachmentPointer;
import org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage;
import org.session.libsignal.service.internal.push.SignalServiceProtos.Content; import org.session.libsignal.service.internal.push.SignalServiceProtos.Content;
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage; import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage;
import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext;
@ -217,34 +212,14 @@ public class SignalServiceMessageSender {
* @param recipient The sender of the received message you're acknowledging. * @param recipient The sender of the received message you're acknowledging.
* @param message The read receipt to deliver. * @param message The read receipt to deliver.
* @throws IOException * @throws IOException
* @throws UntrustedIdentityException
*/ */
public void sendReceipt(SignalServiceAddress recipient, public void sendReceipt(SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess, Optional<UnidentifiedAccessPair> unidentifiedAccess,
SignalServiceReceiptMessage message) SignalServiceReceiptMessage message)
throws IOException, UntrustedIdentityException throws IOException {
{
byte[] content = createReceiptContent(message); byte[] content = createReceiptContent(message);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store);
sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption, false); sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getWhen(), content, false, message.getTTL(), useFallbackEncryption);
}
/**
* Send a typing indicator.
*
* @param recipient The destination
* @param message The typing indicator to deliver
* @throws IOException
* @throws UntrustedIdentityException
*/
public void sendTyping(SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess,
SignalServiceTypingMessage message)
throws IOException, UntrustedIdentityException
{
byte[] content = createTypingContent(message);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store);
sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), useFallbackEncryption, false);
} }
public void sendTyping(List<SignalServiceAddress> recipients, public void sendTyping(List<SignalServiceAddress> recipients,
@ -256,42 +231,24 @@ public class SignalServiceMessageSender {
sendMessage(0, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), false, false); sendMessage(0, recipients, getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, true, message.getTTL(), false, false);
} }
/**
* Send a call setup message to a single recipient.
*
* @param recipient The message's destination.
* @param message The call message.
* @throws IOException
*/
public void sendCallMessage(SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess,
SignalServiceCallMessage message)
throws IOException, UntrustedIdentityException
{
byte[] content = createCallContent(message);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store);
sendMessage(recipient, getTargetUnidentifiedAccess(unidentifiedAccess), System.currentTimeMillis(), content, false, message.getTTL(), useFallbackEncryption, false);
}
/** /**
* Send a message to a single recipient. * Send a message to a single recipient.
* *
* @param recipient The message's destination. * @param recipient The message's destination.
* @param message The message. * @param message The message.
* @throws UntrustedIdentityException
* @throws IOException * @throws IOException
*/ */
public SendMessageResult sendMessage(long messageID, public SendMessageResult sendMessage(long messageID,
SignalServiceAddress recipient, SignalServiceAddress recipient,
Optional<UnidentifiedAccessPair> unidentifiedAccess, Optional<UnidentifiedAccessPair> unidentifiedAccess,
SignalServiceDataMessage message) SignalServiceDataMessage message)
throws UntrustedIdentityException, IOException throws IOException
{ {
byte[] content = createMessageContent(message, recipient); byte[] content = createMessageContent(message, recipient);
long timestamp = message.getTimestamp(); long timestamp = message.getTimestamp();
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store); boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store);
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL; boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), message.getDeviceLink().isPresent(), useFallbackEncryption, isClosedGroup, false, message.hasVisibleContent()); SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), message.getDeviceLink().isPresent(), useFallbackEncryption, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
// // Loki - This shouldn't get invoked for note to self // // Loki - This shouldn't get invoked for note to self
// boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent(); // boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent();
@ -325,8 +282,7 @@ public class SignalServiceMessageSender {
List<SignalServiceAddress> recipients, List<SignalServiceAddress> recipients,
List<Optional<UnidentifiedAccessPair>> unidentifiedAccess, List<Optional<UnidentifiedAccessPair>> unidentifiedAccess,
SignalServiceDataMessage message) SignalServiceDataMessage message)
throws IOException, UntrustedIdentityException throws IOException {
{
// Loki - We only need the first recipient in the line below. This is because the recipient is only used to determine // Loki - We only need the first recipient in the line below. This is because the recipient is only used to determine
// whether an attachment is being sent to an open group or not. // whether an attachment is being sent to an open group or not.
byte[] content = createMessageContent(message, recipients.get(0)); byte[] content = createMessageContent(message, recipients.get(0));
@ -350,7 +306,7 @@ public class SignalServiceMessageSender {
for (String device : linkedDevices) { for (String device : linkedDevices) {
SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store); boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store);
sendMessage(deviceAsAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryption, true); sendMessage(deviceAsAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryption);
} }
} }
@ -392,18 +348,10 @@ public class SignalServiceMessageSender {
for (String device : linkedDevices) { for (String device : linkedDevices) {
SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device); SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, device, store); boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, device, store);
sendMessageToPrivateChat(0, deviceAsAddress, Optional.<UnidentifiedAccess>absent(), timestamp, content, false, message.getTTL(), useFallbackEncryption, false, false); // sendMessageToPrivateChat(0, deviceAsAddress, Optional.<UnidentifiedAccess>absent(), timestamp, content, false, message.getTTL(), useFallbackEncryption, false, false);
} }
} }
public void setSoTimeoutMillis(long soTimeoutMillis) {
socket.setSoTimeoutMillis(soTimeoutMillis);
}
public void cancelInFlightRequests() {
socket.cancelInFlightRequests();
}
public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) { public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
this.pipe.set(Optional.fromNullable(pipe)); this.pipe.set(Optional.fromNullable(pipe));
this.unidentifiedPipe.set(Optional.fromNullable(unidentifiedPipe)); this.unidentifiedPipe.set(Optional.fromNullable(unidentifiedPipe));
@ -452,9 +400,7 @@ public class SignalServiceMessageSender {
result.getUrl()); result.getUrl());
} }
private void sendMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess) private void sendMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess) {
throws IOException, UntrustedIdentityException
{
} }
@ -494,32 +440,6 @@ public class SignalServiceMessageSender {
{ {
Content.Builder container = Content.newBuilder(); Content.Builder container = Content.newBuilder();
// if (message.getPreKeyBundle().isPresent()) {
// PreKeyBundle preKeyBundle = message.getPreKeyBundle().get();
// PreKeyBundleMessage.Builder preKeyBundleMessageBuilder = PreKeyBundleMessage.newBuilder()
// .setDeviceId(preKeyBundle.getDeviceId())
// .setIdentityKey(ByteString.copyFrom(preKeyBundle.getIdentityKey().serialize()))
// .setPreKeyId(preKeyBundle.getPreKeyId())
// .setPreKey(ByteString.copyFrom(preKeyBundle.getPreKey().serialize()))
// .setSignedKeyId(preKeyBundle.getSignedPreKeyId())
// .setSignedKey(ByteString.copyFrom(preKeyBundle.getSignedPreKey().serialize()))
// .setSignature(ByteString.copyFrom(preKeyBundle.getSignedPreKeySignature()))
// .setIdentityKey(ByteString.copyFrom(preKeyBundle.getIdentityKey().serialize()));
// container.setPreKeyBundleMessage(preKeyBundleMessageBuilder);
// }
// if (message.getDeviceLink().isPresent()) {
// DeviceLink deviceLink = message.getDeviceLink().get();
// SignalServiceProtos.DeviceLinkMessage.Builder deviceLinkMessageBuilder = SignalServiceProtos.DeviceLinkMessage.newBuilder()
// .setPrimaryPublicKey(deviceLink.getMasterPublicKey())
// .setSecondaryPublicKey(deviceLink.getSlavePublicKey())
// .setRequestSignature(ByteString.copyFrom(Objects.requireNonNull(deviceLink.getRequestSignature())));
// if (deviceLink.getAuthorizationSignature() != null) {
// deviceLinkMessageBuilder.setAuthorizationSignature(ByteString.copyFrom(deviceLink.getAuthorizationSignature()));
// }
// container.setDeviceLinkMessage(deviceLinkMessageBuilder.build());
// }
DataMessage.Builder builder = DataMessage.newBuilder(); DataMessage.Builder builder = DataMessage.newBuilder();
List<AttachmentPointer> pointers = createAttachmentPointers(message.getAttachments(), recipient); List<AttachmentPointer> pointers = createAttachmentPointers(message.getAttachments(), recipient);
@ -559,6 +479,10 @@ public class SignalServiceMessageSender {
builder.setProfileKey(ByteString.copyFrom(message.getProfileKey().get())); builder.setProfileKey(ByteString.copyFrom(message.getProfileKey().get()));
} }
if (message.getSyncTarget().isPresent()) {
builder.setSyncTarget(message.getSyncTarget().get());
}
if (message.getQuote().isPresent()) { if (message.getQuote().isPresent()) {
DataMessage.Quote.Builder quoteBuilder = DataMessage.Quote.newBuilder() DataMessage.Quote.Builder quoteBuilder = DataMessage.Quote.newBuilder()
.setId(message.getQuote().get().getId()) .setId(message.getQuote().get().getId())
@ -636,40 +560,6 @@ public class SignalServiceMessageSender {
return container.build().toByteArray(); return container.build().toByteArray();
} }
private byte[] createCallContent(SignalServiceCallMessage callMessage) {
Content.Builder container = Content.newBuilder();
CallMessage.Builder builder = CallMessage.newBuilder();
if (callMessage.getOfferMessage().isPresent()) {
OfferMessage offer = callMessage.getOfferMessage().get();
builder.setOffer(CallMessage.Offer.newBuilder()
.setId(offer.getId())
.setDescription(offer.getDescription()));
} else if (callMessage.getAnswerMessage().isPresent()) {
AnswerMessage answer = callMessage.getAnswerMessage().get();
builder.setAnswer(CallMessage.Answer.newBuilder()
.setId(answer.getId())
.setDescription(answer.getDescription()));
} else if (callMessage.getIceUpdateMessages().isPresent()) {
List<IceUpdateMessage> updates = callMessage.getIceUpdateMessages().get();
for (IceUpdateMessage update : updates) {
builder.addIceUpdate(CallMessage.IceUpdate.newBuilder()
.setId(update.getId())
.setSdp(update.getSdp())
.setSdpMid(update.getSdpMid())
.setSdpMLineIndex(update.getSdpMLineIndex()));
}
} else if (callMessage.getHangupMessage().isPresent()) {
builder.setHangup(CallMessage.Hangup.newBuilder().setId(callMessage.getHangupMessage().get().getId()));
} else if (callMessage.getBusyMessage().isPresent()) {
builder.setBusy(CallMessage.Busy.newBuilder().setId(callMessage.getBusyMessage().get().getId()));
}
container.setCallMessage(builder);
return container.build().toByteArray();
}
private byte[] createMultiDeviceContactsContent(SignalServiceAttachmentStream contacts, boolean complete) private byte[] createMultiDeviceContactsContent(SignalServiceAttachmentStream contacts, boolean complete)
throws IOException throws IOException
{ {
@ -987,6 +877,7 @@ public class SignalServiceMessageSender {
throws IOException throws IOException
{ {
List<SendMessageResult> results = new LinkedList<>(); List<SendMessageResult> results = new LinkedList<>();
SignalServiceAddress ownAddress = localAddress;
Iterator<SignalServiceAddress> recipientIterator = recipients.iterator(); Iterator<SignalServiceAddress> recipientIterator = recipients.iterator();
Iterator<Optional<UnidentifiedAccess>> unidentifiedAccessIterator = unidentifiedAccess.iterator(); Iterator<Optional<UnidentifiedAccess>> unidentifiedAccessIterator = unidentifiedAccess.iterator();
@ -995,7 +886,7 @@ public class SignalServiceMessageSender {
try { try {
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store); boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store);
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, false, useFallbackEncryption, isClosedGroup, false, notifyPNServer); SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, false, useFallbackEncryption, isClosedGroup, notifyPNServer, Optional.absent());
results.add(result); results.add(result);
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
Log.w(TAG, e); Log.w(TAG, e);
@ -1009,41 +900,46 @@ public class SignalServiceMessageSender {
return results; return results;
} }
private SendMessageResult sendMessage(SignalServiceAddress recipient, private SendMessageResult sendMessage(SignalServiceAddress recipient,
Optional<UnidentifiedAccess> unidentifiedAccess, Optional<UnidentifiedAccess> unidentifiedAccess,
long timestamp, long timestamp,
byte[] content, byte[] content,
boolean online, boolean online,
int ttl, int ttl,
boolean useFallbackEncryption, boolean useFallbackEncryption)
boolean isSyncMessage)
throws IOException throws IOException
{ {
// Loki - This method is only invoked for various types of control messages // Loki - This method is only invoked for various types of control messages
return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, false, useFallbackEncryption, isSyncMessage, false); return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, false, useFallbackEncryption, false,Optional.absent());
} }
public SendMessageResult sendMessage(final long messageID, public SendMessageResult sendMessage(final long messageID,
final SignalServiceAddress recipient, final SignalServiceAddress recipient,
Optional<UnidentifiedAccess> unidentifiedAccess, Optional<UnidentifiedAccess> unidentifiedAccess,
long timestamp, long timestamp,
byte[] content, byte[] content,
boolean online, boolean online,
int ttl, int ttl,
boolean isDeviceLinkMessage, boolean isDeviceLinkMessage,
boolean useFallbackEncryption, boolean useFallbackEncryption,
boolean isClosedGroup, boolean isClosedGroup,
boolean isSyncMessage, boolean notifyPNServer,
boolean notifyPNServer) Optional<String> syncTarget)
throws IOException throws IOException
{ {
long threadID = threadDatabase.getThreadID(recipient.getNumber()); boolean isSelfSend = syncTarget.isPresent() && !syncTarget.get().isEmpty();
long threadID;
if (isSelfSend) {
threadID = threadDatabase.getThreadID(syncTarget.get());
} else {
threadID = threadDatabase.getThreadID(recipient.getNumber());
}
PublicChat publicChat = threadDatabase.getPublicChat(threadID); PublicChat publicChat = threadDatabase.getPublicChat(threadID);
try { try {
if (publicChat != null) { if (publicChat != null) {
return sendMessageToPublicChat(messageID, recipient, timestamp, content, publicChat); return sendMessageToPublicChat(messageID, recipient, timestamp, content, publicChat);
} else { } else {
return sendMessageToPrivateChat(messageID, recipient, unidentifiedAccess, timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer); return sendMessageToPrivateChat(messageID, recipient, unidentifiedAccess, timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer, syncTarget);
} }
} catch (PushNetworkException e) { } catch (PushNetworkException e) {
return SendMessageResult.networkFailure(recipient); return SendMessageResult.networkFailure(recipient);
@ -1152,10 +1048,11 @@ public class SignalServiceMessageSender {
int ttl, int ttl,
boolean useFallbackEncryption, boolean useFallbackEncryption,
boolean isClosedGroup, boolean isClosedGroup,
final boolean notifyPNServer) final boolean notifyPNServer,
Optional<String> syncTarget)
throws IOException, UntrustedIdentityException throws IOException, UntrustedIdentityException
{ {
if (recipient.getNumber().equals(userPublicKey)) { return SendMessageResult.success(recipient, false, false); } if (recipient.getNumber().equals(userPublicKey) && !syncTarget.isPresent()) { return SendMessageResult.success(recipient, false, false); }
final SettableFuture<?>[] future = { new SettableFuture<Unit>() }; final SettableFuture<?>[] future = { new SettableFuture<Unit>() };
OutgoingPushMessageList messages = getSessionProtocolEncryptedMessage(recipient, timestamp, content); OutgoingPushMessageList messages = getSessionProtocolEncryptedMessage(recipient, timestamp, content);
// Loki - Remove this when we have shared sender keys // Loki - Remove this when we have shared sender keys
@ -1221,14 +1118,10 @@ public class SignalServiceMessageSender {
} }
return Unit.INSTANCE; return Unit.INSTANCE;
} }
}).fail(new Function1<Exception, Unit>() { }).fail(exception -> {
@SuppressWarnings("unchecked") SettableFuture<Unit> f = (SettableFuture<Unit>)future[0];
@Override f.setException(exception);
public Unit invoke(Exception exception) { return Unit.INSTANCE;
@SuppressWarnings("unchecked") SettableFuture<Unit> f = (SettableFuture<Unit>)future[0];
f.setException(exception);
return Unit.INSTANCE;
}
}); });
@SuppressWarnings("unchecked") SettableFuture<Unit> f = (SettableFuture<Unit>)future[0]; @SuppressWarnings("unchecked") SettableFuture<Unit> f = (SettableFuture<Unit>)future[0];
@ -1304,12 +1197,6 @@ public class SignalServiceMessageSender {
return builder.build(); return builder.build();
} }
private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment)
throws IOException
{
return createAttachmentPointer(attachment, false, null);
}
private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment, SignalServiceAddress recipient) private AttachmentPointer createAttachmentPointer(SignalServiceAttachmentStream attachment, SignalServiceAddress recipient)
throws IOException throws IOException
{ {

View File

@ -389,6 +389,7 @@ public class SignalServiceCipher {
ClosedGroupUpdate closedGroupUpdate = content.getClosedGroupUpdate(); ClosedGroupUpdate closedGroupUpdate = content.getClosedGroupUpdate();
ClosedGroupUpdateV2 closedGroupUpdateV2 = content.getClosedGroupUpdateV2(); ClosedGroupUpdateV2 closedGroupUpdateV2 = content.getClosedGroupUpdateV2();
boolean isDeviceUnlinkingRequest = ((content.getFlags() & DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE) != 0); boolean isDeviceUnlinkingRequest = ((content.getFlags() & DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE) != 0);
String syncTarget = content.getSyncTarget();
for (AttachmentPointer pointer : content.getAttachmentsList()) { for (AttachmentPointer pointer : content.getAttachmentsList()) {
attachments.add(createAttachmentPointer(pointer)); attachments.add(createAttachmentPointer(pointer));
@ -417,7 +418,8 @@ public class SignalServiceCipher {
null, null,
closedGroupUpdate, closedGroupUpdate,
closedGroupUpdateV2, closedGroupUpdateV2,
isDeviceUnlinkingRequest); isDeviceUnlinkingRequest,
syncTarget);
} }
private SignalServiceSyncMessage createSynchronizeMessage(Metadata metadata, SyncMessage content) private SignalServiceSyncMessage createSynchronizeMessage(Metadata metadata, SyncMessage content)

View File

@ -41,6 +41,7 @@ public class SignalServiceDataMessage {
private final Optional<ClosedGroupUpdate> closedGroupUpdate; private final Optional<ClosedGroupUpdate> closedGroupUpdate;
private final Optional<ClosedGroupUpdateV2> closedGroupUpdateV2; private final Optional<ClosedGroupUpdateV2> closedGroupUpdateV2;
private final boolean isDeviceUnlinkingRequest; private final boolean isDeviceUnlinkingRequest;
private final Optional<String> syncTarget;
/** /**
* Construct a SignalServiceDataMessage with a body and no attachments. * Construct a SignalServiceDataMessage with a body and no attachments.
@ -134,7 +135,7 @@ public class SignalServiceDataMessage {
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews, Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
Sticker sticker) Sticker sticker)
{ {
this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, null, false); this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, null, false, null);
} }
/** /**
@ -155,7 +156,7 @@ public class SignalServiceDataMessage {
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews, Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
Sticker sticker, PreKeyBundle preKeyBundle, DeviceLink deviceLink, Sticker sticker, PreKeyBundle preKeyBundle, DeviceLink deviceLink,
ClosedGroupUpdate closedGroupUpdate, ClosedGroupUpdateV2 closedGroupUpdateV2, ClosedGroupUpdate closedGroupUpdate, ClosedGroupUpdateV2 closedGroupUpdateV2,
boolean isDeviceUnlinkingRequest) boolean isDeviceUnlinkingRequest, String syncTarget)
{ {
this.timestamp = timestamp; this.timestamp = timestamp;
this.body = Optional.fromNullable(body); this.body = Optional.fromNullable(body);
@ -172,6 +173,7 @@ public class SignalServiceDataMessage {
this.closedGroupUpdate = Optional.fromNullable(closedGroupUpdate); this.closedGroupUpdate = Optional.fromNullable(closedGroupUpdate);
this.closedGroupUpdateV2 = Optional.fromNullable(closedGroupUpdateV2); this.closedGroupUpdateV2 = Optional.fromNullable(closedGroupUpdateV2);
this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest; this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest;
this.syncTarget = Optional.fromNullable(syncTarget);
if (attachments != null && !attachments.isEmpty()) { if (attachments != null && !attachments.isEmpty()) {
this.attachments = Optional.of(attachments); this.attachments = Optional.of(attachments);
@ -250,6 +252,10 @@ public class SignalServiceDataMessage {
return profileKey; return profileKey;
} }
public Optional<String> getSyncTarget() {
return syncTarget;
}
public Optional<Quote> getQuote() { public Optional<Quote> getQuote() {
return quote; return quote;
} }
@ -307,6 +313,7 @@ public class SignalServiceDataMessage {
private Sticker sticker; private Sticker sticker;
private PreKeyBundle preKeyBundle; private PreKeyBundle preKeyBundle;
private DeviceLink deviceLink; private DeviceLink deviceLink;
private String syncTarget;
private boolean isDeviceUnlinkingRequest; private boolean isDeviceUnlinkingRequest;
private Builder() {} private Builder() {}
@ -336,6 +343,11 @@ public class SignalServiceDataMessage {
return this; return this;
} }
public Builder withSyncTarget(String syncTarget) {
this.syncTarget = syncTarget;
return this;
}
public Builder asEndSessionMessage() { public Builder asEndSessionMessage() {
return asEndSessionMessage(true); return asEndSessionMessage(true);
} }
@ -417,7 +429,7 @@ public class SignalServiceDataMessage {
profileKeyUpdate, quote, sharedContacts, previews, profileKeyUpdate, quote, sharedContacts, previews,
sticker, preKeyBundle, deviceLink, sticker, preKeyBundle, deviceLink,
null, null, null, null,
isDeviceUnlinkingRequest); isDeviceUnlinkingRequest, syncTarget);
} }
} }