mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
WIP: clean up signal protocol
This commit is contained in:
parent
0d2f5e0cde
commit
1a907fcf54
@ -27,6 +27,7 @@ import org.session.libsignal.metadata.ProtocolLegacyMessageException;
|
|||||||
import org.session.libsignal.metadata.ProtocolNoSessionException;
|
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.api.crypto.SignalServiceCipher;
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocol;
|
import org.session.libsignal.service.loki.api.crypto.SessionProtocol;
|
||||||
import org.session.libsignal.utilities.PromiseUtilities;
|
import org.session.libsignal.utilities.PromiseUtilities;
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
@ -108,12 +109,8 @@ import org.session.libsignal.service.api.messages.SignalServiceEnvelope;
|
|||||||
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.multidevice.SentTranscriptMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI;
|
|
||||||
import org.session.libsignal.service.loki.crypto.LokiServiceCipher;
|
|
||||||
import org.session.libsignal.service.loki.protocol.mentions.MentionsManager;
|
import org.session.libsignal.service.loki.protocol.mentions.MentionsManager;
|
||||||
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
import org.session.libsignal.service.loki.utilities.PublicKeyValidation;
|
||||||
|
|
||||||
@ -248,7 +245,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context);
|
SessionResetProtocol sessionResetProtocol = new SessionResetImplementation(context);
|
||||||
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
|
||||||
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(context);
|
LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(context);
|
||||||
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, new SessionProtocolImpl(context), sessionResetProtocol, apiDB, UnidentifiedAccessUtil.getCertificateValidator());
|
SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, sessionResetProtocol, new SessionProtocolImpl(context), apiDB);
|
||||||
|
|
||||||
SignalServiceContent content = cipher.decrypt(envelope);
|
SignalServiceContent content = cipher.decrypt(envelope);
|
||||||
|
|
||||||
@ -265,14 +262,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
MultiDeviceProtocol.handleConfigurationMessage(context, content.configurationMessageProto.get(), content.getSender(), content.getTimestamp());
|
MultiDeviceProtocol.handleConfigurationMessage(context, content.configurationMessageProto.get(), content.getSender(), content.getTimestamp());
|
||||||
} else if (content.getDataMessage().isPresent()) {
|
} else if (content.getDataMessage().isPresent()) {
|
||||||
SignalServiceDataMessage message = content.getDataMessage().get();
|
SignalServiceDataMessage message = content.getDataMessage().get();
|
||||||
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
|
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent();
|
||||||
|
|
||||||
if (message.getClosedGroupUpdateV2().isPresent()) {
|
if (message.getClosedGroupControlMessage().isPresent()) {
|
||||||
ClosedGroupsProtocolV2.handleMessage(context, message.getClosedGroupUpdateV2().get(), message.getTimestamp(), envelope.getSource(), content.getSender());
|
ClosedGroupsProtocolV2.handleMessage(context, message.getClosedGroupControlMessage().get(), message.getTimestamp(), envelope.getSource(), content.getSender());
|
||||||
}
|
}
|
||||||
if (message.isEndSession()) {
|
if (message.isGroupUpdate()) {
|
||||||
handleEndSessionMessage(content, smsMessageId);
|
|
||||||
} else if (message.isGroupUpdate()) {
|
|
||||||
handleGroupMessage(content, message, smsMessageId);
|
handleGroupMessage(content, message, smsMessageId);
|
||||||
} else if (message.isExpirationUpdate()) {
|
} else if (message.isExpirationUpdate()) {
|
||||||
handleExpirationUpdate(content, message, smsMessageId);
|
handleExpirationUpdate(content, message, smsMessageId);
|
||||||
@ -293,8 +288,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) {
|
if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.fromSerialized(content.getSender()))) {
|
||||||
handleNeedsDeliveryReceipt(content, message);
|
handleNeedsDeliveryReceipt(content, message);
|
||||||
}
|
}
|
||||||
} else if (content.getSyncMessage().isPresent()) {
|
|
||||||
throw new UnsupportedOperationException("Device link operations are not supported!");
|
|
||||||
} else if (content.getReceiptMessage().isPresent()) {
|
} else if (content.getReceiptMessage().isPresent()) {
|
||||||
SignalServiceReceiptMessage message = content.getReceiptMessage().get();
|
SignalServiceReceiptMessage message = content.getReceiptMessage().get();
|
||||||
|
|
||||||
@ -311,37 +304,16 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
// if (envelope.isPreKeySignalMessage()) {
|
// if (envelope.isPreKeySignalMessage()) {
|
||||||
// ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
// ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob());
|
||||||
// }
|
// }
|
||||||
} catch (ProtocolInvalidVersionException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
|
||||||
} catch (ProtocolInvalidMessageException e) {
|
} catch (ProtocolInvalidMessageException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
if (!isPushNotification) { // This can be triggered if a PN encrypted with an old session comes in after the user performed a session reset
|
if (!isPushNotification) { // This can be triggered if a PN encrypted with an old session comes in after the user performed a session reset
|
||||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e);
|
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e);
|
||||||
}
|
}
|
||||||
} catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
|
}catch (StorageFailedException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e);
|
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e);
|
||||||
} catch (StorageFailedException e) {
|
} catch (InvalidMetadataMessageException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId, e);
|
|
||||||
} catch (ProtocolNoSessionException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
handleNoSessionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
|
||||||
} catch (ProtocolLegacyMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
handleLegacyMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
|
||||||
} catch (ProtocolDuplicateMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
handleDuplicateMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
|
|
||||||
} catch (InvalidMetadataVersionException | InvalidMetadataMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
} catch (SelfSendException e) {
|
|
||||||
Log.i(TAG, "Dropping UD message from self.");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.i(TAG, "IOException during message decryption.");
|
|
||||||
} catch (SessionProtocol.Exception e) {
|
|
||||||
Log.i(TAG, "Couldn't handle message due to error: " + e.getDescription());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +389,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
Optional.absent(),
|
Optional.absent(),
|
||||||
Optional.absent(),
|
Optional.absent(),
|
||||||
Optional.absent(),
|
Optional.absent(),
|
||||||
Optional.absent(),
|
|
||||||
Optional.absent());
|
Optional.absent());
|
||||||
|
|
||||||
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
database.insertSecureDecryptedMessageInbox(mediaMessage, -1);
|
||||||
@ -448,7 +419,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
|
Optional<QuoteModel> quote = getValidatedQuote(message.getQuote());
|
||||||
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
Optional<List<Contact>> sharedContacts = getContacts(message.getSharedContacts());
|
||||||
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
|
Optional<List<LinkPreview>> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
|
||||||
Optional<Attachment> sticker = getStickerAttachment(message.getSticker());
|
|
||||||
|
|
||||||
Address masterAddress = masterRecipient.getAddress();
|
Address masterAddress = masterRecipient.getAddress();
|
||||||
|
|
||||||
@ -523,7 +493,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
} else {
|
} else {
|
||||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1,
|
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1,
|
||||||
message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
|
message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
|
||||||
quote, sharedContacts, linkPreviews, sticker);
|
quote, sharedContacts, linkPreviews);
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
database.beginTransaction();
|
database.beginTransaction();
|
||||||
|
|
||||||
@ -594,110 +564,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException {
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
Recipient recipient = getSyncMessageMasterDestination(message);
|
|
||||||
|
|
||||||
OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipient,
|
|
||||||
message.getTimestamp(),
|
|
||||||
message.getMessage().getExpiresInSeconds() * 1000L);
|
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
|
||||||
long messageId = database.insertMessageOutbox(expirationUpdateMessage, threadId, false, null);
|
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
|
||||||
|
|
||||||
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, message.getMessage().getExpiresInSeconds());
|
|
||||||
|
|
||||||
return threadId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message)
|
|
||||||
throws MmsException
|
|
||||||
{
|
|
||||||
if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; }
|
|
||||||
|
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
Recipient recipients = getSyncMessageMasterDestination(message);
|
|
||||||
Optional<QuoteModel> quote = getValidatedQuote(message.getMessage().getQuote());
|
|
||||||
Optional<Attachment> sticker = getStickerAttachment(message.getMessage().getSticker());
|
|
||||||
Optional<List<Contact>> sharedContacts = getContacts(message.getMessage().getSharedContacts());
|
|
||||||
Optional<List<LinkPreview>> previews = getLinkPreviews(message.getMessage().getPreviews(), message.getMessage().getBody().or(""));
|
|
||||||
List<Attachment> syncAttachments = PointerAttachment.forPointers(message.getMessage().getAttachments());
|
|
||||||
|
|
||||||
if (sticker.isPresent()) {
|
|
||||||
syncAttachments.add(sticker.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
OutgoingMediaMessage mediaMessage = new OutgoingMediaMessage(recipients, message.getMessage().getBody().orNull(),
|
|
||||||
syncAttachments,
|
|
||||||
message.getTimestamp(), -1,
|
|
||||||
message.getMessage().getExpiresInSeconds() * 1000,
|
|
||||||
ThreadDatabase.DistributionTypes.DEFAULT, quote.orNull(),
|
|
||||||
sharedContacts.or(Collections.emptyList()),
|
|
||||||
previews.or(Collections.emptyList()),
|
|
||||||
Collections.emptyList(), Collections.emptyList());
|
|
||||||
|
|
||||||
mediaMessage = new OutgoingSecureMediaMessage(mediaMessage);
|
|
||||||
|
|
||||||
if (recipients.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
|
|
||||||
handleSynchronizeSentExpirationUpdate(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipients);
|
|
||||||
|
|
||||||
database.beginTransaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
long messageId = database.insertMessageOutbox(mediaMessage, threadId, false, null);
|
|
||||||
if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); }
|
|
||||||
|
|
||||||
if (recipients.getAddress().isGroup()) {
|
|
||||||
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
|
||||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipients.getAddress().toGroupString(), false);
|
|
||||||
|
|
||||||
for (Recipient member : members) {
|
|
||||||
receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
|
||||||
database.markUnidentified(messageId, message.isUnidentified(recipients.getAddress().serialize()));
|
|
||||||
|
|
||||||
List<DatabaseAttachment> allAttachments = DatabaseFactory.getAttachmentDatabase(context).getAttachmentsForMessage(messageId);
|
|
||||||
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(messageId, attachment.getAttachmentId(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.getMessage().getExpiresInSeconds() > 0) {
|
|
||||||
database.markExpireStarted(messageId, message.getExpirationStartTimestamp());
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getExpiringMessageManager()
|
|
||||||
.scheduleDeletion(messageId, true,
|
|
||||||
message.getExpirationStartTimestamp(),
|
|
||||||
message.getMessage().getExpiresInSeconds() * 1000L);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recipients.isLocalNumber()) {
|
|
||||||
SyncMessageId id = new SyncMessageId(recipients.getAddress(), message.getTimestamp());
|
|
||||||
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
|
|
||||||
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
database.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
database.endTransaction();
|
|
||||||
}
|
|
||||||
return threadId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleTextMessage(@NonNull SignalServiceContent content,
|
public void handleTextMessage(@NonNull SignalServiceContent content,
|
||||||
@NonNull SignalServiceDataMessage message,
|
@NonNull SignalServiceDataMessage message,
|
||||||
@NonNull Optional<Long> smsMessageId,
|
@NonNull Optional<Long> smsMessageId,
|
||||||
@ -824,86 +690,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message)
|
|
||||||
throws MmsException
|
|
||||||
{
|
|
||||||
if (SessionMetaProtocol.shouldIgnoreMessage(message.getTimestamp())) { return -1; }
|
|
||||||
|
|
||||||
Recipient recipient = getSyncMessageMasterDestination(message);
|
|
||||||
String body = message.getMessage().getBody().or("");
|
|
||||||
long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000L;
|
|
||||||
|
|
||||||
// Ignore the message if it has no body
|
|
||||||
if (body.isEmpty()) { return -1; }
|
|
||||||
|
|
||||||
if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
|
|
||||||
handleSynchronizeSentExpirationUpdate(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient);
|
|
||||||
boolean isGroup = recipient.getAddress().isGroup();
|
|
||||||
|
|
||||||
MessagingDatabase database;
|
|
||||||
long messageId;
|
|
||||||
|
|
||||||
if (isGroup) {
|
|
||||||
OutgoingMediaMessage outgoingMediaMessage = new OutgoingMediaMessage(recipient, new SlideDeck(), body, message.getMessage().getTimestamp(), -1, expiresInMillis, ThreadDatabase.DistributionTypes.DEFAULT, null, Collections.emptyList(), Collections.emptyList());
|
|
||||||
outgoingMediaMessage = new OutgoingSecureMediaMessage(outgoingMediaMessage);
|
|
||||||
|
|
||||||
messageId = DatabaseFactory.getMmsDatabase(context).insertMessageOutbox(outgoingMediaMessage, threadId, false, null,message.getTimestamp());
|
|
||||||
if (message.messageServerID >= 0) { DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageId, message.messageServerID); }
|
|
||||||
|
|
||||||
database = DatabaseFactory.getMmsDatabase(context);
|
|
||||||
|
|
||||||
GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context);
|
|
||||||
List<Recipient> members = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false);
|
|
||||||
|
|
||||||
for (Recipient member : members) {
|
|
||||||
receiptDatabase.setUnidentified(member.getAddress(), messageId, message.isUnidentified(member.getAddress().serialize()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
OutgoingTextMessage outgoingTextMessage = new OutgoingEncryptedMessage(recipient, body, expiresInMillis);
|
|
||||||
|
|
||||||
messageId = DatabaseFactory.getSmsDatabase(context).insertMessageOutbox(threadId, outgoingTextMessage, false, message.getTimestamp(), null);
|
|
||||||
database = DatabaseFactory.getSmsDatabase(context);
|
|
||||||
database.markUnidentified(messageId, message.isUnidentified(recipient.getAddress().serialize()));
|
|
||||||
}
|
|
||||||
|
|
||||||
database.markAsSent(messageId, true);
|
|
||||||
|
|
||||||
if (expiresInMillis > 0) {
|
|
||||||
database.markExpireStarted(messageId, message.getExpirationStartTimestamp());
|
|
||||||
ApplicationContext.getInstance(context)
|
|
||||||
.getExpiringMessageManager()
|
|
||||||
.scheduleDeletion(messageId, isGroup, message.getExpirationStartTimestamp(), expiresInMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recipient.isLocalNumber()) {
|
|
||||||
SyncMessageId id = new SyncMessageId(recipient.getAddress(), message.getTimestamp());
|
|
||||||
DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis());
|
|
||||||
DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
return threadId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleInvalidVersionMessage(@NonNull String sender, int senderDevice, long timestamp,
|
|
||||||
@NonNull Optional<Long> smsMessageId)
|
|
||||||
{
|
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
|
||||||
|
|
||||||
if (!smsMessageId.isPresent()) {
|
|
||||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
|
||||||
smsDatabase.markAsInvalidVersionKeyExchange(insertResult.get().getMessageId());
|
|
||||||
messageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
smsDatabase.markAsInvalidVersionKeyExchange(smsMessageId.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleCorruptMessage(@NonNull String sender, int senderDevice, long timestamp,
|
private void handleCorruptMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||||
@NonNull Optional<Long> smsMessageId, @NonNull Throwable e)
|
@NonNull Optional<Long> smsMessageId, @NonNull Throwable e)
|
||||||
{
|
{
|
||||||
@ -1059,17 +845,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
|
|
||||||
long threadId;
|
long threadId;
|
||||||
|
|
||||||
if (typingMessage.getGroupId().isPresent()) {
|
author = getMessageMasterDestination(content.getSender());
|
||||||
// Typing messages should only apply to closed groups, thus we use `getEncodedId`
|
threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(author);
|
||||||
Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedClosedGroupID(typingMessage.getGroupId().get()));
|
|
||||||
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
|
|
||||||
|
|
||||||
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
|
|
||||||
} else {
|
|
||||||
// See if we need to redirect the message
|
|
||||||
author = getMessageMasterDestination(content.getSender());
|
|
||||||
threadId = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(author);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (threadId <= 0) {
|
if (threadId <= 0) {
|
||||||
Log.w(TAG, "Couldn't find a matching thread for a typing message.");
|
Log.w(TAG, "Couldn't find a matching thread for a typing message.");
|
||||||
@ -1211,28 +988,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
return database.insertMessageInbox(textMessage);
|
return database.insertMessageInbox(textMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recipient getSyncMessageDestination(SentTranscriptMessage message) {
|
|
||||||
if (message.getMessage().isGroupMessage()) {
|
|
||||||
return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false);
|
|
||||||
} else {
|
|
||||||
return Recipient.from(context, Address.fromSerialized(message.getDestination().get()), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Recipient getSyncMessageMasterDestination(SentTranscriptMessage message) {
|
|
||||||
if (message.getMessage().isGroupMessage()) {
|
|
||||||
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.fromSerialized(userPublicKey), false);
|
|
||||||
} else {
|
|
||||||
return Recipient.from(context, Address.fromSerialized(publicKey), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
|
private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
|
||||||
if (message.getGroupInfo().isPresent()) {
|
if (message.getGroupInfo().isPresent()) {
|
||||||
return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedClosedGroupID(message.getGroupInfo().get().getGroupId())), false);
|
return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedClosedGroupID(message.getGroupInfo().get().getGroupId())), false);
|
||||||
@ -1309,8 +1064,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
} else {
|
} else {
|
||||||
return sender.isBlocked();
|
return sender.isBlocked();
|
||||||
}
|
}
|
||||||
} else if (content.getSyncMessage().isPresent()) {
|
|
||||||
throw new UnsupportedOperationException("Device link operations are not supported!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -21,7 +21,6 @@ import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer
|
|||||||
import org.session.libsignal.service.api.messages.SignalServiceContent
|
import org.session.libsignal.service.api.messages.SignalServiceContent
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
|
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.multidevice.SentTranscriptMessage
|
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
import org.session.libsignal.service.api.push.SignalServiceAddress
|
||||||
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
import org.session.libsignal.service.loki.api.opengroups.PublicChat
|
||||||
@ -222,16 +221,6 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
|
|||||||
FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB)
|
FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB)
|
||||||
// Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below
|
// Kovenant propagates a context to chained promises, so LokiPublicChatAPI.sharedContext should be used for all of the below
|
||||||
val promise = api.getMessages(group.channel, group.server).bind(PublicChatAPI.sharedContext) { messages ->
|
val promise = api.getMessages(group.channel, group.server).bind(PublicChatAPI.sharedContext) { messages ->
|
||||||
/*
|
|
||||||
if (messages.isNotEmpty()) {
|
|
||||||
// We need to fetch the device mapping for any devices we don't have
|
|
||||||
uniqueDevices = messages.map { it.senderPublicKey }.toSet()
|
|
||||||
val devicesToUpdate = uniqueDevices.filter { !userDevices.contains(it) && FileServerAPI.shared.hasDeviceLinkCacheExpired(publicKey = it) }
|
|
||||||
if (devicesToUpdate.isNotEmpty()) {
|
|
||||||
return@bind FileServerAPI.shared.getDeviceLinks(devicesToUpdate.toSet()).then { messages }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
Promise.of(messages)
|
Promise.of(messages)
|
||||||
}
|
}
|
||||||
promise.successBackground { messages ->
|
promise.successBackground { messages ->
|
||||||
|
@ -13,6 +13,7 @@ import org.session.libsignal.libsignal.ecc.ECKeyPair
|
|||||||
import org.session.libsignal.libsignal.util.guava.Optional
|
import org.session.libsignal.libsignal.util.guava.Optional
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
import org.session.libsignal.service.api.messages.SignalServiceGroup
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
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
|
||||||
import org.session.libsignal.utilities.ThreadUtils
|
import org.session.libsignal.utilities.ThreadUtils
|
||||||
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
|
||||||
@ -402,48 +403,48 @@ object ClosedGroupsProtocolV2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun handleMessage(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
fun handleMessage(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
||||||
if (!isValid(context, closedGroupUpdate, senderPublicKey, sentTimestamp)) { return }
|
if (!isValid(context, closedGroupUpdate, senderPublicKey, sentTimestamp)) { return }
|
||||||
when (closedGroupUpdate.type) {
|
when (closedGroupUpdate.type) {
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate, senderPublicKey, sentTimestamp)
|
DataMessage.ClosedGroupControlMessage.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate, senderPublicKey, sentTimestamp)
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_REMOVED -> handleClosedGroupMembersRemoved(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_REMOVED -> handleClosedGroupMembersRemoved(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_ADDED -> handleClosedGroupMembersAdded(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED -> handleClosedGroupMembersAdded(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.NAME_CHANGE -> handleClosedGroupNameChange(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> handleClosedGroupNameChange(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBER_LEFT -> handleClosedGroupMemberLeft(context, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> handleClosedGroupMemberLeft(context, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.UPDATE -> handleClosedGroupUpdate(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.UPDATE -> handleClosedGroupUpdate(context, closedGroupUpdate, sentTimestamp, groupPublicKey, senderPublicKey)
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR -> handleGroupEncryptionKeyPair(context, closedGroupUpdate, groupPublicKey, senderPublicKey)
|
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> handleGroupEncryptionKeyPair(context, closedGroupUpdate, groupPublicKey, senderPublicKey)
|
||||||
else -> {
|
else -> {
|
||||||
Log.d("Loki","Can't handle closed group update of unknown type: ${closedGroupUpdate.type}")
|
Log.d("Loki","Can't handle closed group update of unknown type: ${closedGroupUpdate.type}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isValid(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, senderPublicKey: String, sentTimestamp: Long): Boolean {
|
private fun isValid(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, senderPublicKey: String, sentTimestamp: Long): Boolean {
|
||||||
val record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(sentTimestamp, senderPublicKey)
|
val record = DatabaseFactory.getMmsSmsDatabase(context).getMessageFor(sentTimestamp, senderPublicKey)
|
||||||
if (record != null) return false
|
if (record != null) return false
|
||||||
|
|
||||||
return when (closedGroupUpdate.type) {
|
return when (closedGroupUpdate.type) {
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.NEW -> {
|
DataMessage.ClosedGroupControlMessage.Type.NEW -> {
|
||||||
(!closedGroupUpdate.publicKey.isEmpty && !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.encryptionKeyPair.privateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty
|
(!closedGroupUpdate.publicKey.isEmpty && !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.encryptionKeyPair.privateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty
|
||||||
&& !(closedGroupUpdate.encryptionKeyPair.publicKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0)
|
&& !(closedGroupUpdate.encryptionKeyPair.publicKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0)
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_ADDED,
|
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED,
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_REMOVED -> {
|
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_REMOVED -> {
|
||||||
closedGroupUpdate.membersCount > 0
|
closedGroupUpdate.membersCount > 0
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBER_LEFT -> {
|
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
|
||||||
senderPublicKey.isNotEmpty()
|
senderPublicKey.isNotEmpty()
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.UPDATE,
|
DataMessage.ClosedGroupControlMessage.Type.UPDATE,
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.NAME_CHANGE -> {
|
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> {
|
||||||
!closedGroupUpdate.name.isNullOrEmpty()
|
!closedGroupUpdate.name.isNullOrEmpty()
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR -> true
|
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, senderPublicKey: String, sentTimestamp: Long) {
|
public fun handleNewClosedGroup(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, senderPublicKey: String, sentTimestamp: Long) {
|
||||||
// Prepare
|
// Prepare
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||||
@ -483,7 +484,7 @@ object ClosedGroupsProtocolV2 {
|
|||||||
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)
|
LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleClosedGroupMembersRemoved(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
fun handleClosedGroupMembersRemoved(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
||||||
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey)
|
||||||
@ -536,7 +537,7 @@ object ClosedGroupsProtocolV2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleClosedGroupMembersAdded(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
fun handleClosedGroupMembersAdded(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
||||||
@ -578,7 +579,7 @@ object ClosedGroupsProtocolV2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleClosedGroupNameChange(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
fun handleClosedGroupNameChange(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
||||||
// Check that the sender is a member of the group (before the update)
|
// Check that the sender is a member of the group (before the update)
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
val groupDB = DatabaseFactory.getGroupDatabase(context)
|
||||||
@ -646,7 +647,7 @@ object ClosedGroupsProtocolV2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
private fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
|
||||||
// Prepare
|
// Prepare
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)!!
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||||
@ -728,7 +729,7 @@ object ClosedGroupsProtocolV2 {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleGroupEncryptionKeyPair(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, groupPublicKey: String, senderPublicKey: String) {
|
private fun handleGroupEncryptionKeyPair(context: Context, closedGroupUpdate: DataMessage.ClosedGroupControlMessage, groupPublicKey: String, senderPublicKey: String) {
|
||||||
// Prepare
|
// Prepare
|
||||||
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
||||||
|
@ -70,8 +70,7 @@ public class IncomingMediaMessage {
|
|||||||
Optional<List<SignalServiceAttachment>> attachments,
|
Optional<List<SignalServiceAttachment>> attachments,
|
||||||
Optional<QuoteModel> quote,
|
Optional<QuoteModel> quote,
|
||||||
Optional<List<Contact>> sharedContacts,
|
Optional<List<Contact>> sharedContacts,
|
||||||
Optional<List<LinkPreview>> linkPreviews,
|
Optional<List<LinkPreview>> linkPreviews)
|
||||||
Optional<Attachment> sticker)
|
|
||||||
{
|
{
|
||||||
this.push = true;
|
this.push = true;
|
||||||
this.from = from;
|
this.from = from;
|
||||||
@ -89,10 +88,6 @@ public class IncomingMediaMessage {
|
|||||||
this.attachments.addAll(PointerAttachment.forPointers(attachments));
|
this.attachments.addAll(PointerAttachment.forPointers(attachments));
|
||||||
this.sharedContacts.addAll(sharedContacts.or(Collections.emptyList()));
|
this.sharedContacts.addAll(sharedContacts.or(Collections.emptyList()));
|
||||||
this.linkPreviews.addAll(linkPreviews.or(Collections.emptyList()));
|
this.linkPreviews.addAll(linkPreviews.or(Collections.emptyList()));
|
||||||
|
|
||||||
if (sticker.isPresent()) {
|
|
||||||
this.attachments.add(sticker.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IncomingMediaMessage from(VisibleMessage message,
|
public static IncomingMediaMessage from(VisibleMessage message,
|
||||||
@ -104,7 +99,7 @@ public class IncomingMediaMessage {
|
|||||||
Optional<List<LinkPreview>> linkPreviews)
|
Optional<List<LinkPreview>> linkPreviews)
|
||||||
{
|
{
|
||||||
return new IncomingMediaMessage(from, message.getReceivedTimestamp(), -1, expiresIn, false,
|
return new IncomingMediaMessage(from, message.getReceivedTimestamp(), -1, expiresIn, false,
|
||||||
false, Optional.fromNullable(message.getText()), group, attachments, quote, Optional.absent(), linkPreviews, Optional.absent());
|
false, Optional.fromNullable(message.getText()), group, attachments, quote, Optional.absent(), linkPreviews);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSubscriptionId() {
|
public int getSubscriptionId() {
|
||||||
|
@ -6,6 +6,7 @@ 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.utilities.logging.Log
|
import org.session.libsignal.utilities.logging.Log
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
||||||
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage
|
||||||
import org.session.libsignal.service.loki.utilities.toHexString
|
import org.session.libsignal.service.loki.utilities.toHexString
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
|
|
||||||
@ -55,10 +56,10 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
const val TAG = "ClosedGroupControlMessage"
|
const val TAG = "ClosedGroupControlMessage"
|
||||||
|
|
||||||
fun fromProto(proto: SignalServiceProtos.Content): ClosedGroupControlMessage? {
|
fun fromProto(proto: SignalServiceProtos.Content): ClosedGroupControlMessage? {
|
||||||
val closedGroupControlMessageProto = proto.dataMessage?.closedGroupUpdateV2 ?: return null
|
val closedGroupControlMessageProto = proto.dataMessage?.closedGroupControlMessage ?: return null
|
||||||
val kind: Kind
|
val kind: Kind
|
||||||
when(closedGroupControlMessageProto.type) {
|
when(closedGroupControlMessageProto.type) {
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.NEW -> {
|
DataMessage.ClosedGroupControlMessage.Type.NEW -> {
|
||||||
val publicKey = closedGroupControlMessageProto.publicKey ?: return null
|
val publicKey = closedGroupControlMessageProto.publicKey ?: return null
|
||||||
val name = closedGroupControlMessageProto.name ?: return null
|
val name = closedGroupControlMessageProto.name ?: return null
|
||||||
val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null
|
val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null
|
||||||
@ -71,29 +72,31 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.UPDATE -> {
|
DataMessage.ClosedGroupControlMessage.Type.UPDATE -> {
|
||||||
val name = closedGroupControlMessageProto.name ?: return null
|
val name = closedGroupControlMessageProto.name ?: return null
|
||||||
kind = Kind.Update(name, closedGroupControlMessageProto.membersList)
|
kind = Kind.Update(name, closedGroupControlMessageProto.membersList)
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR -> {
|
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR -> {
|
||||||
val publicKey = closedGroupControlMessageProto.publicKey
|
val publicKey = closedGroupControlMessageProto.publicKey
|
||||||
val wrappers = closedGroupControlMessageProto.wrappersList.mapNotNull { KeyPairWrapper.fromProto(it) }
|
val wrappers = closedGroupControlMessageProto.wrappersList.mapNotNull { KeyPairWrapper.fromProto(it) }
|
||||||
kind = Kind.EncryptionKeyPair(publicKey, wrappers)
|
kind = Kind.EncryptionKeyPair(publicKey, wrappers)
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.NAME_CHANGE -> {
|
DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE -> {
|
||||||
val name = closedGroupControlMessageProto.name ?: return null
|
val name = closedGroupControlMessageProto.name ?: return null
|
||||||
kind = Kind.NameChange(name)
|
kind = Kind.NameChange(name)
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_ADDED -> {
|
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED -> {
|
||||||
kind = Kind.MembersAdded(closedGroupControlMessageProto.membersList)
|
kind = Kind.MembersAdded(closedGroupControlMessageProto.membersList)
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_REMOVED -> {
|
DataMessage.ClosedGroupControlMessage.Type.MEMBERS_REMOVED -> {
|
||||||
kind = Kind.MembersRemoved(closedGroupControlMessageProto.membersList)
|
kind = Kind.MembersRemoved(closedGroupControlMessageProto.membersList)
|
||||||
}
|
}
|
||||||
SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBER_LEFT -> {
|
DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT -> {
|
||||||
kind = Kind.MemberLeft
|
kind = Kind.MemberLeft
|
||||||
}
|
}
|
||||||
//TODO: SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR_REQUEST
|
DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR_REQUEST -> {
|
||||||
|
kind = Kind.EncryptionKeyPairRequest
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ClosedGroupControlMessage(kind)
|
return ClosedGroupControlMessage(kind)
|
||||||
}
|
}
|
||||||
@ -130,10 +133,10 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
val closedGroupControlMessage: SignalServiceProtos.ClosedGroupUpdateV2.Builder = SignalServiceProtos.ClosedGroupUpdateV2.newBuilder()
|
val closedGroupControlMessage: DataMessage.ClosedGroupControlMessage.Builder = DataMessage.ClosedGroupControlMessage.newBuilder()
|
||||||
when (kind) {
|
when (kind) {
|
||||||
is Kind.New -> {
|
is Kind.New -> {
|
||||||
closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.NEW
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.NEW
|
||||||
closedGroupControlMessage.publicKey = kind.publicKey
|
closedGroupControlMessage.publicKey = kind.publicKey
|
||||||
closedGroupControlMessage.name = kind.name
|
closedGroupControlMessage.name = kind.name
|
||||||
val encryptionKeyPairAsProto = SignalServiceProtos.KeyPair.newBuilder()
|
val encryptionKeyPairAsProto = SignalServiceProtos.KeyPair.newBuilder()
|
||||||
@ -150,37 +153,37 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
closedGroupControlMessage.addAllAdmins(kind.admins)
|
closedGroupControlMessage.addAllAdmins(kind.admins)
|
||||||
}
|
}
|
||||||
is Kind.Update -> {
|
is Kind.Update -> {
|
||||||
closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.UPDATE
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.UPDATE
|
||||||
closedGroupControlMessage.name = kind.name
|
closedGroupControlMessage.name = kind.name
|
||||||
closedGroupControlMessage.addAllMembers(kind.members)
|
closedGroupControlMessage.addAllMembers(kind.members)
|
||||||
}
|
}
|
||||||
is Kind.EncryptionKeyPair -> {
|
is Kind.EncryptionKeyPair -> {
|
||||||
closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR
|
||||||
closedGroupControlMessage.publicKey = kind.publicKey
|
closedGroupControlMessage.publicKey = kind.publicKey
|
||||||
closedGroupControlMessage.addAllWrappers(kind.wrappers.map { it.toProto() })
|
closedGroupControlMessage.addAllWrappers(kind.wrappers.map { it.toProto() })
|
||||||
}
|
}
|
||||||
is Kind.NameChange -> {
|
is Kind.NameChange -> {
|
||||||
closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.NAME_CHANGE
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.NAME_CHANGE
|
||||||
closedGroupControlMessage.name = kind.name
|
closedGroupControlMessage.name = kind.name
|
||||||
}
|
}
|
||||||
is Kind.MembersAdded -> {
|
is Kind.MembersAdded -> {
|
||||||
closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_ADDED
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.MEMBERS_ADDED
|
||||||
closedGroupControlMessage.addAllMembers(kind.members)
|
closedGroupControlMessage.addAllMembers(kind.members)
|
||||||
}
|
}
|
||||||
is Kind.MembersRemoved -> {
|
is Kind.MembersRemoved -> {
|
||||||
closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBERS_REMOVED
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.MEMBERS_REMOVED
|
||||||
closedGroupControlMessage.addAllMembers(kind.members)
|
closedGroupControlMessage.addAllMembers(kind.members)
|
||||||
}
|
}
|
||||||
is Kind.MemberLeft -> {
|
is Kind.MemberLeft -> {
|
||||||
closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBER_LEFT
|
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.MEMBER_LEFT
|
||||||
}
|
}
|
||||||
is Kind.EncryptionKeyPairRequest -> {
|
is Kind.EncryptionKeyPairRequest -> {
|
||||||
// TODO: closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR_REQUEST
|
// TODO: closedGroupControlMessage.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR_REQUEST
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
val contentProto = SignalServiceProtos.Content.newBuilder()
|
||||||
val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder()
|
val dataMessageProto = DataMessage.newBuilder()
|
||||||
dataMessageProto.closedGroupUpdateV2 = closedGroupControlMessage.build()
|
dataMessageProto.closedGroupControlMessage = closedGroupControlMessage.build()
|
||||||
// Group context
|
// Group context
|
||||||
contentProto.dataMessage = dataMessageProto.build()
|
contentProto.dataMessage = dataMessageProto.build()
|
||||||
return contentProto.build()
|
return contentProto.build()
|
||||||
@ -197,15 +200,15 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromProto(proto: SignalServiceProtos.ClosedGroupUpdateV2.KeyPairWrapper): KeyPairWrapper {
|
fun fromProto(proto: DataMessage.ClosedGroupControlMessage.KeyPairWrapper): KeyPairWrapper {
|
||||||
return KeyPairWrapper(proto.publicKey.toByteArray().toHexString(), proto.encryptedKeyPair)
|
return KeyPairWrapper(proto.publicKey.toByteArray().toHexString(), proto.encryptedKeyPair)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toProto(): SignalServiceProtos.ClosedGroupUpdateV2.KeyPairWrapper? {
|
fun toProto(): DataMessage.ClosedGroupControlMessage.KeyPairWrapper? {
|
||||||
val publicKey = publicKey ?: return null
|
val publicKey = publicKey ?: return null
|
||||||
val encryptedKeyPair = encryptedKeyPair ?: return null
|
val encryptedKeyPair = encryptedKeyPair ?: return null
|
||||||
val result = SignalServiceProtos.ClosedGroupUpdateV2.KeyPairWrapper.newBuilder()
|
val result = DataMessage.ClosedGroupControlMessage.KeyPairWrapper.newBuilder()
|
||||||
result.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(publicKey))
|
result.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(publicKey))
|
||||||
result.encryptedKeyPair = encryptedKeyPair
|
result.encryptedKeyPair = encryptedKeyPair
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package org.session.libsession.messaging.messages.control.unused
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString
|
|
||||||
import org.session.libsession.messaging.messages.control.ControlMessage
|
|
||||||
import org.session.libsignal.utilities.logging.Log
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|
||||||
import java.security.SecureRandom
|
|
||||||
|
|
||||||
class NullMessage() : ControlMessage() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "NullMessage"
|
|
||||||
|
|
||||||
fun fromProto(proto: SignalServiceProtos.Content): NullMessage? {
|
|
||||||
if (proto.nullMessage == null) return null
|
|
||||||
return NullMessage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toProto(): SignalServiceProtos.Content? {
|
|
||||||
val nullMessageProto = SignalServiceProtos.NullMessage.newBuilder()
|
|
||||||
val sr = SecureRandom()
|
|
||||||
val paddingSize = sr.nextInt(512)
|
|
||||||
val padding = ByteArray(paddingSize)
|
|
||||||
nullMessageProto.padding = ByteString.copyFrom(padding)
|
|
||||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
|
||||||
try {
|
|
||||||
contentProto.nullMessage = nullMessageProto.build()
|
|
||||||
return contentProto.build()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Couldn't construct null message proto from: $this")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package org.session.libsession.messaging.messages.control.unused
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString
|
|
||||||
import org.session.libsession.messaging.MessagingConfiguration
|
|
||||||
import org.session.libsession.messaging.messages.control.ControlMessage
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey
|
|
||||||
import org.session.libsignal.libsignal.ecc.DjbECPublicKey
|
|
||||||
import org.session.libsignal.utilities.logging.Log
|
|
||||||
import org.session.libsignal.libsignal.state.PreKeyBundle
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos
|
|
||||||
import java.security.SecureRandom
|
|
||||||
|
|
||||||
class SessionRequest() : ControlMessage() {
|
|
||||||
|
|
||||||
var preKeyBundle: PreKeyBundle? = null
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "SessionRequest"
|
|
||||||
|
|
||||||
fun fromProto(proto: SignalServiceProtos.Content): SessionRequest? {
|
|
||||||
if (proto.nullMessage == null) return null
|
|
||||||
val preKeyBundleProto = proto.preKeyBundleMessage ?: return null
|
|
||||||
var registrationID: Int = 0
|
|
||||||
registrationID = MessagingConfiguration.shared.storage.getOrGenerateRegistrationID() //TODO no implementation for getOrGenerateRegistrationID yet
|
|
||||||
//TODO just confirm if the above code does the equivalent to swift below:
|
|
||||||
/*iOS code: Configuration.shared.storage.with { transaction in
|
|
||||||
registrationID = Configuration.shared.storage.getOrGenerateRegistrationID(using: transaction)
|
|
||||||
}*/
|
|
||||||
val preKeyBundle = PreKeyBundle(
|
|
||||||
registrationID,
|
|
||||||
1,
|
|
||||||
preKeyBundleProto.preKeyId,
|
|
||||||
DjbECPublicKey(preKeyBundleProto.preKey.toByteArray()),
|
|
||||||
preKeyBundleProto.signedKeyId,
|
|
||||||
DjbECPublicKey(preKeyBundleProto.signedKey.toByteArray()),
|
|
||||||
preKeyBundleProto.signature.toByteArray(),
|
|
||||||
IdentityKey(DjbECPublicKey(preKeyBundleProto.identityKey.toByteArray()))
|
|
||||||
)
|
|
||||||
return SessionRequest(preKeyBundle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//constructor
|
|
||||||
internal constructor(preKeyBundle: PreKeyBundle) : this() {
|
|
||||||
this.preKeyBundle = preKeyBundle
|
|
||||||
}
|
|
||||||
|
|
||||||
// validation
|
|
||||||
override fun isValid(): Boolean {
|
|
||||||
if (!super.isValid()) return false
|
|
||||||
return preKeyBundle != null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toProto(): SignalServiceProtos.Content? {
|
|
||||||
val preKeyBundle = preKeyBundle
|
|
||||||
if (preKeyBundle == null) {
|
|
||||||
Log.w(TAG, "Couldn't construct session request proto from: $this")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val nullMessageProto = SignalServiceProtos.NullMessage.newBuilder()
|
|
||||||
val sr = SecureRandom()
|
|
||||||
val paddingSize = sr.nextInt(512)
|
|
||||||
val padding = ByteArray(paddingSize)
|
|
||||||
nullMessageProto.padding = ByteString.copyFrom(padding)
|
|
||||||
val preKeyBundleProto = SignalServiceProtos.PreKeyBundleMessage.newBuilder()
|
|
||||||
preKeyBundleProto.identityKey = ByteString.copyFrom(preKeyBundle.identityKey.publicKey.serialize())
|
|
||||||
preKeyBundleProto.deviceId = preKeyBundle.deviceId
|
|
||||||
preKeyBundleProto.preKeyId = preKeyBundle.preKeyId
|
|
||||||
preKeyBundleProto.preKey = ByteString.copyFrom(preKeyBundle.preKey.serialize())
|
|
||||||
preKeyBundleProto.signedKeyId = preKeyBundle.signedPreKeyId
|
|
||||||
preKeyBundleProto.signedKey = ByteString.copyFrom(preKeyBundle.signedPreKey.serialize())
|
|
||||||
preKeyBundleProto.signature = ByteString.copyFrom(preKeyBundle.signedPreKeySignature)
|
|
||||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
|
||||||
try {
|
|
||||||
contentProto.nullMessage = nullMessageProto.build()
|
|
||||||
contentProto.preKeyBundleMessage = preKeyBundleProto.build()
|
|
||||||
return contentProto.build()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, "Couldn't construct session request proto from: $this")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ class Profile() {
|
|||||||
val profileProto = proto.profile ?: return null
|
val profileProto = proto.profile ?: return null
|
||||||
val displayName = profileProto.displayName ?: return null
|
val displayName = profileProto.displayName ?: return null
|
||||||
val profileKey = proto.profileKey
|
val profileKey = proto.profileKey
|
||||||
val profilePictureURL = profileProto.profilePictureURL
|
val profilePictureURL = profileProto.profilePicture
|
||||||
profileKey?.let {
|
profileKey?.let {
|
||||||
profilePictureURL?.let {
|
profilePictureURL?.let {
|
||||||
return Profile(displayName = displayName, profileKey = profileKey.toByteArray(), profilePictureURL = profilePictureURL)
|
return Profile(displayName = displayName, profileKey = profileKey.toByteArray(), profilePictureURL = profilePictureURL)
|
||||||
@ -41,12 +41,12 @@ class Profile() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder()
|
val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder()
|
||||||
val profileProto = SignalServiceProtos.LokiUserProfile.newBuilder()
|
val profileProto = SignalServiceProtos.DataMessage.LokiProfile.newBuilder()
|
||||||
profileProto.displayName = displayName
|
profileProto.displayName = displayName
|
||||||
val profileKey = profileKey
|
val profileKey = profileKey
|
||||||
profileKey?.let { dataMessageProto.profileKey = ByteString.copyFrom(profileKey) }
|
profileKey?.let { dataMessageProto.profileKey = ByteString.copyFrom(profileKey) }
|
||||||
val profilePictureURL = profilePictureURL
|
val profilePictureURL = profilePictureURL
|
||||||
profilePictureURL?.let { profileProto.profilePictureURL = profilePictureURL }
|
profilePictureURL?.let { profileProto.profilePicture = profilePictureURL }
|
||||||
// Build
|
// Build
|
||||||
try {
|
try {
|
||||||
dataMessageProto.profile = profileProto.build()
|
dataMessageProto.profile = profileProto.build()
|
||||||
|
@ -162,11 +162,11 @@ class OpenGroupPoller(private val openGroup: OpenGroup) {
|
|||||||
}
|
}
|
||||||
val messageServerID = message.serverID
|
val messageServerID = message.serverID
|
||||||
// Profile
|
// Profile
|
||||||
val profileProto = LokiUserProfile.newBuilder()
|
val profileProto = DataMessage.LokiProfile.newBuilder()
|
||||||
profileProto.setDisplayName(message.displayName)
|
profileProto.setDisplayName(message.displayName)
|
||||||
val profilePicture = message.profilePicture
|
val profilePicture = message.profilePicture
|
||||||
if (profilePicture != null) {
|
if (profilePicture != null) {
|
||||||
profileProto.setProfilePictureURL(profilePicture.url)
|
profileProto.setProfilePicture(profilePicture.url)
|
||||||
dataMessageProto.setProfileKey(ByteString.copyFrom(profilePicture.profileKey))
|
dataMessageProto.setProfileKey(ByteString.copyFrom(profilePicture.profileKey))
|
||||||
}
|
}
|
||||||
dataMessageProto.setProfile(profileProto.build())
|
dataMessageProto.setProfile(profileProto.build())
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
syntax = "proto2";
|
|
||||||
|
|
||||||
package textsecure;
|
|
||||||
|
|
||||||
option java_package = "org.session.libsignal.libsignal.fingerprint";
|
|
||||||
option java_outer_classname = "FingerprintProtos";
|
|
||||||
|
|
||||||
message LogicalFingerprint {
|
|
||||||
optional bytes content = 1;
|
|
||||||
// optional bytes identifier = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CombinedFingerprints {
|
|
||||||
optional uint32 version = 1;
|
|
||||||
optional LogicalFingerprint localFingerprint = 2;
|
|
||||||
optional LogicalFingerprint remoteFingerprint = 3;
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
syntax = "proto2";
|
|
||||||
|
|
||||||
package textsecure;
|
|
||||||
|
|
||||||
option java_package = "org.session.libsignal.libsignal.state";
|
|
||||||
option java_outer_classname = "StorageProtos";
|
|
||||||
|
|
||||||
message SessionStructure {
|
|
||||||
message Chain {
|
|
||||||
optional bytes senderRatchetKey = 1;
|
|
||||||
optional bytes senderRatchetKeyPrivate = 2;
|
|
||||||
|
|
||||||
message ChainKey {
|
|
||||||
optional uint32 index = 1;
|
|
||||||
optional bytes key = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional ChainKey chainKey = 3;
|
|
||||||
|
|
||||||
message MessageKey {
|
|
||||||
optional uint32 index = 1;
|
|
||||||
optional bytes cipherKey = 2;
|
|
||||||
optional bytes macKey = 3;
|
|
||||||
optional bytes iv = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
repeated MessageKey messageKeys = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PendingKeyExchange {
|
|
||||||
optional uint32 sequence = 1;
|
|
||||||
optional bytes localBaseKey = 2;
|
|
||||||
optional bytes localBaseKeyPrivate = 3;
|
|
||||||
optional bytes localRatchetKey = 4;
|
|
||||||
optional bytes localRatchetKeyPrivate = 5;
|
|
||||||
optional bytes localIdentityKey = 7;
|
|
||||||
optional bytes localIdentityKeyPrivate = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PendingPreKey {
|
|
||||||
optional uint32 preKeyId = 1;
|
|
||||||
optional int32 signedPreKeyId = 3;
|
|
||||||
optional bytes baseKey = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional uint32 sessionVersion = 1;
|
|
||||||
optional bytes localIdentityPublic = 2;
|
|
||||||
optional bytes remoteIdentityPublic = 3;
|
|
||||||
|
|
||||||
optional bytes rootKey = 4;
|
|
||||||
optional uint32 previousCounter = 5;
|
|
||||||
|
|
||||||
optional Chain senderChain = 6;
|
|
||||||
repeated Chain receiverChains = 7;
|
|
||||||
|
|
||||||
optional PendingKeyExchange pendingKeyExchange = 8;
|
|
||||||
optional PendingPreKey pendingPreKey = 9;
|
|
||||||
|
|
||||||
optional uint32 remoteRegistrationId = 10;
|
|
||||||
optional uint32 localRegistrationId = 11;
|
|
||||||
|
|
||||||
optional bool needsRefresh = 12;
|
|
||||||
optional bytes aliceBaseKey = 13;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RecordStructure {
|
|
||||||
optional SessionStructure currentSession = 1;
|
|
||||||
repeated SessionStructure previousSessions = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PreKeyRecordStructure {
|
|
||||||
optional uint32 id = 1;
|
|
||||||
optional bytes publicKey = 2;
|
|
||||||
optional bytes privateKey = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SignedPreKeyRecordStructure {
|
|
||||||
optional uint32 id = 1;
|
|
||||||
optional bytes publicKey = 2;
|
|
||||||
optional bytes privateKey = 3;
|
|
||||||
optional bytes signature = 4;
|
|
||||||
optional fixed64 timestamp = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message IdentityKeyPairStructure {
|
|
||||||
optional bytes publicKey = 1;
|
|
||||||
optional bytes privateKey = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SenderKeyStateStructure {
|
|
||||||
message SenderChainKey {
|
|
||||||
optional uint32 iteration = 1;
|
|
||||||
optional bytes seed = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SenderMessageKey {
|
|
||||||
optional uint32 iteration = 1;
|
|
||||||
optional bytes seed = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SenderSigningKey {
|
|
||||||
optional bytes public = 1;
|
|
||||||
optional bytes private = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional uint32 senderKeyId = 1;
|
|
||||||
optional SenderChainKey senderChainKey = 2;
|
|
||||||
optional SenderSigningKey senderSigningKey = 3;
|
|
||||||
repeated SenderMessageKey senderMessageKeys = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SenderKeyRecordStructure {
|
|
||||||
repeated SenderKeyStateStructure senderKeyStates = 1;
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
all:
|
all:
|
||||||
protoc25 --java_out=../src/main/java/ SignalService.proto Provisioning.proto WebSocketResources.proto StickerResources.proto
|
protoc25 --java_out=../src/main/java/ SignalService.proto WebSocketResources.proto
|
||||||
protoc25 --java_out=../src/main/java/ UnidentifiedDelivery.proto
|
protoc25 --java_out=../src/main/java/ UnidentifiedDelivery.proto
|
||||||
protoc25 --java_out=../src/main/java/ WhisperTextProtocol.proto LocalStorageProtocol.proto FingerprintProtocol.proto
|
protoc25 --java_out=../src/main/java/ WhisperTextProtocol.proto
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
syntax = "proto2";
|
|
||||||
|
|
||||||
package signalservice;
|
|
||||||
|
|
||||||
option java_package = "org.session.libsignal.service.internal.push";
|
|
||||||
option java_outer_classname = "ProvisioningProtos";
|
|
||||||
|
|
||||||
message ProvisionEnvelope {
|
|
||||||
optional bytes publicKey = 1;
|
|
||||||
optional bytes body = 2; // Encrypted ProvisionMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
message ProvisionMessage {
|
|
||||||
optional bytes identityKeyPublic = 1;
|
|
||||||
optional bytes identityKeyPrivate = 2;
|
|
||||||
optional string number = 3;
|
|
||||||
optional string provisioningCode = 4;
|
|
||||||
optional string userAgent = 5;
|
|
||||||
optional bytes profileKey = 6;
|
|
||||||
optional bool readReceipts = 7;
|
|
||||||
}
|
|
@ -1,9 +1,3 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
syntax = "proto2";
|
syntax = "proto2";
|
||||||
|
|
||||||
package signalservice;
|
package signalservice;
|
||||||
@ -12,88 +6,40 @@ option java_package = "org.session.libsignal.service.internal.push";
|
|||||||
option java_outer_classname = "SignalServiceProtos";
|
option java_outer_classname = "SignalServiceProtos";
|
||||||
|
|
||||||
message Envelope {
|
message Envelope {
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
UNKNOWN = 0;
|
|
||||||
CIPHERTEXT = 1;
|
|
||||||
KEY_EXCHANGE = 2;
|
|
||||||
PREKEY_BUNDLE = 3;
|
|
||||||
RECEIPT = 5;
|
|
||||||
UNIDENTIFIED_SENDER = 6;
|
UNIDENTIFIED_SENDER = 6;
|
||||||
CLOSED_GROUP_CIPHERTEXT = 7; // Loki
|
CLOSED_GROUP_CIPHERTEXT = 7;
|
||||||
FALLBACK_MESSAGE = 101; // Loki - Encrypted using the fallback session cipher. Contains a pre key bundle if it's a session request.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional Type type = 1;
|
// @required
|
||||||
|
required Type type = 1;
|
||||||
optional string source = 2;
|
optional string source = 2;
|
||||||
optional uint32 sourceDevice = 7;
|
optional uint32 sourceDevice = 7;
|
||||||
optional string relay = 3;
|
// @required
|
||||||
optional uint64 timestamp = 5;
|
optional uint64 timestamp = 5;
|
||||||
optional bytes legacyMessage = 6; // Contains an encrypted DataMessage
|
optional bytes content = 8;
|
||||||
optional bytes content = 8; // Contains an encrypted Content
|
|
||||||
optional string serverGuid = 9;
|
|
||||||
optional uint64 serverTimestamp = 10;
|
optional uint64 serverTimestamp = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message TypingMessage {
|
||||||
|
|
||||||
|
enum Action {
|
||||||
|
STARTED = 0;
|
||||||
|
STOPPED = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @required
|
||||||
|
optional uint64 timestamp = 1;
|
||||||
|
// @required
|
||||||
|
optional Action action = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message Content {
|
message Content {
|
||||||
optional DataMessage dataMessage = 1;
|
optional DataMessage dataMessage = 1;
|
||||||
optional SyncMessage syncMessage = 2 [deprecated=true];
|
optional ReceiptMessage receiptMessage = 5;
|
||||||
optional CallMessage callMessage = 3;
|
optional TypingMessage typingMessage = 6;
|
||||||
optional NullMessage nullMessage = 4;
|
optional ConfigurationMessage configurationMessage = 7;
|
||||||
optional ReceiptMessage receiptMessage = 5;
|
|
||||||
optional TypingMessage typingMessage = 6;
|
|
||||||
optional ConfigurationMessage configurationMessage = 7;
|
|
||||||
optional PreKeyBundleMessage preKeyBundleMessage = 101; // Loki
|
|
||||||
optional DeviceLinkMessage deviceLinkMessage = 103; // Loki
|
|
||||||
}
|
|
||||||
|
|
||||||
message DeviceLinkMessage {
|
|
||||||
optional string primaryPublicKey = 1;
|
|
||||||
optional string secondaryPublicKey = 2;
|
|
||||||
optional bytes requestSignature = 3;
|
|
||||||
optional bytes authorizationSignature = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message PreKeyBundleMessage {
|
|
||||||
optional bytes identityKey = 1;
|
|
||||||
optional uint32 deviceId = 2;
|
|
||||||
optional uint32 preKeyId = 3;
|
|
||||||
optional uint32 signedKeyId = 4;
|
|
||||||
optional bytes preKey = 5;
|
|
||||||
optional bytes signedKey = 6;
|
|
||||||
optional bytes signature = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CallMessage {
|
|
||||||
message Offer {
|
|
||||||
optional uint64 id = 1;
|
|
||||||
optional string description = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Answer {
|
|
||||||
optional uint64 id = 1;
|
|
||||||
optional string description = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message IceUpdate {
|
|
||||||
optional uint64 id = 1;
|
|
||||||
optional string sdpMid = 2;
|
|
||||||
optional uint32 sdpMLineIndex = 3;
|
|
||||||
optional string sdp = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Busy {
|
|
||||||
optional uint64 id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Hangup {
|
|
||||||
optional uint64 id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Offer offer = 1;
|
|
||||||
optional Answer answer = 2;
|
|
||||||
repeated IceUpdate iceUpdate = 3;
|
|
||||||
optional Hangup hangup = 4;
|
|
||||||
optional Busy busy = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ClosedGroupCiphertextMessageWrapper {
|
message ClosedGroupCiphertextMessageWrapper {
|
||||||
@ -103,28 +49,43 @@ message ClosedGroupCiphertextMessageWrapper {
|
|||||||
optional bytes ephemeralPublicKey = 2;
|
optional bytes ephemeralPublicKey = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message KeyPair {
|
||||||
|
// @required
|
||||||
|
required bytes publicKey = 1;
|
||||||
|
// @required
|
||||||
|
required bytes privateKey = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message DataMessage {
|
message DataMessage {
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
END_SESSION = 1;
|
EXPIRATION_TIMER_UPDATE = 2;
|
||||||
EXPIRATION_TIMER_UPDATE = 2;
|
|
||||||
PROFILE_KEY_UPDATE = 4;
|
|
||||||
DEVICE_UNLINKING_REQUEST = 128;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Quote {
|
message Quote {
|
||||||
|
|
||||||
message QuotedAttachment {
|
message QuotedAttachment {
|
||||||
|
|
||||||
|
enum Flags {
|
||||||
|
VOICE_MESSAGE = 1;
|
||||||
|
}
|
||||||
|
|
||||||
optional string contentType = 1;
|
optional string contentType = 1;
|
||||||
optional string fileName = 2;
|
optional string fileName = 2;
|
||||||
optional AttachmentPointer thumbnail = 3;
|
optional AttachmentPointer thumbnail = 3;
|
||||||
|
optional uint32 flags = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional uint64 id = 1;
|
// @required
|
||||||
optional string author = 2;
|
optional uint64 id = 1;
|
||||||
optional string text = 3;
|
// @required
|
||||||
repeated QuotedAttachment attachments = 4;
|
optional string author = 2;
|
||||||
|
optional string text = 3;
|
||||||
|
repeated QuotedAttachment attachments = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Contact {
|
message Contact {
|
||||||
|
|
||||||
message Name {
|
message Name {
|
||||||
optional string givenName = 1;
|
optional string givenName = 1;
|
||||||
optional string familyName = 2;
|
optional string familyName = 2;
|
||||||
@ -135,6 +96,7 @@ message DataMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Phone {
|
message Phone {
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
HOME = 1;
|
HOME = 1;
|
||||||
MOBILE = 2;
|
MOBILE = 2;
|
||||||
@ -148,6 +110,7 @@ message DataMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Email {
|
message Email {
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
HOME = 1;
|
HOME = 1;
|
||||||
MOBILE = 2;
|
MOBILE = 2;
|
||||||
@ -161,6 +124,7 @@ message DataMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message PostalAddress {
|
message PostalAddress {
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
HOME = 1;
|
HOME = 1;
|
||||||
WORK = 2;
|
WORK = 2;
|
||||||
@ -192,128 +156,61 @@ message DataMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message Preview {
|
message Preview {
|
||||||
|
// @required
|
||||||
optional string url = 1;
|
optional string url = 1;
|
||||||
optional string title = 2;
|
optional string title = 2;
|
||||||
optional AttachmentPointer image = 3;
|
optional AttachmentPointer image = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Sticker {
|
message LokiProfile {
|
||||||
optional bytes packId = 1;
|
optional string displayName = 1;
|
||||||
optional bytes packKey = 2;
|
optional string profilePicture = 2;
|
||||||
optional uint32 stickerId = 3;
|
|
||||||
optional AttachmentPointer data = 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional string body = 1;
|
message ClosedGroupControlMessage {
|
||||||
repeated AttachmentPointer attachments = 2;
|
|
||||||
optional GroupContext group = 3;
|
|
||||||
optional uint32 flags = 4;
|
|
||||||
optional uint32 expireTimer = 5;
|
|
||||||
optional bytes profileKey = 6;
|
|
||||||
optional uint64 timestamp = 7;
|
|
||||||
optional Quote quote = 8;
|
|
||||||
repeated Contact contact = 9;
|
|
||||||
repeated Preview preview = 10;
|
|
||||||
optional Sticker sticker = 11;
|
|
||||||
optional LokiUserProfile profile = 101; // Loki - The profile of the current user
|
|
||||||
optional ClosedGroupUpdate closedGroupUpdate = 103; // Loki
|
|
||||||
optional ClosedGroupUpdateV2 closedGroupUpdateV2 = 104;
|
|
||||||
optional string syncTarget = 105;
|
|
||||||
}
|
|
||||||
|
|
||||||
message LokiUserProfile {
|
enum Type {
|
||||||
optional string displayName = 1;
|
NEW = 1; // publicKey, name, encryptionKeyPair, members, admins
|
||||||
optional string profilePictureURL = 2;
|
UPDATE = 2; // name, members
|
||||||
}
|
ENCRYPTION_KEY_PAIR = 3; // publicKey, wrappers
|
||||||
|
NAME_CHANGE = 4; // name
|
||||||
message ClosedGroupUpdateV2 {
|
MEMBERS_ADDED = 5; // members
|
||||||
|
MEMBERS_REMOVED = 6; // members
|
||||||
enum Type {
|
MEMBER_LEFT = 7;
|
||||||
NEW = 1; // publicKey, name, encryptionKeyPair, members, admins
|
ENCRYPTION_KEY_PAIR_REQUEST = 8;
|
||||||
UPDATE = 2; // name, members
|
|
||||||
ENCRYPTION_KEY_PAIR = 3; // wrappers
|
|
||||||
NAME_CHANGE = 4; // name
|
|
||||||
MEMBERS_ADDED = 5; // members
|
|
||||||
MEMBERS_REMOVED = 6; // members
|
|
||||||
MEMBER_LEFT = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message KeyPairWrapper {
|
|
||||||
// @required
|
|
||||||
required bytes publicKey = 1; // The public key of the user the key pair is meant for
|
|
||||||
// @required
|
|
||||||
required bytes encryptedKeyPair = 2; // The encrypted key pair
|
|
||||||
}
|
|
||||||
|
|
||||||
// @required
|
|
||||||
required Type type = 1;
|
|
||||||
optional bytes publicKey = 2;
|
|
||||||
optional string name = 3;
|
|
||||||
optional KeyPair encryptionKeyPair = 4;
|
|
||||||
repeated bytes members = 5;
|
|
||||||
repeated bytes admins = 6;
|
|
||||||
repeated KeyPairWrapper wrappers = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message KeyPair {
|
|
||||||
// @required
|
|
||||||
required bytes publicKey = 1;
|
|
||||||
// @required
|
|
||||||
required bytes privateKey = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ClosedGroupUpdate {
|
|
||||||
|
|
||||||
enum Type {
|
|
||||||
NEW = 0; // groupPublicKey, name, groupPrivateKey, senderKeys, members, admins
|
|
||||||
INFO = 1; // groupPublicKey, name, senderKeys, members, admins
|
|
||||||
SENDER_KEY_REQUEST = 2; // groupPublicKey
|
|
||||||
SENDER_KEY = 3; // groupPublicKey, senderKeys
|
|
||||||
}
|
|
||||||
|
|
||||||
message SenderKey {
|
|
||||||
// @required
|
|
||||||
optional bytes chainKey = 1;
|
|
||||||
// @required
|
|
||||||
optional uint32 keyIndex = 2;
|
|
||||||
// @required
|
|
||||||
optional bytes publicKey = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional string name = 1;
|
|
||||||
// @required
|
|
||||||
optional bytes groupPublicKey = 2;
|
|
||||||
optional bytes groupPrivateKey = 3;
|
|
||||||
repeated SenderKey senderKeys = 4;
|
|
||||||
repeated bytes members = 5;
|
|
||||||
repeated bytes admins = 6;
|
|
||||||
// @required
|
|
||||||
optional Type type = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message NullMessage {
|
|
||||||
optional bytes padding = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ReceiptMessage {
|
|
||||||
enum Type {
|
|
||||||
DELIVERY = 0;
|
|
||||||
READ = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Type type = 1;
|
|
||||||
repeated uint64 timestamp = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TypingMessage {
|
|
||||||
enum Action {
|
|
||||||
STARTED = 0;
|
|
||||||
STOPPED = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional uint64 timestamp = 1;
|
message KeyPairWrapper {
|
||||||
optional Action action = 2;
|
// @required
|
||||||
optional bytes groupId = 3;
|
required bytes publicKey = 1; // The public key of the user the key pair is meant for
|
||||||
|
// @required
|
||||||
|
required bytes encryptedKeyPair = 2; // The encrypted key pair
|
||||||
|
}
|
||||||
|
|
||||||
|
// @required
|
||||||
|
required Type type = 1;
|
||||||
|
optional bytes publicKey = 2;
|
||||||
|
optional string name = 3;
|
||||||
|
optional KeyPair encryptionKeyPair = 4;
|
||||||
|
repeated bytes members = 5;
|
||||||
|
repeated bytes admins = 6;
|
||||||
|
repeated KeyPairWrapper wrappers = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional string body = 1;
|
||||||
|
repeated AttachmentPointer attachments = 2;
|
||||||
|
optional GroupContext group = 3;
|
||||||
|
optional uint32 flags = 4;
|
||||||
|
optional uint32 expireTimer = 5;
|
||||||
|
optional bytes profileKey = 6;
|
||||||
|
optional uint64 timestamp = 7;
|
||||||
|
optional Quote quote = 8;
|
||||||
|
repeated Contact contact = 9;
|
||||||
|
repeated Preview preview = 10;
|
||||||
|
optional LokiProfile profile = 101;
|
||||||
|
optional ClosedGroupControlMessage closedGroupControlMessage = 104;
|
||||||
|
optional string syncTarget = 105;
|
||||||
|
optional PublicChatInfo publicChatInfo = 999;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ConfigurationMessage {
|
message ConfigurationMessage {
|
||||||
@ -330,107 +227,25 @@ message ConfigurationMessage {
|
|||||||
repeated string openGroups = 2;
|
repeated string openGroups = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Verified {
|
message ReceiptMessage {
|
||||||
enum State {
|
|
||||||
DEFAULT = 0;
|
enum Type {
|
||||||
VERIFIED = 1;
|
DELIVERY = 0;
|
||||||
UNVERIFIED = 2;
|
READ = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional string destination = 1;
|
// @required
|
||||||
optional bytes identityKey = 2;
|
optional Type type = 1;
|
||||||
optional State state = 3;
|
repeated uint64 timestamp = 2;
|
||||||
optional bytes nullMessage = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SyncMessage {
|
|
||||||
message Sent {
|
|
||||||
message UnidentifiedDeliveryStatus {
|
|
||||||
optional string destination = 1;
|
|
||||||
optional bool unidentified = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional string destination = 1;
|
|
||||||
optional uint64 timestamp = 2;
|
|
||||||
optional DataMessage message = 3;
|
|
||||||
optional uint64 expirationStartTimestamp = 4;
|
|
||||||
repeated UnidentifiedDeliveryStatus unidentifiedStatus = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Contacts {
|
|
||||||
optional AttachmentPointer blob = 1;
|
|
||||||
optional bool complete = 2 [default = false];
|
|
||||||
optional bytes data = 101;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Groups {
|
|
||||||
optional AttachmentPointer blob = 1;
|
|
||||||
optional bytes data = 101;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Blocked {
|
|
||||||
repeated string numbers = 1;
|
|
||||||
repeated bytes groupIds = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Request {
|
|
||||||
enum Type {
|
|
||||||
UNKNOWN = 0;
|
|
||||||
CONTACTS = 1;
|
|
||||||
GROUPS = 2;
|
|
||||||
BLOCKED = 3;
|
|
||||||
CONFIGURATION = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Type type = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Read {
|
|
||||||
optional string sender = 1;
|
|
||||||
optional uint64 timestamp = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Configuration {
|
|
||||||
optional bool readReceipts = 1;
|
|
||||||
optional bool unidentifiedDeliveryIndicators = 2;
|
|
||||||
optional bool typingIndicators = 3;
|
|
||||||
optional bool linkPreviews = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message StickerPackOperation {
|
|
||||||
enum Type {
|
|
||||||
INSTALL = 0;
|
|
||||||
REMOVE = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional bytes packId = 1;
|
|
||||||
optional bytes packKey = 2;
|
|
||||||
optional Type type = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message OpenGroupDetails {
|
|
||||||
optional string url = 1;
|
|
||||||
optional uint32 channelID = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Sent sent = 1;
|
|
||||||
optional Contacts contacts = 2;
|
|
||||||
optional Groups groups = 3;
|
|
||||||
optional Request request = 4;
|
|
||||||
repeated Read read = 5;
|
|
||||||
optional Blocked blocked = 6;
|
|
||||||
optional Verified verified = 7;
|
|
||||||
optional Configuration configuration = 9;
|
|
||||||
optional bytes padding = 8;
|
|
||||||
repeated StickerPackOperation stickerPackOperation = 10;
|
|
||||||
repeated OpenGroupDetails openGroups = 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message AttachmentPointer {
|
message AttachmentPointer {
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
VOICE_MESSAGE = 1;
|
VOICE_MESSAGE = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @required
|
||||||
optional fixed64 id = 1;
|
optional fixed64 id = 1;
|
||||||
optional string contentType = 2;
|
optional string contentType = 2;
|
||||||
optional bytes key = 3;
|
optional bytes key = 3;
|
||||||
@ -446,6 +261,7 @@ message AttachmentPointer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message GroupContext {
|
message GroupContext {
|
||||||
|
|
||||||
enum Type {
|
enum Type {
|
||||||
UNKNOWN = 0;
|
UNKNOWN = 0;
|
||||||
UPDATE = 1;
|
UPDATE = 1;
|
||||||
@ -453,43 +269,43 @@ message GroupContext {
|
|||||||
QUIT = 3;
|
QUIT = 3;
|
||||||
REQUEST_INFO = 4;
|
REQUEST_INFO = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @required
|
||||||
optional bytes id = 1;
|
optional bytes id = 1;
|
||||||
|
// @required
|
||||||
optional Type type = 2;
|
optional Type type = 2;
|
||||||
optional string name = 3;
|
optional string name = 3;
|
||||||
repeated string members = 4;
|
repeated string members = 4;
|
||||||
optional AttachmentPointer avatar = 5;
|
optional AttachmentPointer avatar = 5;
|
||||||
repeated string admins = 6;
|
repeated string admins = 6;
|
||||||
|
|
||||||
// Loki - These fields are only used internally for the Android code base.
|
|
||||||
// This is so that we can differentiate adding/kicking.
|
|
||||||
// DO NOT USE WHEN SENDING MESSAGES.
|
|
||||||
repeated string newMembers = 998;
|
|
||||||
repeated string removedMembers = 999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ContactDetails {
|
message ContactDetails {
|
||||||
|
|
||||||
message Avatar {
|
message Avatar {
|
||||||
optional string contentType = 1;
|
optional string contentType = 1;
|
||||||
optional uint32 length = 2;
|
optional uint32 length = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @required
|
||||||
optional string number = 1;
|
optional string number = 1;
|
||||||
optional string name = 2;
|
optional string name = 2;
|
||||||
optional Avatar avatar = 3;
|
optional Avatar avatar = 3;
|
||||||
optional string color = 4;
|
optional string color = 4;
|
||||||
optional Verified verified = 5;
|
|
||||||
optional bytes profileKey = 6;
|
optional bytes profileKey = 6;
|
||||||
optional bool blocked = 7;
|
optional bool blocked = 7;
|
||||||
optional uint32 expireTimer = 8;
|
optional uint32 expireTimer = 8;
|
||||||
optional string nickname = 101; // Loki
|
optional string nickname = 101;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupDetails {
|
message GroupDetails {
|
||||||
|
|
||||||
message Avatar {
|
message Avatar {
|
||||||
optional string contentType = 1;
|
optional string contentType = 1;
|
||||||
optional uint32 length = 2;
|
optional uint32 length = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @required
|
||||||
optional bytes id = 1;
|
optional bytes id = 1;
|
||||||
optional string name = 2;
|
optional string name = 2;
|
||||||
repeated string members = 3;
|
repeated string members = 3;
|
||||||
@ -500,3 +316,7 @@ message GroupDetails {
|
|||||||
optional bool blocked = 8;
|
optional bool blocked = 8;
|
||||||
repeated string admins = 9;
|
repeated string admins = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message PublicChatInfo { // Intended for internal use only
|
||||||
|
optional uint64 serverID = 1;
|
||||||
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2019 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
syntax = "proto2";
|
|
||||||
|
|
||||||
package signalservice;
|
|
||||||
|
|
||||||
option java_package = "org.session.libsignal.service.internal.sticker";
|
|
||||||
option java_outer_classname = "StickerProtos";
|
|
||||||
|
|
||||||
message Pack {
|
|
||||||
message Sticker {
|
|
||||||
optional uint32 id = 1;
|
|
||||||
optional string emoji = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional string title = 1;
|
|
||||||
optional string author = 2;
|
|
||||||
optional Sticker cover = 3;
|
|
||||||
repeated Sticker stickers = 4;
|
|
||||||
}
|
|
||||||
|
|
@ -24,7 +24,6 @@ import org.session.libsignal.service.api.crypto.InvalidCiphertextException;
|
|||||||
import org.session.libsignal.service.api.crypto.ProfileCipher;
|
import org.session.libsignal.service.api.crypto.ProfileCipher;
|
||||||
import org.session.libsignal.service.api.crypto.ProfileCipherOutputStream;
|
import org.session.libsignal.service.api.crypto.ProfileCipherOutputStream;
|
||||||
import org.session.libsignal.service.api.messages.calls.TurnServerInfo;
|
import org.session.libsignal.service.api.messages.calls.TurnServerInfo;
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceInfo;
|
|
||||||
import org.session.libsignal.service.api.push.ContactTokenDetails;
|
import org.session.libsignal.service.api.push.ContactTokenDetails;
|
||||||
import org.session.libsignal.service.api.push.SignedPreKeyEntity;
|
import org.session.libsignal.service.api.push.SignedPreKeyEntity;
|
||||||
import org.session.libsignal.service.api.util.CredentialsProvider;
|
import org.session.libsignal.service.api.util.CredentialsProvider;
|
||||||
@ -376,10 +375,6 @@ public class SignalServiceAccountManager {
|
|||||||
this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext);
|
this.pushServiceSocket.sendProvisioningMessage(deviceIdentifier, ciphertext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DeviceInfo> getDevices() throws IOException {
|
|
||||||
return this.pushServiceSocket.getDevices();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeDevice(long deviceId) throws IOException {
|
public void removeDevice(long deviceId) throws IOException {
|
||||||
this.pushServiceSocket.removeDevice(deviceId);
|
this.pushServiceSocket.removeDevice(deviceId);
|
||||||
}
|
}
|
||||||
|
@ -225,19 +225,16 @@ public class SignalServiceMessageReceiver {
|
|||||||
if (entity.getSource() != null && entity.getSourceDevice() > 0) {
|
if (entity.getSource() != null && entity.getSourceDevice() > 0) {
|
||||||
envelope = new SignalServiceEnvelope(entity.getType(), entity.getSource(),
|
envelope = new SignalServiceEnvelope(entity.getType(), entity.getSource(),
|
||||||
entity.getSourceDevice(), entity.getTimestamp(),
|
entity.getSourceDevice(), entity.getTimestamp(),
|
||||||
entity.getMessage(), entity.getContent(),
|
entity.getContent(), entity.getServerTimestamp());
|
||||||
entity.getServerTimestamp(), entity.getServerUuid());
|
|
||||||
} else {
|
} else {
|
||||||
envelope = new SignalServiceEnvelope(entity.getType(), entity.getTimestamp(),
|
envelope = new SignalServiceEnvelope(entity.getType(), entity.getTimestamp(),
|
||||||
entity.getMessage(), entity.getContent(),
|
entity.getContent(), entity.getServerTimestamp());
|
||||||
entity.getServerTimestamp(), entity.getServerUuid());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.onMessage(envelope);
|
callback.onMessage(envelope);
|
||||||
results.add(envelope);
|
results.add(envelope);
|
||||||
|
|
||||||
if (envelope.hasUuid()) socket.acknowledgeMessage(envelope.getUuid());
|
socket.acknowledgeMessage(entity.getSource(), entity.getTimestamp());
|
||||||
else socket.acknowledgeMessage(entity.getSource(), entity.getTimestamp());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
@ -26,13 +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.multidevice.BlockedListMessage;
|
|
||||||
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.SentTranscriptMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
import org.session.libsignal.service.api.push.exceptions.PushNetworkException;
|
||||||
@ -50,9 +43,8 @@ import org.session.libsignal.service.internal.push.SignalServiceProtos.Attachmen
|
|||||||
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;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.LokiUserProfile;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.LokiProfile;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage;
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage;
|
||||||
import org.session.libsignal.service.internal.push.http.AttachmentCipherOutputStreamFactory;
|
import org.session.libsignal.service.internal.push.http.AttachmentCipherOutputStreamFactory;
|
||||||
import org.session.libsignal.service.internal.push.http.OutputStreamFactory;
|
import org.session.libsignal.service.internal.push.http.OutputStreamFactory;
|
||||||
@ -238,24 +230,6 @@ public class SignalServiceMessageSender {
|
|||||||
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(), true, 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();
|
|
||||||
// if (wouldSignalSendSyncMessage && SyncMessagesProtocol.shared.shouldSyncMessage(message)) {
|
|
||||||
// byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.of(recipient), timestamp, Collections.singletonList(result));
|
|
||||||
// // Loki - Customize multi device logic
|
|
||||||
// Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
|
|
||||||
// for (String device : linkedDevices) {
|
|
||||||
// SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device);
|
|
||||||
// boolean useFallbackEncryptionForSyncMessage = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store);
|
|
||||||
// sendMessage(deviceAsAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryptionForSyncMessage, true);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Loki - Start a session reset if needed
|
|
||||||
if (message.isEndSession()) {
|
|
||||||
SessionManagementProtocol.shared.setSessionResetStatusToInProgressIfNeeded(recipient, eventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,36 +263,6 @@ public class SignalServiceMessageSender {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendMessage(SignalServiceSyncMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess)
|
|
||||||
throws IOException, UntrustedIdentityException
|
|
||||||
{
|
|
||||||
byte[] content;
|
|
||||||
long timestamp = System.currentTimeMillis();
|
|
||||||
|
|
||||||
if (message.getContacts().isPresent()) {
|
|
||||||
content = createMultiDeviceContactsContent(message.getContacts().get().getContactsStream().asStream(), message.getContacts().get().isComplete());
|
|
||||||
} else if (message.getGroups().isPresent()) {
|
|
||||||
content = createMultiDeviceGroupsContent(message.getGroups().get().asStream());
|
|
||||||
} else if (message.getOpenGroups().isPresent()) {
|
|
||||||
content = createMultiDeviceOpenGroupsContent(message.getOpenGroups().get());
|
|
||||||
} else if (message.getRead().isPresent()) {
|
|
||||||
content = createMultiDeviceReadContent(message.getRead().get());
|
|
||||||
} else if (message.getBlockedList().isPresent()) {
|
|
||||||
content = createMultiDeviceBlockedContent(message.getBlockedList().get());
|
|
||||||
} else if (message.getConfiguration().isPresent()) {
|
|
||||||
content = createMultiDeviceConfigurationContent(message.getConfiguration().get());
|
|
||||||
} else if (message.getSent().isPresent()) {
|
|
||||||
content = createMultiDeviceSentTranscriptContent(message.getSent().get(), unidentifiedAccess);
|
|
||||||
timestamp = message.getSent().get().getTimestamp();
|
|
||||||
} else if (message.getStickerPackOperations().isPresent()) {
|
|
||||||
content = createMultiDeviceStickerPackOperationContent(message.getStickerPackOperations().get());
|
|
||||||
} else if (message.getVerified().isPresent()) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
throw new IOException("Unsupported sync message!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
||||||
@ -373,10 +317,6 @@ public class SignalServiceMessageSender {
|
|||||||
else if (message.isTypingStopped()) builder.setAction(TypingMessage.Action.STOPPED);
|
else if (message.isTypingStopped()) builder.setAction(TypingMessage.Action.STOPPED);
|
||||||
else throw new IllegalArgumentException("Unknown typing indicator");
|
else throw new IllegalArgumentException("Unknown typing indicator");
|
||||||
|
|
||||||
if (message.getGroupId().isPresent()) {
|
|
||||||
builder.setGroupId(ByteString.copyFrom(message.getGroupId().get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return container.setTypingMessage(builder).build().toByteArray();
|
return container.setTypingMessage(builder).build().toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,18 +354,10 @@ public class SignalServiceMessageSender {
|
|||||||
builder.setGroup(createGroupContent(message.getGroupInfo().get(), recipient));
|
builder.setGroup(createGroupContent(message.getGroupInfo().get(), recipient));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isEndSession()) {
|
|
||||||
builder.setFlags(DataMessage.Flags.END_SESSION_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.isExpirationUpdate()) {
|
if (message.isExpirationUpdate()) {
|
||||||
builder.setFlags(DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE);
|
builder.setFlags(DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isProfileKeyUpdate()) {
|
|
||||||
builder.setFlags(DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.getExpiresInSeconds() > 0) {
|
if (message.getExpiresInSeconds() > 0) {
|
||||||
builder.setExpireTimer(message.getExpiresInSeconds());
|
builder.setExpireTimer(message.getExpiresInSeconds());
|
||||||
}
|
}
|
||||||
@ -485,27 +417,11 @@ public class SignalServiceMessageSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.getSticker().isPresent()) {
|
LokiProfile.Builder lokiUserProfileBuilder = LokiProfile.newBuilder();
|
||||||
DataMessage.Sticker.Builder stickerBuilder = DataMessage.Sticker.newBuilder();
|
|
||||||
|
|
||||||
stickerBuilder.setPackId(ByteString.copyFrom(message.getSticker().get().getPackId()));
|
|
||||||
stickerBuilder.setPackKey(ByteString.copyFrom(message.getSticker().get().getPackKey()));
|
|
||||||
stickerBuilder.setStickerId(message.getSticker().get().getStickerId());
|
|
||||||
|
|
||||||
if (message.getSticker().get().getAttachment().isStream()) {
|
|
||||||
stickerBuilder.setData(createAttachmentPointer(message.getSticker().get().getAttachment().asStream(), true, recipient));
|
|
||||||
} else {
|
|
||||||
stickerBuilder.setData(createAttachmentPointer(message.getSticker().get().getAttachment().asPointer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.setSticker(stickerBuilder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
LokiUserProfile.Builder lokiUserProfileBuilder = LokiUserProfile.newBuilder();
|
|
||||||
String displayName = userDatabase.getDisplayName(userPublicKey);
|
String displayName = userDatabase.getDisplayName(userPublicKey);
|
||||||
if (displayName != null) { lokiUserProfileBuilder.setDisplayName(displayName); }
|
if (displayName != null) { lokiUserProfileBuilder.setDisplayName(displayName); }
|
||||||
String profilePictureURL = userDatabase.getProfilePictureURL(userPublicKey);
|
String profilePictureURL = userDatabase.getProfilePictureURL(userPublicKey);
|
||||||
if (profilePictureURL != null) { lokiUserProfileBuilder.setProfilePictureURL(profilePictureURL); }
|
if (profilePictureURL != null) { lokiUserProfileBuilder.setProfilePicture(profilePictureURL); }
|
||||||
builder.setProfile(lokiUserProfileBuilder.build());
|
builder.setProfile(lokiUserProfileBuilder.build());
|
||||||
|
|
||||||
builder.setTimestamp(message.getTimestamp());
|
builder.setTimestamp(message.getTimestamp());
|
||||||
@ -515,183 +431,6 @@ public class SignalServiceMessageSender {
|
|||||||
return container.build().toByteArray();
|
return container.build().toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createMultiDeviceContactsContent(SignalServiceAttachmentStream contacts, boolean complete)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder builder = createSyncMessageBuilder();
|
|
||||||
builder.setContacts(SyncMessage.Contacts.newBuilder()
|
|
||||||
.setData(ByteString.readFrom(contacts.getInputStream()))
|
|
||||||
.setComplete(complete));
|
|
||||||
|
|
||||||
return container.setSyncMessage(builder).build().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceGroupsContent(SignalServiceAttachmentStream groups)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder builder = createSyncMessageBuilder();
|
|
||||||
builder.setGroups(SyncMessage.Groups.newBuilder()
|
|
||||||
.setData(ByteString.readFrom(groups.getInputStream())));
|
|
||||||
|
|
||||||
return container.setSyncMessage(builder).build().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceOpenGroupsContent(List<PublicChat> openGroups)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder builder = createSyncMessageBuilder();
|
|
||||||
for (PublicChat openGroup : openGroups) {
|
|
||||||
String url = openGroup.getServer();
|
|
||||||
int channel = Long.valueOf(openGroup.getChannel()).intValue();
|
|
||||||
SyncMessage.OpenGroupDetails details = SyncMessage.OpenGroupDetails.newBuilder()
|
|
||||||
.setUrl(url)
|
|
||||||
.setChannelID(channel)
|
|
||||||
.build();
|
|
||||||
builder.addOpenGroups(details);
|
|
||||||
}
|
|
||||||
|
|
||||||
return container.setSyncMessage(builder).build().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceSentTranscriptContent(SentTranscriptMessage transcript, Optional<UnidentifiedAccessPair> unidentifiedAccess)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
SignalServiceAddress address = new SignalServiceAddress(transcript.getDestination().get());
|
|
||||||
SendMessageResult result = SendMessageResult.success(address, unidentifiedAccess.isPresent(), true);
|
|
||||||
|
|
||||||
return createMultiDeviceSentTranscriptContent(createMessageContent(transcript.getMessage(), address),
|
|
||||||
Optional.of(address),
|
|
||||||
transcript.getTimestamp(),
|
|
||||||
Collections.singletonList(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceSentTranscriptContent(byte[] content, Optional<SignalServiceAddress> recipient,
|
|
||||||
long timestamp, List<SendMessageResult> sendMessageResults)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder syncMessage = createSyncMessageBuilder();
|
|
||||||
SyncMessage.Sent.Builder sentMessage = SyncMessage.Sent.newBuilder();
|
|
||||||
DataMessage dataMessage = Content.parseFrom(content).getDataMessage();
|
|
||||||
|
|
||||||
sentMessage.setTimestamp(timestamp);
|
|
||||||
sentMessage.setMessage(dataMessage);
|
|
||||||
|
|
||||||
for (SendMessageResult result : sendMessageResults) {
|
|
||||||
if (result.getSuccess() != null) {
|
|
||||||
sentMessage.addUnidentifiedStatus(SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder()
|
|
||||||
.setDestination(result.getAddress().getNumber())
|
|
||||||
.setUnidentified(result.getSuccess().isUnidentified()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recipient.isPresent()) {
|
|
||||||
sentMessage.setDestination(recipient.get().getNumber());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataMessage.getExpireTimer() > 0) {
|
|
||||||
sentMessage.setExpirationStartTimestamp(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
return container.setSyncMessage(syncMessage.setSent(sentMessage)).build().toByteArray();
|
|
||||||
} catch (InvalidProtocolBufferException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceReadContent(List<ReadMessage> readMessages) {
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder builder = createSyncMessageBuilder();
|
|
||||||
|
|
||||||
for (ReadMessage readMessage : readMessages) {
|
|
||||||
builder.addRead(SyncMessage.Read.newBuilder()
|
|
||||||
.setTimestamp(readMessage.getTimestamp())
|
|
||||||
.setSender(readMessage.getSender()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return container.setSyncMessage(builder).build().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceBlockedContent(BlockedListMessage blocked) {
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder syncMessage = createSyncMessageBuilder();
|
|
||||||
SyncMessage.Blocked.Builder blockedMessage = SyncMessage.Blocked.newBuilder();
|
|
||||||
|
|
||||||
blockedMessage.addAllNumbers(blocked.getNumbers());
|
|
||||||
|
|
||||||
for (byte[] groupId : blocked.getGroupIds()) {
|
|
||||||
blockedMessage.addGroupIds(ByteString.copyFrom(groupId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return container.setSyncMessage(syncMessage.setBlocked(blockedMessage)).build().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceConfigurationContent(ConfigurationMessage configuration) {
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder syncMessage = createSyncMessageBuilder();
|
|
||||||
SyncMessage.Configuration.Builder configurationMessage = SyncMessage.Configuration.newBuilder();
|
|
||||||
|
|
||||||
if (configuration.getReadReceipts().isPresent()) {
|
|
||||||
configurationMessage.setReadReceipts(configuration.getReadReceipts().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuration.getUnidentifiedDeliveryIndicators().isPresent()) {
|
|
||||||
configurationMessage.setUnidentifiedDeliveryIndicators(configuration.getUnidentifiedDeliveryIndicators().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuration.getTypingIndicators().isPresent()) {
|
|
||||||
configurationMessage.setTypingIndicators(configuration.getTypingIndicators().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configuration.getLinkPreviews().isPresent()) {
|
|
||||||
configurationMessage.setLinkPreviews(configuration.getLinkPreviews().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
return container.setSyncMessage(syncMessage.setConfiguration(configurationMessage)).build().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createMultiDeviceStickerPackOperationContent(List<StickerPackOperationMessage> stickerPackOperations) {
|
|
||||||
Content.Builder container = Content.newBuilder();
|
|
||||||
SyncMessage.Builder syncMessage = createSyncMessageBuilder();
|
|
||||||
|
|
||||||
for (StickerPackOperationMessage stickerPackOperation : stickerPackOperations) {
|
|
||||||
SyncMessage.StickerPackOperation.Builder builder = SyncMessage.StickerPackOperation.newBuilder();
|
|
||||||
|
|
||||||
if (stickerPackOperation.getPackId().isPresent()) {
|
|
||||||
builder.setPackId(ByteString.copyFrom(stickerPackOperation.getPackId().get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stickerPackOperation.getPackKey().isPresent()) {
|
|
||||||
builder.setPackKey(ByteString.copyFrom(stickerPackOperation.getPackKey().get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stickerPackOperation.getType().isPresent()) {
|
|
||||||
switch (stickerPackOperation.getType().get()) {
|
|
||||||
case INSTALL: builder.setType(SyncMessage.StickerPackOperation.Type.INSTALL); break;
|
|
||||||
case REMOVE: builder.setType(SyncMessage.StickerPackOperation.Type.REMOVE); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
syncMessage.addStickerPackOperation(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return container.setSyncMessage(syncMessage).build().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private SyncMessage.Builder createSyncMessageBuilder() {
|
|
||||||
SecureRandom random = new SecureRandom();
|
|
||||||
byte[] padding = Util.getRandomLengthBytes(512);
|
|
||||||
random.nextBytes(padding);
|
|
||||||
|
|
||||||
SyncMessage.Builder builder = SyncMessage.newBuilder();
|
|
||||||
builder.setPadding(ByteString.copyFrom(padding));
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private GroupContext createGroupContent(SignalServiceGroup group, SignalServiceAddress recipient)
|
private GroupContext createGroupContent(SignalServiceGroup group, SignalServiceAddress recipient)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
package org.session.libsignal.service.api.crypto;
|
package org.session.libsignal.service.api.crypto;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.ecc.ECKeyPair;
|
import org.session.libsignal.libsignal.ecc.ECKeyPair;
|
||||||
@ -22,9 +21,7 @@ import org.session.libsignal.metadata.ProtocolNoSessionException;
|
|||||||
import org.session.libsignal.metadata.ProtocolUntrustedIdentityException;
|
import org.session.libsignal.metadata.ProtocolUntrustedIdentityException;
|
||||||
import org.session.libsignal.metadata.SealedSessionCipher;
|
import org.session.libsignal.metadata.SealedSessionCipher;
|
||||||
import org.session.libsignal.metadata.SelfSendException;
|
import org.session.libsignal.metadata.SelfSendException;
|
||||||
import org.session.libsignal.metadata.certificate.CertificateValidator;
|
|
||||||
import org.session.libsignal.libsignal.DuplicateMessageException;
|
import org.session.libsignal.libsignal.DuplicateMessageException;
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyException;
|
import org.session.libsignal.libsignal.InvalidKeyException;
|
||||||
import org.session.libsignal.libsignal.InvalidKeyIdException;
|
import org.session.libsignal.libsignal.InvalidKeyIdException;
|
||||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
import org.session.libsignal.libsignal.InvalidMessageException;
|
||||||
@ -36,69 +33,37 @@ import org.session.libsignal.libsignal.SignalProtocolAddress;
|
|||||||
import org.session.libsignal.libsignal.UntrustedIdentityException;
|
import org.session.libsignal.libsignal.UntrustedIdentityException;
|
||||||
import org.session.libsignal.libsignal.loki.LokiSessionCipher;
|
import org.session.libsignal.libsignal.loki.LokiSessionCipher;
|
||||||
import org.session.libsignal.libsignal.loki.SessionResetProtocol;
|
import org.session.libsignal.libsignal.loki.SessionResetProtocol;
|
||||||
import org.session.libsignal.libsignal.protocol.CiphertextMessage;
|
|
||||||
import org.session.libsignal.libsignal.protocol.PreKeySignalMessage;
|
import org.session.libsignal.libsignal.protocol.PreKeySignalMessage;
|
||||||
import org.session.libsignal.libsignal.protocol.SignalMessage;
|
import org.session.libsignal.libsignal.protocol.SignalMessage;
|
||||||
import org.session.libsignal.libsignal.state.SignalProtocolStore;
|
import org.session.libsignal.libsignal.state.SignalProtocolStore;
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer;
|
import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceContent;
|
import org.session.libsignal.service.api.messages.SignalServiceContent;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview;
|
import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Sticker;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceEnvelope;
|
import org.session.libsignal.service.api.messages.SignalServiceEnvelope;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceGroup;
|
import org.session.libsignal.service.api.messages.SignalServiceGroup;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceNullMessage;
|
|
||||||
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.BusyMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.calls.HangupMessage;
|
|
||||||
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.ContactsMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.ReadMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.RequestMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SentTranscriptMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage.VerifiedState;
|
|
||||||
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
import org.session.libsignal.service.internal.push.OutgoingPushMessage;
|
|
||||||
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.ClosedGroupUpdate;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ClosedGroupControlMessage;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateV2;
|
|
||||||
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.Envelope.Type;
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.ReceiptMessage;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage;
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.TypingMessage;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.Verified;
|
|
||||||
import org.session.libsignal.utilities.Base64;
|
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocol;
|
import org.session.libsignal.service.loki.api.crypto.SessionProtocol;
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocolUtilities;
|
import org.session.libsignal.service.loki.api.crypto.SessionProtocolUtilities;
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
|
||||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
|
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
|
||||||
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.session.libsignal.service.internal.push.SignalServiceProtos.CallMessage;
|
|
||||||
import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.DELIVER;
|
import static org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext.Type.DELIVER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,67 +81,20 @@ public class SignalServiceCipher {
|
|||||||
private final SignalServiceAddress localAddress;
|
private final SignalServiceAddress localAddress;
|
||||||
private final SessionProtocol sessionProtocolImpl;
|
private final SessionProtocol sessionProtocolImpl;
|
||||||
private final LokiAPIDatabaseProtocol apiDB;
|
private final LokiAPIDatabaseProtocol apiDB;
|
||||||
private final CertificateValidator certificateValidator;
|
|
||||||
|
|
||||||
public SignalServiceCipher(SignalServiceAddress localAddress,
|
public SignalServiceCipher(SignalServiceAddress localAddress,
|
||||||
SignalProtocolStore signalProtocolStore,
|
SignalProtocolStore signalProtocolStore,
|
||||||
SessionResetProtocol sessionResetProtocol,
|
SessionResetProtocol sessionResetProtocol,
|
||||||
SessionProtocol sessionProtocolImpl,
|
SessionProtocol sessionProtocolImpl,
|
||||||
LokiAPIDatabaseProtocol apiDB,
|
LokiAPIDatabaseProtocol apiDB)
|
||||||
CertificateValidator certificateValidator)
|
|
||||||
{
|
{
|
||||||
this.signalProtocolStore = signalProtocolStore;
|
this.signalProtocolStore = signalProtocolStore;
|
||||||
this.sessionResetProtocol = sessionResetProtocol;
|
this.sessionResetProtocol = sessionResetProtocol;
|
||||||
this.localAddress = localAddress;
|
this.localAddress = localAddress;
|
||||||
this.sessionProtocolImpl = sessionProtocolImpl;
|
this.sessionProtocolImpl = sessionProtocolImpl;
|
||||||
this.apiDB = apiDB;
|
this.apiDB = apiDB;
|
||||||
this.certificateValidator = certificateValidator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// public OutgoingPushMessage encrypt(SignalProtocolAddress destination,
|
|
||||||
// Optional<UnidentifiedAccess> unidentifiedAccess,
|
|
||||||
// byte[] unpaddedMessage)
|
|
||||||
// throws UntrustedIdentityException, InvalidKeyException, IOException
|
|
||||||
// {
|
|
||||||
// if (unidentifiedAccess.isPresent() && sskDatabase.isSSKBasedClosedGroup(destination.getName())) {
|
|
||||||
// String userPublicKey = localAddress.getNumber();
|
|
||||||
// SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userPublicKey, 1);
|
|
||||||
// SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, signalProtocolAddress);
|
|
||||||
// PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination));
|
|
||||||
// byte[] plaintext = transportDetails.getPaddedMessageBody(unpaddedMessage);
|
|
||||||
// byte[] ciphertext = ClosedGroupUtilities.encrypt(plaintext, destination.getName(), userPublicKey);
|
|
||||||
// String body = Base64.encodeBytes(ciphertext);
|
|
||||||
// int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(destination);
|
|
||||||
// return new OutgoingPushMessage(Type.CLOSED_GROUP_CIPHERTEXT_VALUE, destination.getDeviceId(), remoteRegistrationId, body);
|
|
||||||
// } else if (unidentifiedAccess.isPresent()) {
|
|
||||||
// SealedSessionCipher sessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1));
|
|
||||||
// PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion(destination));
|
|
||||||
// byte[] ciphertext = sessionCipher.encrypt(destination, unidentifiedAccess.get().getUnidentifiedCertificate(), transportDetails.getPaddedMessageBody(unpaddedMessage));
|
|
||||||
// String body = Base64.encodeBytes(ciphertext);
|
|
||||||
// int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(destination);
|
|
||||||
//
|
|
||||||
// return new OutgoingPushMessage(Type.UNIDENTIFIED_SENDER_VALUE, destination.getDeviceId(), remoteRegistrationId, body);
|
|
||||||
// } else {
|
|
||||||
// SessionCipher sessionCipher = new SessionCipher(signalProtocolStore, destination);
|
|
||||||
// PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion());
|
|
||||||
// CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage));
|
|
||||||
// int remoteRegistrationId = sessionCipher.getRemoteRegistrationId();
|
|
||||||
// String body = Base64.encodeBytes(message.serialize());
|
|
||||||
//
|
|
||||||
// int type;
|
|
||||||
//
|
|
||||||
// switch (message.getType()) {
|
|
||||||
// case CiphertextMessage.PREKEY_TYPE: type = Type.PREKEY_BUNDLE_VALUE; break;
|
|
||||||
// case CiphertextMessage.WHISPER_TYPE: type = Type.CIPHERTEXT_VALUE; break;
|
|
||||||
// case CiphertextMessage.FALLBACK_MESSAGE_TYPE: type = Type.FALLBACK_MESSAGE_VALUE; break;
|
|
||||||
// case CiphertextMessage.CLOSED_GROUP_CIPHERTEXT: type = Type.CLOSED_GROUP_CIPHERTEXT_VALUE; break;
|
|
||||||
// default: throw new AssertionError("Bad type: " + message.getType());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return new OutgoingPushMessage(type, destination.getDeviceId(), remoteRegistrationId, body);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt a received {@link SignalServiceEnvelope}
|
* Decrypt a received {@link SignalServiceEnvelope}
|
||||||
*
|
*
|
||||||
@ -184,32 +102,12 @@ public class SignalServiceCipher {
|
|||||||
*
|
*
|
||||||
* @return a decrypted SignalServiceContent
|
* @return a decrypted SignalServiceContent
|
||||||
*/
|
*/
|
||||||
public SignalServiceContent decrypt(SignalServiceEnvelope envelope)
|
public SignalServiceContent decrypt(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException,ProtocolInvalidMessageException
|
||||||
throws InvalidMetadataMessageException, InvalidMetadataVersionException,
|
|
||||||
ProtocolInvalidKeyIdException, ProtocolLegacyMessageException,
|
|
||||||
ProtocolUntrustedIdentityException, ProtocolNoSessionException,
|
|
||||||
ProtocolInvalidVersionException, ProtocolInvalidMessageException,
|
|
||||||
ProtocolInvalidKeyException, ProtocolDuplicateMessageException,
|
|
||||||
SelfSendException, IOException, SessionProtocol.Exception
|
|
||||||
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Plaintext plaintext = decrypt(envelope, envelope.getContent());
|
Plaintext plaintext = decrypt(envelope, envelope.getContent());
|
||||||
Content message = Content.parseFrom(plaintext.getData());
|
Content message = Content.parseFrom(plaintext.getData());
|
||||||
|
|
||||||
PreKeyBundleMessage preKeyBundleMessage = null;
|
|
||||||
if (message.hasPreKeyBundleMessage()) {
|
|
||||||
SignalServiceProtos.PreKeyBundleMessage protoPreKeyBundleMessage = message.getPreKeyBundleMessage();
|
|
||||||
preKeyBundleMessage = new PreKeyBundleMessage(protoPreKeyBundleMessage.getIdentityKey().toByteArray(),
|
|
||||||
protoPreKeyBundleMessage.getDeviceId(),
|
|
||||||
protoPreKeyBundleMessage.getPreKeyId(),
|
|
||||||
protoPreKeyBundleMessage.getSignedKeyId(),
|
|
||||||
protoPreKeyBundleMessage.getPreKey().toByteArray(),
|
|
||||||
protoPreKeyBundleMessage.getSignedKey().toByteArray(),
|
|
||||||
protoPreKeyBundleMessage.getSignature().toByteArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.hasConfigurationMessage()) {
|
if (message.hasConfigurationMessage()) {
|
||||||
SignalServiceCipher.Metadata metadata = plaintext.getMetadata();
|
SignalServiceCipher.Metadata metadata = plaintext.getMetadata();
|
||||||
SignalServiceContent content = new SignalServiceContent(message, metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp());
|
SignalServiceContent content = new SignalServiceContent(message, metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp());
|
||||||
@ -231,30 +129,9 @@ public class SignalServiceCipher {
|
|||||||
plaintext.getMetadata().getTimestamp(),
|
plaintext.getMetadata().getTimestamp(),
|
||||||
plaintext.getMetadata().isNeedsReceipt());
|
plaintext.getMetadata().isNeedsReceipt());
|
||||||
|
|
||||||
content.setPreKeyBundleMessage(preKeyBundleMessage);
|
|
||||||
|
|
||||||
setProfile(dataMessage, content);
|
setProfile(dataMessage, content);
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
} else if (message.hasSyncMessage()) {
|
|
||||||
SignalServiceContent content = new SignalServiceContent(createSynchronizeMessage(
|
|
||||||
plaintext.getMetadata(),
|
|
||||||
message.getSyncMessage()),
|
|
||||||
plaintext.getMetadata().getSender(),
|
|
||||||
plaintext.getMetadata().getSenderDevice(),
|
|
||||||
plaintext.getMetadata().getTimestamp());
|
|
||||||
|
|
||||||
if (message.getSyncMessage().hasSent() && message.getSyncMessage().getSent().hasMessage()) {
|
|
||||||
DataMessage dataMessage = message.getSyncMessage().getSent().getMessage();
|
|
||||||
setProfile(dataMessage, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
} else if (message.hasCallMessage()) {
|
|
||||||
return new SignalServiceContent(createCallMessage(message.getCallMessage()),
|
|
||||||
plaintext.getMetadata().getSender(),
|
|
||||||
plaintext.getMetadata().getSenderDevice(),
|
|
||||||
plaintext.getMetadata().getTimestamp());
|
|
||||||
} else if (message.hasReceiptMessage()) {
|
} else if (message.hasReceiptMessage()) {
|
||||||
return new SignalServiceContent(createReceiptMessage(plaintext.getMetadata(), message.getReceiptMessage()),
|
return new SignalServiceContent(createReceiptMessage(plaintext.getMetadata(), message.getReceiptMessage()),
|
||||||
plaintext.getMetadata().getSender(),
|
plaintext.getMetadata().getSender(),
|
||||||
@ -265,15 +142,6 @@ public class SignalServiceCipher {
|
|||||||
plaintext.getMetadata().getSender(),
|
plaintext.getMetadata().getSender(),
|
||||||
plaintext.getMetadata().getSenderDevice(),
|
plaintext.getMetadata().getSenderDevice(),
|
||||||
plaintext.getMetadata().getTimestamp());
|
plaintext.getMetadata().getTimestamp());
|
||||||
} else if (message.hasNullMessage()) {
|
|
||||||
SignalServiceContent content = new SignalServiceContent(new SignalServiceNullMessage(),
|
|
||||||
plaintext.getMetadata().getSender(),
|
|
||||||
plaintext.getMetadata().getSenderDevice(),
|
|
||||||
plaintext.getMetadata().getTimestamp());
|
|
||||||
|
|
||||||
content.setPreKeyBundleMessage(preKeyBundleMessage);
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -284,90 +152,53 @@ public class SignalServiceCipher {
|
|||||||
|
|
||||||
private void setProfile(DataMessage message, SignalServiceContent content) {
|
private void setProfile(DataMessage message, SignalServiceContent content) {
|
||||||
if (!message.hasProfile()) { return; }
|
if (!message.hasProfile()) { return; }
|
||||||
SignalServiceProtos.LokiUserProfile profile = message.getProfile();
|
SignalServiceProtos.DataMessage.LokiProfile profile = message.getProfile();
|
||||||
if (profile.hasDisplayName()) { content.setSenderDisplayName(profile.getDisplayName()); }
|
if (profile.hasDisplayName()) { content.setSenderDisplayName(profile.getDisplayName()); }
|
||||||
if (profile.hasProfilePictureURL()) { content.setSenderProfilePictureURL(profile.getProfilePictureURL()); }
|
if (profile.hasProfilePicture()) { content.setSenderProfilePictureURL(profile.getProfilePicture()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext)
|
protected Plaintext decrypt(SignalServiceEnvelope envelope, byte[] ciphertext) throws InvalidMetadataMessageException
|
||||||
throws InvalidMetadataMessageException, InvalidMetadataVersionException,
|
|
||||||
ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException,
|
|
||||||
ProtocolLegacyMessageException, ProtocolInvalidKeyException,
|
|
||||||
ProtocolInvalidVersionException, ProtocolInvalidMessageException,
|
|
||||||
ProtocolInvalidKeyIdException, ProtocolNoSessionException,
|
|
||||||
SelfSendException, IOException, SessionProtocol.Exception
|
|
||||||
{
|
{
|
||||||
try {
|
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), envelope.getSourceDevice());
|
||||||
SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.getSource(), envelope.getSourceDevice());
|
SessionCipher sessionCipher = new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sourceAddress);
|
||||||
SessionCipher sessionCipher = new LokiSessionCipher(signalProtocolStore, sessionResetProtocol, sourceAddress);
|
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1));
|
||||||
SealedSessionCipher sealedSessionCipher = new SealedSessionCipher(signalProtocolStore, sessionResetProtocol, new SignalProtocolAddress(localAddress.getNumber(), 1));
|
|
||||||
|
|
||||||
byte[] paddedMessage;
|
byte[] paddedMessage;
|
||||||
Metadata metadata;
|
Metadata metadata;
|
||||||
int sessionVersion;
|
int sessionVersion;
|
||||||
|
|
||||||
if (envelope.isClosedGroupCiphertext()) {
|
if (envelope.isClosedGroupCiphertext()) {
|
||||||
String groupPublicKey = envelope.getSource();
|
String groupPublicKey = envelope.getSource();
|
||||||
kotlin.Pair<byte[], String> plaintextAndSenderPublicKey = SessionProtocolUtilities.INSTANCE.decryptClosedGroupCiphertext(ciphertext, groupPublicKey, apiDB, sessionProtocolImpl);
|
kotlin.Pair<byte[], String> plaintextAndSenderPublicKey = SessionProtocolUtilities.INSTANCE.decryptClosedGroupCiphertext(ciphertext, groupPublicKey, apiDB, sessionProtocolImpl);
|
||||||
paddedMessage = plaintextAndSenderPublicKey.getFirst();
|
paddedMessage = plaintextAndSenderPublicKey.getFirst();
|
||||||
String senderPublicKey = plaintextAndSenderPublicKey.getSecond();
|
String senderPublicKey = plaintextAndSenderPublicKey.getSecond();
|
||||||
metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false);
|
metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false);
|
||||||
sessionVersion = sessionCipher.getSessionVersion();
|
sessionVersion = sessionCipher.getSessionVersion();
|
||||||
} else if (envelope.isPreKeySignalMessage()) {
|
} else if (envelope.isUnidentifiedSender()) {
|
||||||
paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(ciphertext));
|
ECKeyPair userX25519KeyPair = apiDB.getUserX25519KeyPair();
|
||||||
metadata = new Metadata(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), false);
|
kotlin.Pair<byte[], String> plaintextAndSenderPublicKey = sessionProtocolImpl.decrypt(ciphertext, userX25519KeyPair);
|
||||||
sessionVersion = sessionCipher.getSessionVersion();
|
paddedMessage = plaintextAndSenderPublicKey.getFirst();
|
||||||
} else if (envelope.isSignalMessage()) {
|
String senderPublicKey = plaintextAndSenderPublicKey.getSecond();
|
||||||
paddedMessage = sessionCipher.decrypt(new SignalMessage(ciphertext));
|
metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false);
|
||||||
metadata = new Metadata(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), false);
|
sessionVersion = sealedSessionCipher.getSessionVersion(new SignalProtocolAddress(metadata.getSender(), metadata.getSenderDevice()));
|
||||||
sessionVersion = sessionCipher.getSessionVersion();
|
} else {
|
||||||
} else if (envelope.isUnidentifiedSender()) {
|
throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType());
|
||||||
ECKeyPair userX25519KeyPair = apiDB.getUserX25519KeyPair();
|
|
||||||
kotlin.Pair<byte[], String> plaintextAndSenderPublicKey = sessionProtocolImpl.decrypt(ciphertext, userX25519KeyPair);
|
|
||||||
paddedMessage = plaintextAndSenderPublicKey.getFirst();
|
|
||||||
String senderPublicKey = plaintextAndSenderPublicKey.getSecond();
|
|
||||||
metadata = new Metadata(senderPublicKey, 1, envelope.getTimestamp(), false);
|
|
||||||
sessionVersion = sealedSessionCipher.getSessionVersion(new SignalProtocolAddress(metadata.getSender(), metadata.getSenderDevice()));
|
|
||||||
} else {
|
|
||||||
throw new InvalidMetadataMessageException("Unknown type: " + envelope.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
PushTransportDetails transportDetails = new PushTransportDetails(sessionVersion);
|
|
||||||
byte[] data = transportDetails.getStrippedPaddingMessageBody(paddedMessage);
|
|
||||||
|
|
||||||
return new Plaintext(metadata, data);
|
|
||||||
} catch (DuplicateMessageException e) {
|
|
||||||
throw new ProtocolDuplicateMessageException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
} catch (LegacyMessageException e) {
|
|
||||||
throw new ProtocolLegacyMessageException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
} catch (InvalidMessageException e) {
|
|
||||||
throw new ProtocolInvalidMessageException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
} catch (InvalidKeyIdException e) {
|
|
||||||
throw new ProtocolInvalidKeyIdException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new ProtocolInvalidKeyException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
} catch (UntrustedIdentityException e) {
|
|
||||||
throw new ProtocolUntrustedIdentityException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
} catch (InvalidVersionException e) {
|
|
||||||
throw new ProtocolInvalidVersionException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
} catch (NoSessionException e) {
|
|
||||||
throw new ProtocolNoSessionException(e, envelope.getSource(), envelope.getSourceDevice());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PushTransportDetails transportDetails = new PushTransportDetails(sessionVersion);
|
||||||
|
byte[] data = transportDetails.getStrippedPaddingMessageBody(paddedMessage);
|
||||||
|
|
||||||
|
return new Plaintext(metadata, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceDataMessage createSignalServiceMessage(Metadata metadata, DataMessage content) throws ProtocolInvalidMessageException {
|
private SignalServiceDataMessage createSignalServiceMessage(Metadata metadata, DataMessage content) throws ProtocolInvalidMessageException {
|
||||||
SignalServiceGroup groupInfo = createGroupInfo(content);
|
SignalServiceGroup groupInfo = createGroupInfo(content);
|
||||||
List<SignalServiceAttachment> attachments = new LinkedList<SignalServiceAttachment>();
|
List<SignalServiceAttachment> attachments = new LinkedList<SignalServiceAttachment>();
|
||||||
boolean endSession = ((content.getFlags() & DataMessage.Flags.END_SESSION_VALUE ) != 0);
|
|
||||||
boolean expirationUpdate = ((content.getFlags() & DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE) != 0);
|
boolean expirationUpdate = ((content.getFlags() & DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE) != 0);
|
||||||
boolean profileKeyUpdate = ((content.getFlags() & DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE ) != 0);
|
|
||||||
SignalServiceDataMessage.Quote quote = createQuote(content);
|
SignalServiceDataMessage.Quote quote = createQuote(content);
|
||||||
List<SharedContact> sharedContacts = createSharedContacts(content);
|
List<SharedContact> sharedContacts = createSharedContacts(content);
|
||||||
List<Preview> previews = createPreviews(content);
|
List<Preview> previews = createPreviews(content);
|
||||||
Sticker sticker = createSticker(content);
|
ClosedGroupControlMessage closedGroupControlMessage = content.getClosedGroupControlMessage();
|
||||||
ClosedGroupUpdate closedGroupUpdate = content.getClosedGroupUpdate();
|
|
||||||
ClosedGroupUpdateV2 closedGroupUpdateV2 = content.getClosedGroupUpdateV2();
|
|
||||||
boolean isDeviceUnlinkingRequest = ((content.getFlags() & DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE) != 0);
|
|
||||||
String syncTarget = content.getSyncTarget();
|
String syncTarget = content.getSyncTarget();
|
||||||
|
|
||||||
for (AttachmentPointer pointer : content.getAttachmentsList()) {
|
for (AttachmentPointer pointer : content.getAttachmentsList()) {
|
||||||
@ -384,170 +215,16 @@ public class SignalServiceCipher {
|
|||||||
groupInfo,
|
groupInfo,
|
||||||
attachments,
|
attachments,
|
||||||
content.getBody(),
|
content.getBody(),
|
||||||
endSession,
|
|
||||||
content.getExpireTimer(),
|
content.getExpireTimer(),
|
||||||
expirationUpdate,
|
expirationUpdate,
|
||||||
content.hasProfileKey() ? content.getProfileKey().toByteArray() : null,
|
content.hasProfileKey() ? content.getProfileKey().toByteArray() : null,
|
||||||
profileKeyUpdate,
|
|
||||||
quote,
|
quote,
|
||||||
sharedContacts,
|
sharedContacts,
|
||||||
previews,
|
previews,
|
||||||
sticker,
|
closedGroupControlMessage,
|
||||||
null,
|
|
||||||
closedGroupUpdate,
|
|
||||||
closedGroupUpdateV2,
|
|
||||||
syncTarget);
|
syncTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceSyncMessage createSynchronizeMessage(Metadata metadata, SyncMessage content)
|
|
||||||
throws ProtocolInvalidMessageException, ProtocolInvalidKeyException
|
|
||||||
{
|
|
||||||
if (content.hasSent()) {
|
|
||||||
SyncMessage.Sent sentContent = content.getSent();
|
|
||||||
Map<String, Boolean> unidentifiedStatuses = new HashMap<String, Boolean>();
|
|
||||||
|
|
||||||
for (SyncMessage.Sent.UnidentifiedDeliveryStatus status : sentContent.getUnidentifiedStatusList()) {
|
|
||||||
unidentifiedStatuses.put(status.getDestination(), status.getUnidentified());
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalServiceSyncMessage.forSentTranscript(new SentTranscriptMessage(sentContent.getDestination(),
|
|
||||||
sentContent.getTimestamp(),
|
|
||||||
createSignalServiceMessage(metadata, sentContent.getMessage()),
|
|
||||||
sentContent.getExpirationStartTimestamp(),
|
|
||||||
unidentifiedStatuses));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.hasRequest()) {
|
|
||||||
return SignalServiceSyncMessage.forRequest(new RequestMessage(content.getRequest()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.getReadList().size() > 0) {
|
|
||||||
List<ReadMessage> readMessages = new LinkedList<ReadMessage>();
|
|
||||||
|
|
||||||
for (SyncMessage.Read read : content.getReadList()) {
|
|
||||||
readMessages.add(new ReadMessage(read.getSender(), read.getTimestamp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalServiceSyncMessage.forRead(readMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.hasContacts()) {
|
|
||||||
SyncMessage.Contacts contacts = content.getContacts();
|
|
||||||
ByteString data = contacts.getData();
|
|
||||||
if (data != null && !data.isEmpty()) {
|
|
||||||
byte[] bytes = data.toByteArray();
|
|
||||||
SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(new ByteArrayInputStream(data.toByteArray()))
|
|
||||||
.withContentType("application/octet-stream")
|
|
||||||
.withLength(bytes.length)
|
|
||||||
.build();
|
|
||||||
return SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, contacts.getComplete()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.hasGroups()) {
|
|
||||||
SyncMessage.Groups groups = content.getGroups();
|
|
||||||
ByteString data = groups.getData();
|
|
||||||
if (data != null && !data.isEmpty()) {
|
|
||||||
byte[] bytes = data.toByteArray();
|
|
||||||
SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder()
|
|
||||||
.withStream(new ByteArrayInputStream(data.toByteArray()))
|
|
||||||
.withContentType("application/octet-stream")
|
|
||||||
.withLength(bytes.length)
|
|
||||||
.build();
|
|
||||||
return SignalServiceSyncMessage.forGroups(attachmentStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.hasVerified()) {
|
|
||||||
try {
|
|
||||||
Verified verified = content.getVerified();
|
|
||||||
String destination = verified.getDestination();
|
|
||||||
IdentityKey identityKey = new IdentityKey(verified.getIdentityKey().toByteArray(), 0);
|
|
||||||
|
|
||||||
VerifiedState verifiedState;
|
|
||||||
|
|
||||||
if (verified.getState() == Verified.State.DEFAULT) {
|
|
||||||
verifiedState = VerifiedState.DEFAULT;
|
|
||||||
} else if (verified.getState() == Verified.State.VERIFIED) {
|
|
||||||
verifiedState = VerifiedState.VERIFIED;
|
|
||||||
} else if (verified.getState() == Verified.State.UNVERIFIED) {
|
|
||||||
verifiedState = VerifiedState.UNVERIFIED;
|
|
||||||
} else {
|
|
||||||
throw new ProtocolInvalidMessageException(new InvalidMessageException("Unknown state: " + verified.getState().getNumber()),
|
|
||||||
metadata.getSender(), metadata.getSenderDevice());
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalServiceSyncMessage.forVerified(new VerifiedMessage(destination, identityKey, verifiedState, System.currentTimeMillis()));
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new ProtocolInvalidKeyException(e, metadata.getSender(), metadata.getSenderDevice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.getStickerPackOperationList().size() > 0) {
|
|
||||||
List<StickerPackOperationMessage> operations = new LinkedList<StickerPackOperationMessage>();
|
|
||||||
|
|
||||||
for (SyncMessage.StickerPackOperation operation : content.getStickerPackOperationList()) {
|
|
||||||
byte[] packId = operation.hasPackId() ? operation.getPackId().toByteArray() : null;
|
|
||||||
byte[] packKey = operation.hasPackKey() ? operation.getPackKey().toByteArray() : null;
|
|
||||||
StickerPackOperationMessage.Type type = null;
|
|
||||||
|
|
||||||
if (operation.hasType()) {
|
|
||||||
switch (operation.getType()) {
|
|
||||||
case INSTALL: type = StickerPackOperationMessage.Type.INSTALL; break;
|
|
||||||
case REMOVE: type = StickerPackOperationMessage.Type.REMOVE; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
operations.add(new StickerPackOperationMessage(packId, packKey, type));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalServiceSyncMessage.forStickerPackOperations(operations);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SyncMessage.OpenGroupDetails> openGroupDetails = content.getOpenGroupsList();
|
|
||||||
if (openGroupDetails.size() > 0) {
|
|
||||||
List<PublicChat> openGroups = new LinkedList<>();
|
|
||||||
for (SyncMessage.OpenGroupDetails details : content.getOpenGroupsList()) {
|
|
||||||
openGroups.add(new PublicChat(details.getChannelID(), details.getUrl(), "", true));
|
|
||||||
}
|
|
||||||
return SignalServiceSyncMessage.forOpenGroups(openGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.hasBlocked()) {
|
|
||||||
SyncMessage.Blocked blocked = content.getBlocked();
|
|
||||||
List<String> publicKeys = blocked.getNumbersList();
|
|
||||||
return SignalServiceSyncMessage.forBlocked(new BlockedListMessage(publicKeys, new ArrayList<byte[]>()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalServiceSyncMessage.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private SignalServiceCallMessage createCallMessage(CallMessage content) {
|
|
||||||
if (content.hasOffer()) {
|
|
||||||
CallMessage.Offer offerContent = content.getOffer();
|
|
||||||
return SignalServiceCallMessage.forOffer(new OfferMessage(offerContent.getId(), offerContent.getDescription()));
|
|
||||||
} else if (content.hasAnswer()) {
|
|
||||||
CallMessage.Answer answerContent = content.getAnswer();
|
|
||||||
return SignalServiceCallMessage.forAnswer(new AnswerMessage(answerContent.getId(), answerContent.getDescription()));
|
|
||||||
} else if (content.getIceUpdateCount() > 0) {
|
|
||||||
List<IceUpdateMessage> iceUpdates = new LinkedList<IceUpdateMessage>();
|
|
||||||
|
|
||||||
for (CallMessage.IceUpdate iceUpdate : content.getIceUpdateList()) {
|
|
||||||
iceUpdates.add(new IceUpdateMessage(iceUpdate.getId(), iceUpdate.getSdpMid(), iceUpdate.getSdpMLineIndex(), iceUpdate.getSdp()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalServiceCallMessage.forIceUpdates(iceUpdates);
|
|
||||||
} else if (content.hasHangup()) {
|
|
||||||
CallMessage.Hangup hangup = content.getHangup();
|
|
||||||
return SignalServiceCallMessage.forHangup(new HangupMessage(hangup.getId()));
|
|
||||||
} else if (content.hasBusy()) {
|
|
||||||
CallMessage.Busy busy = content.getBusy();
|
|
||||||
return SignalServiceCallMessage.forBusy(new BusyMessage(busy.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return SignalServiceCallMessage.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private SignalServiceReceiptMessage createReceiptMessage(Metadata metadata, ReceiptMessage content) {
|
private SignalServiceReceiptMessage createReceiptMessage(Metadata metadata, ReceiptMessage content) {
|
||||||
SignalServiceReceiptMessage.Type type;
|
SignalServiceReceiptMessage.Type type;
|
||||||
|
|
||||||
@ -571,9 +248,7 @@ public class SignalServiceCipher {
|
|||||||
metadata.getSenderDevice());
|
metadata.getSenderDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SignalServiceTypingMessage(action, content.getTimestamp(),
|
return new SignalServiceTypingMessage(action, content.getTimestamp());
|
||||||
content.hasGroupId() ? Optional.of(content.getGroupId().toByteArray()) :
|
|
||||||
Optional.<byte[]>absent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceDataMessage.Quote createQuote(DataMessage content) {
|
private SignalServiceDataMessage.Quote createQuote(DataMessage content) {
|
||||||
@ -613,24 +288,6 @@ public class SignalServiceCipher {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Sticker createSticker(DataMessage content) {
|
|
||||||
if (!content.hasSticker() ||
|
|
||||||
!content.getSticker().hasPackId() ||
|
|
||||||
!content.getSticker().hasPackKey() ||
|
|
||||||
!content.getSticker().hasStickerId() ||
|
|
||||||
!content.getSticker().hasData())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataMessage.Sticker sticker = content.getSticker();
|
|
||||||
|
|
||||||
return new Sticker(sticker.getPackId().toByteArray(),
|
|
||||||
sticker.getPackKey().toByteArray(),
|
|
||||||
sticker.getStickerId(),
|
|
||||||
createAttachmentPointer(sticker.getData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SharedContact> createSharedContacts(DataMessage content) {
|
private List<SharedContact> createSharedContacts(DataMessage content) {
|
||||||
if (content.getContactCount() <= 0) return null;
|
if (content.getContactCount() <= 0) return null;
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ package org.session.libsignal.service.api.messages;
|
|||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage;
|
import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage;
|
||||||
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
||||||
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
|
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
|
||||||
|
|
||||||
@ -20,9 +19,6 @@ public class SignalServiceContent {
|
|||||||
|
|
||||||
// Loki
|
// Loki
|
||||||
private Optional<SignalServiceDataMessage> message;
|
private Optional<SignalServiceDataMessage> message;
|
||||||
private Optional<SignalServiceSyncMessage> synchronizeMessage;
|
|
||||||
private final Optional<SignalServiceCallMessage> callMessage;
|
|
||||||
private final Optional<SignalServiceNullMessage> nullMessage;
|
|
||||||
private final Optional<SignalServiceReceiptMessage> readMessage;
|
private final Optional<SignalServiceReceiptMessage> readMessage;
|
||||||
private final Optional<SignalServiceTypingMessage> typingMessage;
|
private final Optional<SignalServiceTypingMessage> typingMessage;
|
||||||
|
|
||||||
@ -38,35 +34,6 @@ public class SignalServiceContent {
|
|||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.needsReceipt = needsReceipt;
|
this.needsReceipt = needsReceipt;
|
||||||
this.message = Optional.fromNullable(message);
|
this.message = Optional.fromNullable(message);
|
||||||
this.synchronizeMessage = Optional.absent();
|
|
||||||
this.callMessage = Optional.absent();
|
|
||||||
this.nullMessage = Optional.absent();
|
|
||||||
this.readMessage = Optional.absent();
|
|
||||||
this.typingMessage = Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, String sender, int senderDevice, long timestamp) {
|
|
||||||
this.sender = sender;
|
|
||||||
this.senderDevice = senderDevice;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.needsReceipt = false;
|
|
||||||
this.message = Optional.absent();
|
|
||||||
this.synchronizeMessage = Optional.fromNullable(synchronizeMessage);
|
|
||||||
this.callMessage = Optional.absent();
|
|
||||||
this.nullMessage = Optional.absent();
|
|
||||||
this.readMessage = Optional.absent();
|
|
||||||
this.typingMessage = Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalServiceContent(SignalServiceCallMessage callMessage, String sender, int senderDevice, long timestamp) {
|
|
||||||
this.sender = sender;
|
|
||||||
this.senderDevice = senderDevice;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.needsReceipt = false;
|
|
||||||
this.message = Optional.absent();
|
|
||||||
this.synchronizeMessage = Optional.absent();
|
|
||||||
this.callMessage = Optional.of(callMessage);
|
|
||||||
this.nullMessage = Optional.absent();
|
|
||||||
this.readMessage = Optional.absent();
|
this.readMessage = Optional.absent();
|
||||||
this.typingMessage = Optional.absent();
|
this.typingMessage = Optional.absent();
|
||||||
}
|
}
|
||||||
@ -77,9 +44,6 @@ public class SignalServiceContent {
|
|||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.needsReceipt = false;
|
this.needsReceipt = false;
|
||||||
this.message = Optional.absent();
|
this.message = Optional.absent();
|
||||||
this.synchronizeMessage = Optional.absent();
|
|
||||||
this.callMessage = Optional.absent();
|
|
||||||
this.nullMessage = Optional.absent();
|
|
||||||
this.readMessage = Optional.of(receiptMessage);
|
this.readMessage = Optional.of(receiptMessage);
|
||||||
this.typingMessage = Optional.absent();
|
this.typingMessage = Optional.absent();
|
||||||
}
|
}
|
||||||
@ -90,9 +54,6 @@ public class SignalServiceContent {
|
|||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.needsReceipt = false;
|
this.needsReceipt = false;
|
||||||
this.message = Optional.absent();
|
this.message = Optional.absent();
|
||||||
this.synchronizeMessage = Optional.absent();
|
|
||||||
this.callMessage = Optional.absent();
|
|
||||||
this.nullMessage = Optional.absent();
|
|
||||||
this.readMessage = Optional.absent();
|
this.readMessage = Optional.absent();
|
||||||
this.typingMessage = Optional.of(typingMessage);
|
this.typingMessage = Optional.of(typingMessage);
|
||||||
}
|
}
|
||||||
@ -103,41 +64,17 @@ public class SignalServiceContent {
|
|||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.needsReceipt = false;
|
this.needsReceipt = false;
|
||||||
this.message = Optional.absent();
|
this.message = Optional.absent();
|
||||||
this.synchronizeMessage = Optional.absent();
|
|
||||||
this.callMessage = Optional.absent();
|
|
||||||
this.nullMessage = Optional.absent();
|
|
||||||
this.readMessage = Optional.absent();
|
this.readMessage = Optional.absent();
|
||||||
this.typingMessage = Optional.absent();
|
this.typingMessage = Optional.absent();
|
||||||
this.configurationMessageProto = Optional.fromNullable(configurationMessageProto);
|
this.configurationMessageProto = Optional.fromNullable(configurationMessageProto);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceContent(SignalServiceNullMessage nullMessage, String sender, int senderDevice, long timestamp) {
|
|
||||||
this.sender = sender;
|
|
||||||
this.senderDevice = senderDevice;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.needsReceipt = false;
|
|
||||||
this.message = Optional.absent();
|
|
||||||
this.synchronizeMessage = Optional.absent();
|
|
||||||
this.callMessage = Optional.absent();
|
|
||||||
this.nullMessage = Optional.of(nullMessage);
|
|
||||||
this.readMessage = Optional.absent();
|
|
||||||
this.typingMessage = Optional.absent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<SignalServiceDataMessage> getDataMessage() {
|
public Optional<SignalServiceDataMessage> getDataMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDataMessage(SignalServiceDataMessage message) { this.message = Optional.fromNullable(message); }
|
public void setDataMessage(SignalServiceDataMessage message) { this.message = Optional.fromNullable(message); }
|
||||||
|
|
||||||
public Optional<SignalServiceSyncMessage> getSyncMessage() { return synchronizeMessage; }
|
|
||||||
|
|
||||||
public void setSyncMessage(SignalServiceSyncMessage message) { this.synchronizeMessage = Optional.fromNullable(message); }
|
|
||||||
|
|
||||||
public Optional<SignalServiceCallMessage> getCallMessage() {
|
|
||||||
return callMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<SignalServiceReceiptMessage> getReceiptMessage() {
|
public Optional<SignalServiceReceiptMessage> getReceiptMessage() {
|
||||||
return readMessage;
|
return readMessage;
|
||||||
}
|
}
|
||||||
@ -162,12 +99,7 @@ public class SignalServiceContent {
|
|||||||
return needsReceipt;
|
return needsReceipt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<SignalServiceNullMessage> getNullMessage() { return nullMessage; }
|
|
||||||
|
|
||||||
// Loki
|
// Loki
|
||||||
|
|
||||||
public void setPreKeyBundleMessage(PreKeyBundleMessage preKeyBundleMessage) { this.preKeyBundleMessage = Optional.fromNullable(preKeyBundleMessage); }
|
|
||||||
|
|
||||||
public void setSenderDisplayName(String displayName) { senderDisplayName = Optional.fromNullable(displayName); }
|
public void setSenderDisplayName(String displayName) { senderDisplayName = Optional.fromNullable(displayName); }
|
||||||
|
|
||||||
public void setSenderProfilePictureURL(String url) { senderProfilePictureURL = Optional.fromNullable(url); }
|
public void setSenderProfilePictureURL(String url) { senderProfilePictureURL = Optional.fromNullable(url); }
|
||||||
|
@ -10,8 +10,7 @@ import org.session.libsignal.libsignal.state.PreKeyBundle;
|
|||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
import org.session.libsignal.libsignal.util.guava.Optional;
|
||||||
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
import org.session.libsignal.service.api.messages.shared.SharedContact;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateV2;
|
import org.session.libsignal.service.internal.push.SignalServiceProtos.DataMessage.ClosedGroupControlMessage;
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate;
|
|
||||||
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
|
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -26,18 +25,13 @@ public class SignalServiceDataMessage {
|
|||||||
private final Optional<String> body;
|
private final Optional<String> body;
|
||||||
public final Optional<SignalServiceGroup> group;
|
public final Optional<SignalServiceGroup> group;
|
||||||
private final Optional<byte[]> profileKey;
|
private final Optional<byte[]> profileKey;
|
||||||
private final boolean endSession;
|
|
||||||
private final boolean expirationUpdate;
|
private final boolean expirationUpdate;
|
||||||
private final int expiresInSeconds;
|
private final int expiresInSeconds;
|
||||||
private final boolean profileKeyUpdate;
|
|
||||||
private final Optional<Quote> quote;
|
private final Optional<Quote> quote;
|
||||||
public final Optional<List<SharedContact>> contacts;
|
public final Optional<List<SharedContact>> contacts;
|
||||||
private final Optional<List<Preview>> previews;
|
private final Optional<List<Preview>> previews;
|
||||||
private final Optional<Sticker> sticker;
|
|
||||||
// Loki
|
// Loki
|
||||||
private final Optional<PreKeyBundle> preKeyBundle;
|
private final Optional<ClosedGroupControlMessage> closedGroupControlMessage;
|
||||||
private final Optional<ClosedGroupUpdate> closedGroupUpdate;
|
|
||||||
private final Optional<ClosedGroupUpdateV2> closedGroupUpdateV2;
|
|
||||||
private final Optional<String> syncTarget;
|
private final Optional<String> syncTarget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,7 +106,7 @@ public class SignalServiceDataMessage {
|
|||||||
* @param expiresInSeconds The number of seconds in which a message should disappear after having been seen.
|
* @param expiresInSeconds The number of seconds in which a message should disappear after having been seen.
|
||||||
*/
|
*/
|
||||||
public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, List<SignalServiceAttachment> attachments, String body, int expiresInSeconds) {
|
public SignalServiceDataMessage(long timestamp, SignalServiceGroup group, List<SignalServiceAttachment> attachments, String body, int expiresInSeconds) {
|
||||||
this(timestamp, group, attachments, body, false, expiresInSeconds, false, null, false, null, null, null, null);
|
this(timestamp, group, attachments, body, expiresInSeconds, false, null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,17 +116,15 @@ public class SignalServiceDataMessage {
|
|||||||
* @param group The group information (or null if none).
|
* @param group The group information (or null if none).
|
||||||
* @param attachments The attachments (or null if none).
|
* @param attachments The attachments (or null if none).
|
||||||
* @param body The message contents.
|
* @param body The message contents.
|
||||||
* @param endSession Flag indicating whether this message should close a session.
|
|
||||||
* @param expiresInSeconds Number of seconds in which the message should disappear after being seen.
|
* @param expiresInSeconds Number of seconds in which the message should disappear after being seen.
|
||||||
*/
|
*/
|
||||||
public SignalServiceDataMessage(long timestamp, SignalServiceGroup group,
|
public SignalServiceDataMessage(long timestamp, SignalServiceGroup group,
|
||||||
List<SignalServiceAttachment> attachments,
|
List<SignalServiceAttachment> attachments,
|
||||||
String body, boolean endSession, int expiresInSeconds,
|
String body, int expiresInSeconds,
|
||||||
boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate,
|
boolean expirationUpdate, byte[] profileKey,
|
||||||
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
|
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews)
|
||||||
Sticker sticker)
|
|
||||||
{
|
{
|
||||||
this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, null);
|
this(timestamp, group, attachments, body, expiresInSeconds, expirationUpdate, profileKey, quote, sharedContacts, previews, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,32 +134,24 @@ public class SignalServiceDataMessage {
|
|||||||
* @param group The group information (or null if none).
|
* @param group The group information (or null if none).
|
||||||
* @param attachments The attachments (or null if none).
|
* @param attachments The attachments (or null if none).
|
||||||
* @param body The message contents.
|
* @param body The message contents.
|
||||||
* @param endSession Flag indicating whether this message should close a session.
|
|
||||||
* @param expiresInSeconds Number of seconds in which the message should disappear after being seen.
|
* @param expiresInSeconds Number of seconds in which the message should disappear after being seen.
|
||||||
* @param preKeyBundle The pre key bundle to attach to this message (or null if none).
|
|
||||||
*/
|
*/
|
||||||
public SignalServiceDataMessage(long timestamp, SignalServiceGroup group,
|
public SignalServiceDataMessage(long timestamp, SignalServiceGroup group,
|
||||||
List<SignalServiceAttachment> attachments,
|
List<SignalServiceAttachment> attachments,
|
||||||
String body, boolean endSession, int expiresInSeconds,
|
String body, int expiresInSeconds,
|
||||||
boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate,
|
boolean expirationUpdate, byte[] profileKey,
|
||||||
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
|
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
|
||||||
Sticker sticker, PreKeyBundle preKeyBundle,
|
ClosedGroupControlMessage closedGroupControlMessage,
|
||||||
ClosedGroupUpdate closedGroupUpdate, ClosedGroupUpdateV2 closedGroupUpdateV2,
|
|
||||||
String syncTarget)
|
String syncTarget)
|
||||||
{
|
{
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.body = Optional.fromNullable(body);
|
this.body = Optional.fromNullable(body);
|
||||||
this.group = Optional.fromNullable(group);
|
this.group = Optional.fromNullable(group);
|
||||||
this.endSession = endSession;
|
|
||||||
this.expiresInSeconds = expiresInSeconds;
|
this.expiresInSeconds = expiresInSeconds;
|
||||||
this.expirationUpdate = expirationUpdate;
|
this.expirationUpdate = expirationUpdate;
|
||||||
this.profileKey = Optional.fromNullable(profileKey);
|
this.profileKey = Optional.fromNullable(profileKey);
|
||||||
this.profileKeyUpdate = profileKeyUpdate;
|
|
||||||
this.quote = Optional.fromNullable(quote);
|
this.quote = Optional.fromNullable(quote);
|
||||||
this.sticker = Optional.fromNullable(sticker);
|
this.closedGroupControlMessage = Optional.fromNullable(closedGroupControlMessage);
|
||||||
this.preKeyBundle = Optional.fromNullable(preKeyBundle);
|
|
||||||
this.closedGroupUpdate = Optional.fromNullable(closedGroupUpdate);
|
|
||||||
this.closedGroupUpdateV2 = Optional.fromNullable(closedGroupUpdateV2);
|
|
||||||
this.syncTarget = Optional.fromNullable(syncTarget);
|
this.syncTarget = Optional.fromNullable(syncTarget);
|
||||||
|
|
||||||
if (attachments != null && !attachments.isEmpty()) {
|
if (attachments != null && !attachments.isEmpty()) {
|
||||||
@ -221,18 +205,10 @@ public class SignalServiceDataMessage {
|
|||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEndSession() {
|
|
||||||
return endSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExpirationUpdate() {
|
public boolean isExpirationUpdate() {
|
||||||
return expirationUpdate;
|
return expirationUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isProfileKeyUpdate() {
|
|
||||||
return profileKeyUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGroupMessage() {
|
public boolean isGroupMessage() {
|
||||||
return group.isPresent();
|
return group.isPresent();
|
||||||
}
|
}
|
||||||
@ -263,16 +239,8 @@ public class SignalServiceDataMessage {
|
|||||||
return previews;
|
return previews;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Sticker> getSticker() {
|
|
||||||
return sticker;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loki
|
// Loki
|
||||||
public Optional<ClosedGroupUpdate> getClosedGroupUpdate() { return closedGroupUpdate; }
|
public Optional<ClosedGroupControlMessage> getClosedGroupControlMessage() { return closedGroupControlMessage; }
|
||||||
|
|
||||||
public Optional<ClosedGroupUpdateV2> getClosedGroupUpdateV2() { return closedGroupUpdateV2; }
|
|
||||||
|
|
||||||
public Optional<PreKeyBundle> getPreKeyBundle() { return preKeyBundle; }
|
|
||||||
|
|
||||||
public boolean hasVisibleContent() {
|
public boolean hasVisibleContent() {
|
||||||
return (body.isPresent() && !body.get().isEmpty())
|
return (body.isPresent() && !body.get().isEmpty())
|
||||||
@ -399,11 +367,8 @@ public class SignalServiceDataMessage {
|
|||||||
public SignalServiceDataMessage build() {
|
public SignalServiceDataMessage build() {
|
||||||
if (timestamp == 0) timestamp = System.currentTimeMillis();
|
if (timestamp == 0) timestamp = System.currentTimeMillis();
|
||||||
// closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob)
|
// closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob)
|
||||||
return new SignalServiceDataMessage(timestamp, group, attachments, body, endSession,
|
return new SignalServiceDataMessage(timestamp, group, attachments, body, expiresInSeconds, expirationUpdate, profileKey, quote, sharedContacts, previews,
|
||||||
expiresInSeconds, expirationUpdate, profileKey,
|
null, syncTarget);
|
||||||
profileKeyUpdate, quote, sharedContacts, previews,
|
|
||||||
sticker, preKeyBundle, null, null,
|
|
||||||
syncTarget);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,19 +107,13 @@ public class SignalServiceEnvelope {
|
|||||||
}
|
}
|
||||||
builder.setTimestamp(proto.getTimestamp());
|
builder.setTimestamp(proto.getTimestamp());
|
||||||
builder.setServerTimestamp(proto.getServerTimestamp());
|
builder.setServerTimestamp(proto.getServerTimestamp());
|
||||||
if (proto.getServerGuid() != null) {
|
|
||||||
builder.setServerGuid(proto.getServerGuid());
|
|
||||||
}
|
|
||||||
if (proto.getLegacyMessage() != null) {
|
|
||||||
builder.setLegacyMessage(ByteString.copyFrom(proto.getLegacyMessage().toByteArray()));
|
|
||||||
}
|
|
||||||
if (proto.getContent() != null) {
|
if (proto.getContent() != null) {
|
||||||
builder.setContent(ByteString.copyFrom(proto.getContent().toByteArray()));
|
builder.setContent(ByteString.copyFrom(proto.getContent().toByteArray()));
|
||||||
}
|
}
|
||||||
this.envelope = builder.build();
|
this.envelope = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceEnvelope(int type, String sender, int senderDevice, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) {
|
public SignalServiceEnvelope(int type, String sender, int senderDevice, long timestamp, byte[] content, long serverTimestamp) {
|
||||||
Envelope.Builder builder = Envelope.newBuilder()
|
Envelope.Builder builder = Envelope.newBuilder()
|
||||||
.setType(Envelope.Type.valueOf(type))
|
.setType(Envelope.Type.valueOf(type))
|
||||||
.setSource(sender)
|
.setSource(sender)
|
||||||
@ -127,40 +121,22 @@ public class SignalServiceEnvelope {
|
|||||||
.setTimestamp(timestamp)
|
.setTimestamp(timestamp)
|
||||||
.setServerTimestamp(serverTimestamp);
|
.setServerTimestamp(serverTimestamp);
|
||||||
|
|
||||||
if (uuid != null) {
|
|
||||||
builder.setServerGuid(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
|
||||||
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
||||||
|
|
||||||
this.envelope = builder.build();
|
this.envelope = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SignalServiceEnvelope(int type, long timestamp, byte[] legacyMessage, byte[] content, long serverTimestamp, String uuid) {
|
public SignalServiceEnvelope(int type, long timestamp, byte[] content, long serverTimestamp) {
|
||||||
Envelope.Builder builder = Envelope.newBuilder()
|
Envelope.Builder builder = Envelope.newBuilder()
|
||||||
.setType(Envelope.Type.valueOf(type))
|
.setType(Envelope.Type.valueOf(type))
|
||||||
.setTimestamp(timestamp)
|
.setTimestamp(timestamp)
|
||||||
.setServerTimestamp(serverTimestamp);
|
.setServerTimestamp(serverTimestamp);
|
||||||
|
|
||||||
if (uuid != null) {
|
|
||||||
builder.setServerGuid(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (legacyMessage != null) builder.setLegacyMessage(ByteString.copyFrom(legacyMessage));
|
|
||||||
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
if (content != null) builder.setContent(ByteString.copyFrom(content));
|
||||||
|
|
||||||
this.envelope = builder.build();
|
this.envelope = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUuid() {
|
|
||||||
return envelope.getServerGuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasUuid() {
|
|
||||||
return envelope.hasServerGuid();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSource() {
|
public boolean hasSource() {
|
||||||
return envelope.hasSource() && envelope.getSource().length() > 0;
|
return envelope.hasSource() && envelope.getSource().length() > 0;
|
||||||
}
|
}
|
||||||
@ -208,20 +184,6 @@ public class SignalServiceEnvelope {
|
|||||||
return envelope.getServerTimestamp();
|
return envelope.getServerTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Whether the envelope contains a SignalServiceDataMessage
|
|
||||||
*/
|
|
||||||
public boolean hasLegacyMessage() {
|
|
||||||
return envelope.hasLegacyMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The envelope's containing SignalService message.
|
|
||||||
*/
|
|
||||||
public byte[] getLegacyMessage() {
|
|
||||||
return envelope.getLegacyMessage().toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Whether the envelope contains an encrypted SignalServiceContent
|
* @return Whether the envelope contains an encrypted SignalServiceContent
|
||||||
*/
|
*/
|
||||||
@ -236,35 +198,10 @@ public class SignalServiceEnvelope {
|
|||||||
return envelope.getContent().toByteArray();
|
return envelope.getContent().toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the containing message is a {@link org.session.libsignal.libsignal.protocol.SignalMessage}
|
|
||||||
*/
|
|
||||||
public boolean isSignalMessage() {
|
|
||||||
return envelope.getType().getNumber() == Envelope.Type.CIPHERTEXT_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the containing message is a {@link org.session.libsignal.libsignal.protocol.PreKeySignalMessage}
|
|
||||||
*/
|
|
||||||
public boolean isPreKeySignalMessage() {
|
|
||||||
return envelope.getType().getNumber() == Envelope.Type.PREKEY_BUNDLE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the containing message is a delivery receipt.
|
|
||||||
*/
|
|
||||||
public boolean isReceipt() {
|
|
||||||
return envelope.getType().getNumber() == Envelope.Type.RECEIPT_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUnidentifiedSender() {
|
public boolean isUnidentifiedSender() {
|
||||||
return envelope.getType().getNumber() == Envelope.Type.UNIDENTIFIED_SENDER_VALUE;
|
return envelope.getType().getNumber() == Envelope.Type.UNIDENTIFIED_SENDER_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFallbackMessage() {
|
|
||||||
return envelope.getType().getNumber() == Envelope.Type.FALLBACK_MESSAGE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isClosedGroupCiphertext() {
|
public boolean isClosedGroupCiphertext() {
|
||||||
return envelope.getType().getNumber() == Envelope.Type.CLOSED_GROUP_CIPHERTEXT_VALUE;
|
return envelope.getType().getNumber() == Envelope.Type.CLOSED_GROUP_CIPHERTEXT_VALUE;
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,10 @@ public class SignalServiceTypingMessage {
|
|||||||
|
|
||||||
private final Action action;
|
private final Action action;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
private final Optional<byte[]> groupId;
|
|
||||||
|
|
||||||
public SignalServiceTypingMessage(Action action, long timestamp, Optional<byte[]> groupId) {
|
public SignalServiceTypingMessage(Action action, long timestamp) {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.groupId = groupId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Action getAction() {
|
public Action getAction() {
|
||||||
@ -27,10 +25,6 @@ public class SignalServiceTypingMessage {
|
|||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<byte[]> getGroupId() {
|
|
||||||
return groupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTypingStarted() {
|
public boolean isTypingStarted() {
|
||||||
return action == Action.STARTED;
|
return action == Action.STARTED;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BlockedListMessage {
|
|
||||||
|
|
||||||
private final List<String> numbers;
|
|
||||||
private final List<byte[]> groupIds;
|
|
||||||
|
|
||||||
public BlockedListMessage(List<String> numbers, List<byte[]> groupIds) {
|
|
||||||
this.numbers = numbers;
|
|
||||||
this.groupIds = groupIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getNumbers() {
|
|
||||||
return numbers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<byte[]> getGroupIds() {
|
|
||||||
return groupIds;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.service.internal.util.Util;
|
|
||||||
|
|
||||||
import java.io.FilterInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public class ChunkedInputStream {
|
|
||||||
|
|
||||||
protected final InputStream in;
|
|
||||||
|
|
||||||
public ChunkedInputStream(InputStream in) {
|
|
||||||
this.in = in;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int readInt32() throws IOException {
|
|
||||||
try {
|
|
||||||
byte[] bytes = new byte[4];
|
|
||||||
Util.readFully(in, bytes);
|
|
||||||
return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
|
|
||||||
} catch (IndexOutOfBoundsException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int readRawVarint32() throws IOException {
|
|
||||||
byte tmp = (byte)in.read();
|
|
||||||
if (tmp >= 0) {
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
int result = tmp & 0x7f;
|
|
||||||
if ((tmp = (byte)in.read()) >= 0) {
|
|
||||||
result |= tmp << 7;
|
|
||||||
} else {
|
|
||||||
result |= (tmp & 0x7f) << 7;
|
|
||||||
if ((tmp = (byte)in.read()) >= 0) {
|
|
||||||
result |= tmp << 14;
|
|
||||||
} else {
|
|
||||||
result |= (tmp & 0x7f) << 14;
|
|
||||||
if ((tmp = (byte)in.read()) >= 0) {
|
|
||||||
result |= tmp << 21;
|
|
||||||
} else {
|
|
||||||
result |= (tmp & 0x7f) << 21;
|
|
||||||
result |= (tmp = (byte)in.read()) << 28;
|
|
||||||
if (tmp < 0) {
|
|
||||||
// Discard upper 32 bits.
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
if ((byte)in.read() >= 0) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IOException("Malformed varint!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final class LimitedInputStream extends FilterInputStream {
|
|
||||||
|
|
||||||
private long left;
|
|
||||||
private long mark = -1;
|
|
||||||
|
|
||||||
LimitedInputStream(InputStream in, long limit) {
|
|
||||||
super(in);
|
|
||||||
left = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int available() throws IOException {
|
|
||||||
return (int) Math.min(in.available(), left);
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's okay to mark even if mark isn't supported, as reset won't work
|
|
||||||
@Override public synchronized void mark(int readLimit) {
|
|
||||||
in.mark(readLimit);
|
|
||||||
mark = left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int read() throws IOException {
|
|
||||||
if (left == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = in.read();
|
|
||||||
if (result != -1) {
|
|
||||||
--left;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
if (left == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = (int) Math.min(len, left);
|
|
||||||
int result = in.read(b, off, len);
|
|
||||||
if (result != -1) {
|
|
||||||
left -= result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public synchronized void reset() throws IOException {
|
|
||||||
if (!in.markSupported()) {
|
|
||||||
throw new IOException("Mark not supported");
|
|
||||||
}
|
|
||||||
if (mark == -1) {
|
|
||||||
throw new IOException("Mark not set");
|
|
||||||
}
|
|
||||||
|
|
||||||
in.reset();
|
|
||||||
left = mark;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public long skip(long n) throws IOException {
|
|
||||||
n = Math.min(n, left);
|
|
||||||
long skipped = in.skip(n);
|
|
||||||
left -= skipped;
|
|
||||||
return skipped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class ChunkedOutputStream {
|
|
||||||
|
|
||||||
protected final OutputStream out;
|
|
||||||
|
|
||||||
public ChunkedOutputStream(OutputStream out) {
|
|
||||||
this.out = out;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeVarint32(int value) throws IOException {
|
|
||||||
while (true) {
|
|
||||||
if ((value & ~0x7F) == 0) {
|
|
||||||
out.write(value);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
out.write((value & 0x7F) | 0x80);
|
|
||||||
value >>>= 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeStream(InputStream in) throws IOException {
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
int read;
|
|
||||||
|
|
||||||
while ((read = in.read(buffer)) != -1) {
|
|
||||||
out.write(buffer, 0, read);
|
|
||||||
}
|
|
||||||
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected byte[] toByteArray(int value) {
|
|
||||||
return new byte[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value };
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
|
|
||||||
public class ConfigurationMessage {
|
|
||||||
|
|
||||||
private final Optional<Boolean> readReceipts;
|
|
||||||
private final Optional<Boolean> unidentifiedDeliveryIndicators;
|
|
||||||
private final Optional<Boolean> typingIndicators;
|
|
||||||
private final Optional<Boolean> linkPreviews;
|
|
||||||
|
|
||||||
public ConfigurationMessage(Optional<Boolean> readReceipts,
|
|
||||||
Optional<Boolean> unidentifiedDeliveryIndicators,
|
|
||||||
Optional<Boolean> typingIndicators,
|
|
||||||
Optional<Boolean> linkPreviews)
|
|
||||||
{
|
|
||||||
this.readReceipts = readReceipts;
|
|
||||||
this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators;
|
|
||||||
this.typingIndicators = typingIndicators;
|
|
||||||
this.linkPreviews = linkPreviews;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Boolean> getReadReceipts() {
|
|
||||||
return readReceipts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Boolean> getUnidentifiedDeliveryIndicators() {
|
|
||||||
return unidentifiedDeliveryIndicators;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Boolean> getTypingIndicators() {
|
|
||||||
return typingIndicators;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Boolean> getLinkPreviews() {
|
|
||||||
return linkPreviews;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
|
||||||
|
|
||||||
public class ContactsMessage {
|
|
||||||
|
|
||||||
private final SignalServiceAttachment contacts;
|
|
||||||
private final boolean complete;
|
|
||||||
|
|
||||||
public ContactsMessage(SignalServiceAttachment contacts, boolean complete) {
|
|
||||||
this.contacts = contacts;
|
|
||||||
this.complete = complete;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalServiceAttachment getContactsStream() {
|
|
||||||
return contacts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isComplete() {
|
|
||||||
return complete;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.VerifiedMessage;
|
|
||||||
|
|
||||||
public class DeviceContact {
|
|
||||||
|
|
||||||
private final String number;
|
|
||||||
private final Optional<String> name;
|
|
||||||
private final Optional<SignalServiceAttachmentStream> avatar;
|
|
||||||
private final Optional<String> color;
|
|
||||||
private final Optional<VerifiedMessage> verified;
|
|
||||||
private final Optional<byte[]> profileKey;
|
|
||||||
private final boolean blocked;
|
|
||||||
private final Optional<Integer> expirationTimer;
|
|
||||||
|
|
||||||
public DeviceContact(String number, Optional<String> name,
|
|
||||||
Optional<SignalServiceAttachmentStream> avatar,
|
|
||||||
Optional<String> color,
|
|
||||||
Optional<VerifiedMessage> verified,
|
|
||||||
Optional<byte[]> profileKey,
|
|
||||||
boolean blocked,
|
|
||||||
Optional<Integer> expirationTimer)
|
|
||||||
{
|
|
||||||
this.number = number;
|
|
||||||
this.name = name;
|
|
||||||
this.avatar = avatar;
|
|
||||||
this.color = color;
|
|
||||||
this.verified = verified;
|
|
||||||
this.profileKey = profileKey;
|
|
||||||
this.blocked = blocked;
|
|
||||||
this.expirationTimer = expirationTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<SignalServiceAttachmentStream> getAvatar() {
|
|
||||||
return avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<String> getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNumber() {
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<String> getColor() {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<VerifiedMessage> getVerified() {
|
|
||||||
return verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<byte[]> getProfileKey() {
|
|
||||||
return profileKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlocked() {
|
|
||||||
return blocked;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Integer> getExpirationTimer() {
|
|
||||||
return expirationTimer;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014-2018 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
import org.session.libsignal.libsignal.InvalidKeyException;
|
|
||||||
import org.session.libsignal.libsignal.InvalidMessageException;
|
|
||||||
import org.session.libsignal.utilities.logging.Log;
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
|
||||||
import org.session.libsignal.service.internal.util.Util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DeviceContactsInputStream extends ChunkedInputStream {
|
|
||||||
|
|
||||||
private static final String TAG = DeviceContactsInputStream.class.getSimpleName();
|
|
||||||
|
|
||||||
public DeviceContactsInputStream(InputStream in) {
|
|
||||||
super(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeviceContact read() throws Exception {
|
|
||||||
try {
|
|
||||||
long detailsLength = readInt32();
|
|
||||||
byte[] detailsSerialized = new byte[(int) detailsLength];
|
|
||||||
Util.readFully(in, detailsSerialized);
|
|
||||||
|
|
||||||
SignalServiceProtos.ContactDetails details = SignalServiceProtos.ContactDetails.parseFrom(detailsSerialized);
|
|
||||||
String number = details.getNumber();
|
|
||||||
Optional<String> name = Optional.fromNullable(details.getName());
|
|
||||||
Optional<SignalServiceAttachmentStream> avatar = Optional.absent();
|
|
||||||
Optional<String> color = details.hasColor() ? Optional.of(details.getColor()) : Optional.<String>absent();
|
|
||||||
Optional<VerifiedMessage> verified = Optional.absent();
|
|
||||||
Optional<byte[]> profileKey = Optional.absent();
|
|
||||||
boolean blocked = false;
|
|
||||||
Optional<Integer> expireTimer = Optional.absent();
|
|
||||||
|
|
||||||
if (details.hasAvatar()) {
|
|
||||||
long avatarLength = details.getAvatar().getLength();
|
|
||||||
InputStream avatarStream = new LimitedInputStream(in, avatarLength);
|
|
||||||
String avatarContentType = details.getAvatar().getContentType();
|
|
||||||
|
|
||||||
avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.<String>absent(), false, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (details.hasVerified()) {
|
|
||||||
try {
|
|
||||||
String destination = details.getVerified().getDestination();
|
|
||||||
IdentityKey identityKey = new IdentityKey(details.getVerified().getIdentityKey().toByteArray(), 0);
|
|
||||||
|
|
||||||
VerifiedMessage.VerifiedState state;
|
|
||||||
|
|
||||||
switch (details.getVerified().getState()) {
|
|
||||||
case VERIFIED:
|
|
||||||
state = VerifiedMessage.VerifiedState.VERIFIED;
|
|
||||||
break;
|
|
||||||
case UNVERIFIED:
|
|
||||||
state = VerifiedMessage.VerifiedState.UNVERIFIED;
|
|
||||||
break;
|
|
||||||
case DEFAULT:
|
|
||||||
state = VerifiedMessage.VerifiedState.DEFAULT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new InvalidMessageException("Unknown state: " + details.getVerified().getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
verified = Optional.of(new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis()));
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
verified = Optional.absent();
|
|
||||||
} catch (InvalidMessageException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
verified = Optional.absent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (details.hasProfileKey()) {
|
|
||||||
profileKey = Optional.fromNullable(details.getProfileKey().toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (details.hasExpireTimer() && details.getExpireTimer() > 0) {
|
|
||||||
expireTimer = Optional.of(details.getExpireTimer());
|
|
||||||
}
|
|
||||||
|
|
||||||
blocked = details.getBlocked();
|
|
||||||
|
|
||||||
return new DeviceContact(number, name, avatar, color, verified, profileKey, blocked, expireTimer);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read all device contacts.
|
|
||||||
*
|
|
||||||
* This will also close the input stream upon reading.
|
|
||||||
*/
|
|
||||||
public List<DeviceContact> readAll() throws Exception {
|
|
||||||
ArrayList<DeviceContact> devices = new ArrayList<DeviceContact>();
|
|
||||||
try {
|
|
||||||
DeviceContact deviceContact = read();
|
|
||||||
while (deviceContact != null) {
|
|
||||||
devices.add(deviceContact);
|
|
||||||
// Read the next contact
|
|
||||||
deviceContact = read();
|
|
||||||
}
|
|
||||||
return devices;
|
|
||||||
} finally {
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014-2018 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class DeviceContactsOutputStream extends ChunkedOutputStream {
|
|
||||||
|
|
||||||
public DeviceContactsOutputStream(OutputStream out) {
|
|
||||||
super(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(DeviceContact contact) throws IOException {
|
|
||||||
writeContactDetails(contact);
|
|
||||||
writeAvatarImage(contact);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws IOException {
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeAvatarImage(DeviceContact contact) throws IOException {
|
|
||||||
if (contact.getAvatar().isPresent()) {
|
|
||||||
writeStream(contact.getAvatar().get().getInputStream());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeContactDetails(DeviceContact contact) throws IOException {
|
|
||||||
SignalServiceProtos.ContactDetails.Builder contactDetails = SignalServiceProtos.ContactDetails.newBuilder();
|
|
||||||
contactDetails.setNumber(contact.getNumber());
|
|
||||||
|
|
||||||
if (contact.getName().isPresent()) {
|
|
||||||
contactDetails.setName(contact.getName().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contact.getAvatar().isPresent()) {
|
|
||||||
SignalServiceProtos.ContactDetails.Avatar.Builder avatarBuilder = SignalServiceProtos.ContactDetails.Avatar.newBuilder();
|
|
||||||
avatarBuilder.setContentType(contact.getAvatar().get().getContentType());
|
|
||||||
avatarBuilder.setLength((int)contact.getAvatar().get().getLength());
|
|
||||||
contactDetails.setAvatar(avatarBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contact.getColor().isPresent()) {
|
|
||||||
contactDetails.setColor(contact.getColor().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contact.getVerified().isPresent()) {
|
|
||||||
SignalServiceProtos.Verified.State state;
|
|
||||||
|
|
||||||
switch (contact.getVerified().get().getVerified()) {
|
|
||||||
case VERIFIED: state = SignalServiceProtos.Verified.State.VERIFIED; break;
|
|
||||||
case UNVERIFIED: state = SignalServiceProtos.Verified.State.UNVERIFIED; break;
|
|
||||||
default: state = SignalServiceProtos.Verified.State.DEFAULT; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
contactDetails.setVerified(SignalServiceProtos.Verified.newBuilder()
|
|
||||||
.setDestination(contact.getVerified().get().getDestination())
|
|
||||||
.setIdentityKey(ByteString.copyFrom(contact.getVerified().get().getIdentityKey().serialize()))
|
|
||||||
.setState(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contact.getProfileKey().isPresent()) {
|
|
||||||
contactDetails.setProfileKey(ByteString.copyFrom(contact.getProfileKey().get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contact.getExpirationTimer().isPresent()) {
|
|
||||||
contactDetails.setExpireTimer(contact.getExpirationTimer().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
contactDetails.setBlocked(contact.isBlocked());
|
|
||||||
|
|
||||||
byte[] serializedContactDetails = contactDetails.build().toByteArray();
|
|
||||||
|
|
||||||
// Loki - Since iOS has trouble parsing variable length integers, just write a fixed length one
|
|
||||||
out.write(toByteArray(serializedContactDetails.length));
|
|
||||||
out.write(serializedContactDetails);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DeviceGroup {
|
|
||||||
|
|
||||||
private final byte[] id;
|
|
||||||
private final Optional<String> name;
|
|
||||||
private final List<String> members;
|
|
||||||
private final List<String> admins;
|
|
||||||
private final Optional<SignalServiceAttachmentStream> avatar;
|
|
||||||
private final boolean active;
|
|
||||||
private final Optional<Integer> expirationTimer;
|
|
||||||
private final Optional<String> color;
|
|
||||||
private final boolean blocked;
|
|
||||||
|
|
||||||
public DeviceGroup(byte[] id, Optional<String> name, List<String> members,
|
|
||||||
List<String> admins,
|
|
||||||
Optional<SignalServiceAttachmentStream> avatar,
|
|
||||||
boolean active, Optional<Integer> expirationTimer,
|
|
||||||
Optional<String> color, boolean blocked)
|
|
||||||
{
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this.members = members;
|
|
||||||
this.admins = admins;
|
|
||||||
this.avatar = avatar;
|
|
||||||
this.active = active;
|
|
||||||
this.expirationTimer = expirationTimer;
|
|
||||||
this.color = color;
|
|
||||||
this.blocked = blocked;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<SignalServiceAttachmentStream> getAvatar() {
|
|
||||||
return avatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<String> getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getMembers() {
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getAdmins() { return admins; }
|
|
||||||
|
|
||||||
public boolean isActive() {
|
|
||||||
return active;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Integer> getExpirationTimer() {
|
|
||||||
return expirationTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<String> getColor() {
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlocked() {
|
|
||||||
return blocked;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014-2018 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachmentStream;
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
|
||||||
import org.session.libsignal.service.internal.util.Util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DeviceGroupsInputStream extends ChunkedInputStream{
|
|
||||||
|
|
||||||
public DeviceGroupsInputStream(InputStream in) {
|
|
||||||
super(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeviceGroup read() throws IOException {
|
|
||||||
try {
|
|
||||||
long detailsLength = readInt32();
|
|
||||||
byte[] detailsSerialized = new byte[(int) detailsLength];
|
|
||||||
Util.readFully(in, detailsSerialized);
|
|
||||||
|
|
||||||
SignalServiceProtos.GroupDetails details = SignalServiceProtos.GroupDetails.parseFrom(detailsSerialized);
|
|
||||||
|
|
||||||
if (!details.hasId()) {
|
|
||||||
throw new IOException("ID missing on group record!");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] id = details.getId().toByteArray();
|
|
||||||
Optional<String> name = Optional.fromNullable(details.getName());
|
|
||||||
List<String> members = details.getMembersList();
|
|
||||||
List<String> admins = details.getAdminsList();
|
|
||||||
Optional<SignalServiceAttachmentStream> avatar = Optional.absent();
|
|
||||||
boolean active = details.getActive();
|
|
||||||
Optional<Integer> expirationTimer = Optional.absent();
|
|
||||||
Optional<String> color = Optional.fromNullable(details.getColor());
|
|
||||||
boolean blocked = details.getBlocked();
|
|
||||||
|
|
||||||
if (details.hasAvatar()) {
|
|
||||||
long avatarLength = details.getAvatar().getLength();
|
|
||||||
InputStream avatarStream = new ChunkedInputStream.LimitedInputStream(in, avatarLength);
|
|
||||||
String avatarContentType = details.getAvatar().getContentType();
|
|
||||||
|
|
||||||
avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.<String>absent(), false, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (details.hasExpireTimer() && details.getExpireTimer() > 0) {
|
|
||||||
expirationTimer = Optional.of(details.getExpireTimer());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DeviceGroup(id, name, members, admins, avatar, active, expirationTimer, color, blocked);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read all device contacts.
|
|
||||||
*
|
|
||||||
* This will also close the input stream upon reading.
|
|
||||||
*/
|
|
||||||
public List<DeviceGroup> readAll() throws Exception {
|
|
||||||
ArrayList<DeviceGroup> devices = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
DeviceGroup deviceGroup = read();
|
|
||||||
while (deviceGroup != null) {
|
|
||||||
devices.add(deviceGroup);
|
|
||||||
// Read the next contact
|
|
||||||
deviceGroup = read();
|
|
||||||
}
|
|
||||||
return devices;
|
|
||||||
} finally {
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class DeviceGroupsOutputStream extends ChunkedOutputStream {
|
|
||||||
|
|
||||||
public DeviceGroupsOutputStream(OutputStream out) {
|
|
||||||
super(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(DeviceGroup group) throws IOException {
|
|
||||||
writeGroupDetails(group);
|
|
||||||
writeAvatarImage(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws IOException {
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeAvatarImage(DeviceGroup contact) throws IOException {
|
|
||||||
// Loki - Temporarily disable this
|
|
||||||
/*
|
|
||||||
if (contact.getAvatar().isPresent()) {
|
|
||||||
writeStream(contact.getAvatar().get().getInputStream());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeGroupDetails(DeviceGroup group) throws IOException {
|
|
||||||
SignalServiceProtos.GroupDetails.Builder groupDetails = SignalServiceProtos.GroupDetails.newBuilder();
|
|
||||||
groupDetails.setId(ByteString.copyFrom(group.getId()));
|
|
||||||
|
|
||||||
if (group.getName().isPresent()) {
|
|
||||||
groupDetails.setName(group.getName().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group.getAvatar().isPresent()) {
|
|
||||||
SignalServiceProtos.GroupDetails.Avatar.Builder avatarBuilder = SignalServiceProtos.GroupDetails.Avatar.newBuilder();
|
|
||||||
avatarBuilder.setContentType(group.getAvatar().get().getContentType());
|
|
||||||
avatarBuilder.setLength((int)group.getAvatar().get().getLength());
|
|
||||||
groupDetails.setAvatar(avatarBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group.getExpirationTimer().isPresent()) {
|
|
||||||
groupDetails.setExpireTimer(group.getExpirationTimer().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group.getColor().isPresent()) {
|
|
||||||
groupDetails.setColor(group.getColor().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
groupDetails.addAllMembers(group.getMembers());
|
|
||||||
groupDetails.addAllAdmins(group.getAdmins());
|
|
||||||
groupDetails.setActive(group.isActive());
|
|
||||||
groupDetails.setBlocked(group.isBlocked());
|
|
||||||
|
|
||||||
byte[] serializedContactDetails = groupDetails.build().toByteArray();
|
|
||||||
|
|
||||||
// Loki - Since iOS has trouble parsing variable length integers, just write a fixed length one
|
|
||||||
out.write(toByteArray(serializedContactDetails.length));
|
|
||||||
out.write(serializedContactDetails);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
public class DeviceInfo {
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private long id;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private long created;
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private long lastSeen;
|
|
||||||
|
|
||||||
public DeviceInfo() {}
|
|
||||||
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCreated() {
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastSeen() {
|
|
||||||
return lastSeen;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ReadMessage {
|
|
||||||
|
|
||||||
private final String sender;
|
|
||||||
private final long timestamp;
|
|
||||||
|
|
||||||
public ReadMessage(String sender, long timestamp) {
|
|
||||||
this.sender = sender;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSender() {
|
|
||||||
return sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.service.internal.push.SignalServiceProtos.SyncMessage.Request;
|
|
||||||
|
|
||||||
public class RequestMessage {
|
|
||||||
|
|
||||||
private final Request request;
|
|
||||||
|
|
||||||
public RequestMessage(Request request) {
|
|
||||||
this.request = request;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isContactsRequest() {
|
|
||||||
return request.getType() == Request.Type.CONTACTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGroupsRequest() {
|
|
||||||
return request.getType() == Request.Type.GROUPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlockedListRequest() {
|
|
||||||
return request.getType() == Request.Type.BLOCKED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConfigurationRequest() {
|
|
||||||
return request.getType() == Request.Type.CONFIGURATION;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class SentTranscriptMessage {
|
|
||||||
|
|
||||||
private final Optional<String> destination;
|
|
||||||
private final long timestamp;
|
|
||||||
private final long expirationStartTimestamp;
|
|
||||||
private final SignalServiceDataMessage message;
|
|
||||||
private final Map<String, Boolean> unidentifiedStatus;
|
|
||||||
|
|
||||||
// Loki - Open groups
|
|
||||||
public long messageServerID = -1;
|
|
||||||
|
|
||||||
public SentTranscriptMessage(String destination, long timestamp, SignalServiceDataMessage message,
|
|
||||||
long expirationStartTimestamp, Map<String, Boolean> unidentifiedStatus)
|
|
||||||
{
|
|
||||||
this.destination = Optional.of(destination);
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.message = message;
|
|
||||||
this.expirationStartTimestamp = expirationStartTimestamp;
|
|
||||||
this.unidentifiedStatus = new HashMap<String, Boolean>(unidentifiedStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SentTranscriptMessage(long timestamp, SignalServiceDataMessage message) {
|
|
||||||
this.destination = Optional.absent();
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.message = message;
|
|
||||||
this.expirationStartTimestamp = 0;
|
|
||||||
this.unidentifiedStatus = Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<String> getDestination() {
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getExpirationStartTimestamp() {
|
|
||||||
return expirationStartTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalServiceDataMessage getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUnidentified(String destination) {
|
|
||||||
if (unidentifiedStatus.containsKey(destination)) {
|
|
||||||
return unidentifiedStatus.get(destination);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,250 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2014-2016 Open Whisper Systems
|
|
||||||
*
|
|
||||||
* Licensed according to the LICENSE file in this repository.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment;
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.StickerPackOperationMessage;
|
|
||||||
import org.session.libsignal.service.loki.api.opengroups.PublicChat;
|
|
||||||
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SignalServiceSyncMessage {
|
|
||||||
|
|
||||||
private final Optional<SentTranscriptMessage> sent;
|
|
||||||
private final Optional<ContactsMessage> contacts;
|
|
||||||
private final Optional<SignalServiceAttachment> groups;
|
|
||||||
private final Optional<List<PublicChat>> openGroups;
|
|
||||||
private final Optional<BlockedListMessage> blockedList;
|
|
||||||
private final Optional<RequestMessage> request;
|
|
||||||
private final Optional<List<ReadMessage>> reads;
|
|
||||||
private final Optional<VerifiedMessage> verified;
|
|
||||||
private final Optional<ConfigurationMessage> configuration;
|
|
||||||
private final Optional<List<StickerPackOperationMessage>> stickerPackOperations;
|
|
||||||
|
|
||||||
private SignalServiceSyncMessage(Optional<SentTranscriptMessage> sent,
|
|
||||||
Optional<ContactsMessage> contacts,
|
|
||||||
Optional<SignalServiceAttachment> groups,
|
|
||||||
Optional<BlockedListMessage> blockedList,
|
|
||||||
Optional<RequestMessage> request,
|
|
||||||
Optional<List<ReadMessage>> reads,
|
|
||||||
Optional<VerifiedMessage> verified,
|
|
||||||
Optional<ConfigurationMessage> configuration,
|
|
||||||
Optional<List<StickerPackOperationMessage>> stickerPackOperations,
|
|
||||||
Optional<List<PublicChat>> openGroups)
|
|
||||||
{
|
|
||||||
this.sent = sent;
|
|
||||||
this.contacts = contacts;
|
|
||||||
this.groups = groups;
|
|
||||||
this.blockedList = blockedList;
|
|
||||||
this.request = request;
|
|
||||||
this.reads = reads;
|
|
||||||
this.verified = verified;
|
|
||||||
this.configuration = configuration;
|
|
||||||
this.stickerPackOperations = stickerPackOperations;
|
|
||||||
this.openGroups = openGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forSentTranscript(SentTranscriptMessage sent) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.of(sent),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forContacts(ContactsMessage contacts) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.of(contacts),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forGroups(SignalServiceAttachment groups) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.of(groups),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forRequest(RequestMessage request) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.of(request),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forRead(List<ReadMessage> reads) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.of(reads),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forRead(ReadMessage read) {
|
|
||||||
List<ReadMessage> reads = new LinkedList<ReadMessage>();
|
|
||||||
reads.add(read);
|
|
||||||
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.of(reads),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forVerified(VerifiedMessage verifiedMessage) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.of(verifiedMessage),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forBlocked(BlockedListMessage blocked) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.of(blocked),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forConfiguration(ConfigurationMessage configuration) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.of(configuration),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forStickerPackOperations(List<StickerPackOperationMessage> stickerPackOperations) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.of(stickerPackOperations),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage forOpenGroups(List<PublicChat> openGroups) {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.of(openGroups));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignalServiceSyncMessage empty() {
|
|
||||||
return new SignalServiceSyncMessage(Optional.<SentTranscriptMessage>absent(),
|
|
||||||
Optional.<ContactsMessage>absent(),
|
|
||||||
Optional.<SignalServiceAttachment>absent(),
|
|
||||||
Optional.<BlockedListMessage>absent(),
|
|
||||||
Optional.<RequestMessage>absent(),
|
|
||||||
Optional.<List<ReadMessage>>absent(),
|
|
||||||
Optional.<VerifiedMessage>absent(),
|
|
||||||
Optional.<ConfigurationMessage>absent(),
|
|
||||||
Optional.<List<StickerPackOperationMessage>>absent(),
|
|
||||||
Optional.<List<PublicChat>>absent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<SentTranscriptMessage> getSent() {
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<SignalServiceAttachment> getGroups() {
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<ContactsMessage> getContacts() {
|
|
||||||
return contacts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<RequestMessage> getRequest() {
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<List<ReadMessage>> getRead() {
|
|
||||||
return reads;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<BlockedListMessage> getBlockedList() {
|
|
||||||
return blockedList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<VerifiedMessage> getVerified() {
|
|
||||||
return verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<ConfigurationMessage> getConfiguration() {
|
|
||||||
return configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<List<StickerPackOperationMessage>> getStickerPackOperations() { return stickerPackOperations; }
|
|
||||||
|
|
||||||
public Optional<List<PublicChat>> getOpenGroups() { return openGroups; }
|
|
||||||
|
|
||||||
public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Sync); }
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.util.guava.Optional;
|
|
||||||
|
|
||||||
public class StickerPackOperationMessage {
|
|
||||||
|
|
||||||
private final Optional<byte[]> packId;
|
|
||||||
private final Optional<byte[]> packKey;
|
|
||||||
private final Optional<Type> type;
|
|
||||||
|
|
||||||
public StickerPackOperationMessage(byte[] packId, byte[] packKey, Type type) {
|
|
||||||
this.packId = Optional.fromNullable(packId);
|
|
||||||
this.packKey = Optional.fromNullable(packKey);
|
|
||||||
this.type = Optional.fromNullable(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<byte[]> getPackId() {
|
|
||||||
return packId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<byte[]> getPackKey() {
|
|
||||||
return packKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Type> getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
INSTALL, REMOVE
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package org.session.libsignal.service.api.messages.multidevice;
|
|
||||||
|
|
||||||
|
|
||||||
import org.session.libsignal.libsignal.IdentityKey;
|
|
||||||
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
|
|
||||||
|
|
||||||
public class VerifiedMessage {
|
|
||||||
|
|
||||||
public enum VerifiedState {
|
|
||||||
DEFAULT, VERIFIED, UNVERIFIED
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String destination;
|
|
||||||
private final IdentityKey identityKey;
|
|
||||||
private final VerifiedState verified;
|
|
||||||
private final long timestamp;
|
|
||||||
|
|
||||||
public VerifiedMessage(String destination, IdentityKey identityKey, VerifiedState verified, long timestamp) {
|
|
||||||
this.destination = destination;
|
|
||||||
this.identityKey = identityKey;
|
|
||||||
this.verified = verified;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDestination() {
|
|
||||||
return destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IdentityKey getIdentityKey() {
|
|
||||||
return identityKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VerifiedState getVerified() {
|
|
||||||
return verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTTL() { return TTLUtilities.getTTL(TTLUtilities.MessageType.Verified); }
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package org.session.libsignal.service.internal.push;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DeviceInfoList {
|
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private List<DeviceInfo> devices;
|
|
||||||
|
|
||||||
public DeviceInfoList() {}
|
|
||||||
|
|
||||||
public List<DeviceInfo> getDevices() {
|
|
||||||
return devices;
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,7 +20,6 @@ import org.session.libsignal.libsignal.util.guava.Optional;
|
|||||||
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
|
import org.session.libsignal.service.api.crypto.UnidentifiedAccess;
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceAttachment.ProgressListener;
|
import org.session.libsignal.service.api.messages.SignalServiceAttachment.ProgressListener;
|
||||||
import org.session.libsignal.service.api.messages.calls.TurnServerInfo;
|
import org.session.libsignal.service.api.messages.calls.TurnServerInfo;
|
||||||
import org.session.libsignal.service.api.messages.multidevice.DeviceInfo;
|
|
||||||
import org.session.libsignal.service.api.profiles.SignalServiceProfile;
|
import org.session.libsignal.service.api.profiles.SignalServiceProfile;
|
||||||
import org.session.libsignal.service.api.push.ContactTokenDetails;
|
import org.session.libsignal.service.api.push.ContactTokenDetails;
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
import org.session.libsignal.service.api.push.SignalServiceAddress;
|
||||||
@ -217,11 +216,6 @@ public class PushServiceSocket {
|
|||||||
return JsonUtil.fromJson(responseText, DeviceCode.class).getVerificationCode();
|
return JsonUtil.fromJson(responseText, DeviceCode.class).getVerificationCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DeviceInfo> getDevices() throws IOException {
|
|
||||||
String responseText = makeServiceRequest(String.format(DEVICE_PATH, ""), "GET", null);
|
|
||||||
return JsonUtil.fromJson(responseText, DeviceInfoList.class).getDevices();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeDevice(long deviceId) throws IOException {
|
public void removeDevice(long deviceId) throws IOException {
|
||||||
makeServiceRequest(String.format(DEVICE_PATH, String.valueOf(deviceId)), "DELETE", null);
|
makeServiceRequest(String.format(DEVICE_PATH, String.valueOf(deviceId)), "DELETE", null);
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,12 @@ public class SignalServiceEnvelopeEntity {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private int sourceDevice;
|
private int sourceDevice;
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private byte[] message;
|
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private byte[] content;
|
private byte[] content;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private long serverTimestamp;
|
private long serverTimestamp;
|
||||||
|
|
||||||
@JsonProperty
|
|
||||||
private String guid;
|
|
||||||
|
|
||||||
public SignalServiceEnvelopeEntity() {}
|
public SignalServiceEnvelopeEntity() {}
|
||||||
|
|
||||||
public int getType() {
|
public int getType() {
|
||||||
@ -53,10 +47,6 @@ public class SignalServiceEnvelopeEntity {
|
|||||||
return sourceDevice;
|
return sourceDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getContent() {
|
public byte[] getContent() {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
@ -64,8 +54,4 @@ public class SignalServiceEnvelopeEntity {
|
|||||||
public long getServerTimestamp() {
|
public long getServerTimestamp() {
|
||||||
return serverTimestamp;
|
return serverTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getServerUuid() {
|
|
||||||
return guid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,32 +0,0 @@
|
|||||||
package org.session.libsignal.service.loki.crypto
|
|
||||||
|
|
||||||
import org.session.libsignal.metadata.certificate.CertificateValidator
|
|
||||||
import org.session.libsignal.libsignal.InvalidMessageException
|
|
||||||
import org.session.libsignal.libsignal.loki.FallbackSessionCipher
|
|
||||||
import org.session.libsignal.libsignal.loki.SessionResetProtocol
|
|
||||||
import org.session.libsignal.libsignal.state.SignalProtocolStore
|
|
||||||
import org.session.libsignal.service.api.crypto.SignalServiceCipher
|
|
||||||
import org.session.libsignal.service.api.messages.SignalServiceEnvelope
|
|
||||||
import org.session.libsignal.service.api.push.SignalServiceAddress
|
|
||||||
import org.session.libsignal.service.internal.push.PushTransportDetails
|
|
||||||
import org.session.libsignal.service.loki.api.crypto.SessionProtocol
|
|
||||||
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
|
|
||||||
|
|
||||||
class LokiServiceCipher(localAddress: SignalServiceAddress, private val signalProtocolStore: SignalProtocolStore, sessionProtocolImpl: SessionProtocol, sessionResetProtocol: SessionResetProtocol, apiDB: LokiAPIDatabaseProtocol, certificateValidator: CertificateValidator?) : SignalServiceCipher(localAddress, signalProtocolStore, sessionResetProtocol, sessionProtocolImpl, apiDB, certificateValidator) {
|
|
||||||
|
|
||||||
|
|
||||||
private val userPrivateKey get() = signalProtocolStore.identityKeyPair.privateKey.serialize()
|
|
||||||
|
|
||||||
override fun decrypt(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext {
|
|
||||||
return if (envelope.isFallbackMessage) decryptFallbackMessage(envelope, ciphertext) else super.decrypt(envelope, ciphertext)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decryptFallbackMessage(envelope: SignalServiceEnvelope, ciphertext: ByteArray): Plaintext {
|
|
||||||
val cipher = FallbackSessionCipher(userPrivateKey, envelope.source)
|
|
||||||
val paddedMessageBody = cipher.decrypt(ciphertext) ?: throw InvalidMessageException("Failed to decrypt fallback message.")
|
|
||||||
val transportDetails = PushTransportDetails(FallbackSessionCipher.sessionVersion)
|
|
||||||
val unpaddedMessageBody = transportDetails.getStrippedPaddingMessageBody(paddedMessageBody)
|
|
||||||
val metadata = Metadata(envelope.source, envelope.sourceDevice, envelope.timestamp, false)
|
|
||||||
return Plaintext(metadata, unpaddedMessageBody)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user