diff --git a/js/background.js b/js/background.js index cfd4e1366..179affa15 100644 --- a/js/background.js +++ b/js/background.js @@ -1259,7 +1259,7 @@ async function initIncomingMessage(data, options = {}) { const { isError } = options; - const message = new Whisper.Message({ + let messageData = { source: data.source, sourceDevice: data.sourceDevice, sent_at: data.timestamp, @@ -1268,7 +1268,19 @@ unidentifiedDeliveryReceived: data.unidentifiedDeliveryReceived, type: 'incoming', unread: 1, - }); + }; + + if (data.type === 'friend-request') { + messageData = { + ...messageData, + type: 'friend-request', + friendStatus: 'pending', + preKeyBundle: data.preKeyBundle || null, + direction: 'incoming', + } + } + + const message = new Whisper.Message(messageData); // If we don't return early here, we can get into infinite error loops. So, no // delivery receipts for sealed sender errors. diff --git a/js/models/conversations.js b/js/models/conversations.js index ced2a574f..b4e56afac 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -144,10 +144,6 @@ this.on('expiration-change', this.updateAndMerge); this.on('expired', this.onExpired); - setTimeout(() => { - this.setFriendRequestTimer(); - }, 0); - const sealedSender = this.get('sealedSender'); if (sealedSender === undefined) { this.set({ sealedSender: SEALED_SENDER.UNKNOWN }); @@ -472,14 +468,23 @@ Conversation: Whisper.Conversation, }); }, + async waitingForFriendRequestApproval() { + // Check if we have an incoming friend request + // Or any successful outgoing ones + const incoming = await this.getPendingFriendRequests('incoming'); + const outgoing = await this.getPendingFriendRequests('outgoing'); + const successfulOutgoing = outgoing.filter(o => !o.hasErrors()); + + return (incoming.length > 0 || successfulOutgoing.length > 0); + }, async isFriend() { // We are a friend IF: // - We have the preKey bundle of the user OR // - We have a session with the user const preKeys = await window.Signal.Data.getContactPreKeyByIdentityKey(this.id); - const session = await window.Signal.Data.getSessionsByNumber(this.id); + // const session = await window.Signal.Data.getSessionsByNumber(this.id); - return !!(preKeys || session); + return !!preKeys; }, // Update any pending friend requests for the current user async updateFriendRequestUI() { @@ -1114,14 +1119,9 @@ // Check if we need to disable the text field const isFriend = await this.isFriend(); if (isFriend) { - // Check if we have an incoming friend request - // Or any successful outgoing ones - const incoming = await this.getPendingFriendRequests('incoming'); - const outgoing = await this.getPendingFriendRequests('outgoing'); - const successfulOutgoing = outgoing.filter(o => !o.hasErrors()); - - // Disable the input - if (incoming.length > 0 || successfulOutgoing.length > 0) { + // Disable the input if we're waiting for friend request approval + const waiting = await this.waitingForFriendRequestApproval(); + if (waiting) { this.trigger('disable:input', true); this.trigger('change:placeholder', 'disabled'); return; diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 35099e78b..5512d7d39 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -70,14 +70,10 @@ template: $('#conversation').html(), render_attributes() { let sendMessagePlaceholder = 'sendMessageFriendRequest'; - const sendDisabled = this.model.waitingForFriendRequestApproval(); - if (sendDisabled) { - sendMessagePlaceholder = 'sendMessageDisabled'; - } else if (this.model.getFriendRequestStatus() === null) { + if (this.model.isFriend()) { sendMessagePlaceholder = 'sendMessage'; } return { - 'disable-inputs': sendDisabled, 'send-message': i18n(sendMessagePlaceholder), 'android-length-warning': i18n('androidMessageLengthWarning'), }; @@ -240,6 +236,8 @@ this.$('.send-message').blur(this.unfocusBottomBar.bind(this)); this.$emojiPanelContainer = this.$('.emoji-panel-container'); + + this.model.updateFriendRequestUI(); }, events: { diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 9f015214f..1d3da3f16 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -929,7 +929,10 @@ MessageReceiver.prototype.extend({ }) ); }, - handleDataMessage(envelope, msg) { + async handleFriendRequestMessage(envelope, msg) { + return this.handleDataMessage(envelope, msg, 'friend-request'); + }, + handleDataMessage(envelope, msg, type = 'data') { window.log.info('data message from', this.getEnvelopeId(envelope)); let p = Promise.resolve(); // eslint-disable-next-line no-bitwise @@ -946,6 +949,13 @@ MessageReceiver.prototype.extend({ message.group.type === textsecure.protobuf.GroupContext.Type.QUIT ); + if (type === 'friend-request' && isMe) { + window.log.info( + 'refusing to add a friend request to ourselves' + ); + throw new Error('Cannot add a friend request for ourselves!') + } + if (groupId && isBlocked && !(isMe && isLeavingGroup)) { window.log.warn( `Message ${this.getEnvelopeId( @@ -955,15 +965,19 @@ MessageReceiver.prototype.extend({ return this.removeFromCache(envelope); } + const preKeyBundle = envelope.preKeyBundleMessage && this.decodePreKeyBundleMessage(envelope.preKeyBundleMessage); + const ev = new Event('message'); ev.confirm = this.removeFromCache.bind(this, envelope); ev.data = { + type, source: envelope.source, sourceDevice: envelope.sourceDevice, timestamp: envelope.timestamp.toNumber(), receivedAt: envelope.receivedAt, unidentifiedDeliveryReceived: envelope.unidentifiedDeliveryReceived, message, + preKeyBundle: preKeyBundle || null, }; return this.dispatchAndWait(ev); }) @@ -1046,17 +1060,7 @@ MessageReceiver.prototype.extend({ } if (envelope.type === textsecure.protobuf.Envelope.Type.FRIEND_REQUEST) { - // only prompt friend request if there is no conversation yet - if (!conversation) { - this.promptUserToAcceptFriendRequest( - envelope, - content.dataMessage.body, - envelope.preKeyBundleMessage - ); - } - - // Exit early since the friend request reply will be a regular empty message - return null; + return this.handleFriendRequestMessage(envelope, content.dataMessage); } else if (envelope.type === textsecure.protobuf.Envelope.Type.CIPHERTEXT) { // If we get a cipher text and we are friends then we can mark keys as exchanged if (conversation && conversation.isFriend()) {