Make sure XEP-0363 urls are also OMEMO encrypted
by re-using `ChatBox.prototype.sendMessage`. updates #1182
This commit is contained in:
parent
3d8852950d
commit
01efb02f9e
@ -85,6 +85,7 @@ module.exports = function(config) {
|
|||||||
{ pattern: "src/plugins/muc-views/tests/unfurls.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/unfurls.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/muc-views/tests/xss.js", type: 'module' },
|
{ pattern: "src/plugins/muc-views/tests/xss.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/notifications/tests/notification.js", type: 'module' },
|
{ pattern: "src/plugins/notifications/tests/notification.js", type: 'module' },
|
||||||
|
{ pattern: "src/plugins/omemo/tests/media-sharing.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/omemo/tests/omemo.js", type: 'module' },
|
{ pattern: "src/plugins/omemo/tests/omemo.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
{ pattern: "src/plugins/register/tests/register.js", type: 'module' },
|
||||||
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
{ pattern: "src/plugins/rootview/tests/root.js", type: 'module' },
|
||||||
|
65
spec/mock.js
65
spec/mock.js
@ -672,3 +672,68 @@ const initConverse = async (settings) => {
|
|||||||
window.converse_disable_effects = true;
|
window.converse_disable_effects = true;
|
||||||
return _converse;
|
return _converse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mock.deviceListFetched = async function deviceListFetched (_converse, jid) {
|
||||||
|
const selector = `iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.devicelist"]`;
|
||||||
|
const stanza = await u.waitUntil(
|
||||||
|
() => Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop()
|
||||||
|
);
|
||||||
|
await u.waitUntil(() => _converse.devicelists.get(jid));
|
||||||
|
return stanza;
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.ownDeviceHasBeenPublished = function ownDeviceHasBeenPublished (_converse) {
|
||||||
|
return Array.from(_converse.connection.IQ_stanzas).filter(
|
||||||
|
iq => iq.querySelector('iq[from="'+_converse.bare_jid+'"] publish[node="eu.siacs.conversations.axolotl.devicelist"]')
|
||||||
|
).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.bundleHasBeenPublished = function bundleHasBeenPublished (_converse) {
|
||||||
|
const selector = 'publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]';
|
||||||
|
return Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.bundleFetched = function bundleFetched (_converse, jid, device_id) {
|
||||||
|
return Array.from(_converse.connection.IQ_stanzas).filter(
|
||||||
|
iq => iq.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.bundles:${device_id}"]`)
|
||||||
|
).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.initializedOMEMO = async function initializedOMEMO (_converse) {
|
||||||
|
await mock.waitUntilDiscoConfirmed(
|
||||||
|
_converse, _converse.bare_jid,
|
||||||
|
[{'category': 'pubsub', 'type': 'pep'}],
|
||||||
|
['http://jabber.org/protocol/pubsub#publish-options']
|
||||||
|
);
|
||||||
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, _converse.bare_jid));
|
||||||
|
let stanza = $iq({
|
||||||
|
'from': _converse.bare_jid,
|
||||||
|
'id': iq_stanza.getAttribute('id'),
|
||||||
|
'to': _converse.bare_jid,
|
||||||
|
'type': 'result',
|
||||||
|
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||||
|
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||||
|
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||||
|
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||||
|
.c('device', {'id': '482886413b977930064a5888b92134fe'});
|
||||||
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
iq_stanza = await u.waitUntil(() => mock.ownDeviceHasBeenPublished(_converse))
|
||||||
|
|
||||||
|
stanza = $iq({
|
||||||
|
'from': _converse.bare_jid,
|
||||||
|
'id': iq_stanza.getAttribute('id'),
|
||||||
|
'to': _converse.bare_jid,
|
||||||
|
'type': 'result'});
|
||||||
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
|
iq_stanza = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse))
|
||||||
|
|
||||||
|
stanza = $iq({
|
||||||
|
'from': _converse.bare_jid,
|
||||||
|
'id': iq_stanza.getAttribute('id'),
|
||||||
|
'to': _converse.bare_jid,
|
||||||
|
'type': 'result'});
|
||||||
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
await _converse.api.waitUntil('OMEMOInitialized');
|
||||||
|
}
|
||||||
|
@ -241,9 +241,16 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onMessageUploadChanged (message) {
|
async onMessageUploadChanged (message) {
|
||||||
if (message.get('upload') === _converse.SUCCESS) {
|
if (message.get('upload') === _converse.SUCCESS) {
|
||||||
api.send(this.createMessageStanza(message));
|
const attrs = {
|
||||||
|
'body': message.get('message'),
|
||||||
|
'spoiler_hint': message.get('spoiler_hint'),
|
||||||
|
'oob_url': message.get('oob_url')
|
||||||
|
|
||||||
|
}
|
||||||
|
await this.sendMessage(attrs);
|
||||||
|
message.destroy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -842,11 +849,12 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
return stanza;
|
return stanza;
|
||||||
},
|
},
|
||||||
|
|
||||||
getOutgoingMessageAttributes (text, spoiler_hint) {
|
getOutgoingMessageAttributes (attrs) {
|
||||||
const is_spoiler = this.get('composing_spoiler');
|
const is_spoiler = !!this.get('composing_spoiler');
|
||||||
const origin_id = u.getUniqueId();
|
const origin_id = u.getUniqueId();
|
||||||
|
const text = attrs?.body;
|
||||||
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
||||||
return {
|
return Object.assign({}, attrs, {
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'fullname': _converse.xmppstatus.get('fullname'),
|
'fullname': _converse.xmppstatus.get('fullname'),
|
||||||
'id': origin_id,
|
'id': origin_id,
|
||||||
@ -856,13 +864,12 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
'msgid': origin_id,
|
'msgid': origin_id,
|
||||||
'nickname': this.get('nickname'),
|
'nickname': this.get('nickname'),
|
||||||
'sender': 'me',
|
'sender': 'me',
|
||||||
'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
|
|
||||||
'time': (new Date()).toISOString(),
|
'time': (new Date()).toISOString(),
|
||||||
'type': this.get('message_type'),
|
'type': this.get('message_type'),
|
||||||
body,
|
body,
|
||||||
is_spoiler,
|
is_spoiler,
|
||||||
origin_id
|
origin_id
|
||||||
}
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -911,15 +918,14 @@ const ChatBox = ModelWithContact.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @method _converse.ChatBox#sendMessage
|
* @method _converse.ChatBox#sendMessage
|
||||||
* @memberOf _converse.ChatBox
|
* @memberOf _converse.ChatBox
|
||||||
* @param { String } text - The chat message text
|
* @param { Object } [attrs] - A map of attributes to be saved on the message
|
||||||
* @param { String } spoiler_hint - An optional hint, if the message being sent is a spoiler
|
|
||||||
* @returns { _converse.Message }
|
* @returns { _converse.Message }
|
||||||
* @example
|
* @example
|
||||||
* const chat = api.chats.get('buddy1@example.com');
|
* const chat = api.chats.get('buddy1@example.org');
|
||||||
* chat.sendMessage('hello world');
|
* chat.sendMessage({'body': 'hello world'});
|
||||||
*/
|
*/
|
||||||
async sendMessage (text, spoiler_hint) {
|
async sendMessage (attrs) {
|
||||||
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
|
attrs = this.getOutgoingMessageAttributes(attrs);
|
||||||
let message = this.messages.findWhere('correcting')
|
let message = this.messages.findWhere('correcting')
|
||||||
if (message) {
|
if (message) {
|
||||||
const older_versions = message.get('older_versions') || {};
|
const older_versions = message.get('older_versions') || {};
|
||||||
|
@ -959,12 +959,15 @@ const ChatRoomMixin = {
|
|||||||
return [updated_message, updated_references];
|
return [updated_message, updated_references];
|
||||||
},
|
},
|
||||||
|
|
||||||
getOutgoingMessageAttributes (original_message, spoiler_hint) {
|
getOutgoingMessageAttributes (attrs) {
|
||||||
const is_spoiler = this.get('composing_spoiler');
|
const is_spoiler = this.get('composing_spoiler');
|
||||||
const [text, references] = this.parseTextForReferences(original_message);
|
let text = '', references;
|
||||||
|
if (attrs?.body) {
|
||||||
|
[text, references] = this.parseTextForReferences(attrs.body);
|
||||||
|
}
|
||||||
const origin_id = u.getUniqueId();
|
const origin_id = u.getUniqueId();
|
||||||
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
const body = text ? u.httpToGeoUri(u.shortnamesToUnicode(text), _converse) : undefined;
|
||||||
return {
|
return Object.assign({}, attrs, {
|
||||||
body,
|
body,
|
||||||
is_spoiler,
|
is_spoiler,
|
||||||
origin_id,
|
origin_id,
|
||||||
@ -977,9 +980,8 @@ const ChatRoomMixin = {
|
|||||||
'message': body,
|
'message': body,
|
||||||
'nick': this.get('nick'),
|
'nick': this.get('nick'),
|
||||||
'sender': 'me',
|
'sender': 'me',
|
||||||
'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
|
|
||||||
'type': 'groupchat'
|
'type': 'groupchat'
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,20 +14,20 @@ describe("A Groupchat Message", function () {
|
|||||||
const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||||
expect(model.ui.get('scrolled')).toBeFalsy();
|
expect(model.ui.get('scrolled')).toBeFalsy();
|
||||||
|
|
||||||
model.sendMessage('1st message');
|
model.sendMessage({'body': '1st message'});
|
||||||
model.sendMessage('2nd message');
|
model.sendMessage({'body': '2nd message'});
|
||||||
model.sendMessage('3rd message');
|
model.sendMessage({'body': '3rd message'});
|
||||||
await u.waitUntil(() => model.messages.length === 3);
|
await u.waitUntil(() => model.messages.length === 3);
|
||||||
// Make sure pruneHistory fires
|
// Make sure pruneHistory fires
|
||||||
await new Promise(resolve => setTimeout(resolve, 550));
|
await new Promise(resolve => setTimeout(resolve, 550));
|
||||||
|
|
||||||
model.sendMessage('4th message');
|
model.sendMessage({'body': '4th message'});
|
||||||
await u.waitUntil(() => model.messages.length === 4);
|
await u.waitUntil(() => model.messages.length === 4);
|
||||||
await u.waitUntil(() => model.messages.length === 3, 550);
|
await u.waitUntil(() => model.messages.length === 3, 550);
|
||||||
|
|
||||||
model.ui.set('scrolled', true);
|
model.ui.set('scrolled', true);
|
||||||
model.sendMessage('5th message');
|
model.sendMessage({'body': '5th message'});
|
||||||
model.sendMessage('6th message');
|
model.sendMessage({'body': '6th message'});
|
||||||
await u.waitUntil(() => model.messages.length === 5);
|
await u.waitUntil(() => model.messages.length === 5);
|
||||||
|
|
||||||
// Wait long enough to be sure the debounced pruneHistory method didn't fire.
|
// Wait long enough to be sure the debounced pruneHistory method didn't fire.
|
||||||
|
@ -195,7 +195,7 @@ export default class MessageForm extends ElementView {
|
|||||||
this.querySelector('converse-emoji-dropdown')?.hideMenu();
|
this.querySelector('converse-emoji-dropdown')?.hideMenu();
|
||||||
|
|
||||||
const is_command = this.parseMessageForCommands(message_text);
|
const is_command = this.parseMessageForCommands(message_text);
|
||||||
const message = is_command ? null : await this.model.sendMessage(message_text, spoiler_hint);
|
const message = is_command ? null : await this.model.sendMessage({'body': message_text, spoiler_hint});
|
||||||
if (is_command || message) {
|
if (is_command || message) {
|
||||||
hint_el.value = '';
|
hint_el.value = '';
|
||||||
textarea.value = '';
|
textarea.value = '';
|
||||||
|
@ -1030,7 +1030,7 @@ describe("A Chat Message", function () {
|
|||||||
await _converse.api.chats.open(sender_jid)
|
await _converse.api.chats.open(sender_jid)
|
||||||
let msg_text = 'This message will not be sent, due to an error';
|
let msg_text = 'This message will not be sent, due to an error';
|
||||||
const view = _converse.chatboxviews.get(sender_jid);
|
const view = _converse.chatboxviews.get(sender_jid);
|
||||||
const message = await view.model.sendMessage(msg_text);
|
const message = await view.model.sendMessage({'body': msg_text});
|
||||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length);
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length);
|
||||||
let msg_txt = sizzle('.chat-msg:last .chat-msg__text', view).pop().textContent;
|
let msg_txt = sizzle('.chat-msg:last .chat-msg__text', view).pop().textContent;
|
||||||
expect(msg_txt).toEqual(msg_text);
|
expect(msg_txt).toEqual(msg_text);
|
||||||
@ -1039,7 +1039,7 @@ describe("A Chat Message", function () {
|
|||||||
// not be received, to test that errors appear
|
// not be received, to test that errors appear
|
||||||
// after the relevant message.
|
// after the relevant message.
|
||||||
msg_text = 'This message will be sent, and also receive an error';
|
msg_text = 'This message will be sent, and also receive an error';
|
||||||
const second_message = await view.model.sendMessage(msg_text);
|
const second_message = await view.model.sendMessage({'body': msg_text});
|
||||||
await u.waitUntil(() => sizzle('.chat-msg .chat-msg__text', view).length === 2, 1000);
|
await u.waitUntil(() => sizzle('.chat-msg .chat-msg__text', view).length === 2, 1000);
|
||||||
msg_txt = sizzle('.chat-msg:last .chat-msg__text', view).pop().textContent;
|
msg_txt = sizzle('.chat-msg:last .chat-msg__text', view).pop().textContent;
|
||||||
expect(msg_txt).toEqual(msg_text);
|
expect(msg_txt).toEqual(msg_text);
|
||||||
@ -1098,7 +1098,7 @@ describe("A Chat Message", function () {
|
|||||||
expect(view.querySelectorAll('.chat-msg__error').length).toEqual(2);
|
expect(view.querySelectorAll('.chat-msg__error').length).toEqual(2);
|
||||||
|
|
||||||
msg_text = 'This message will be sent, and also receive an error';
|
msg_text = 'This message will be sent, and also receive an error';
|
||||||
const third_message = await view.model.sendMessage(msg_text);
|
const third_message = await view.model.sendMessage({'body': msg_text});
|
||||||
await u.waitUntil(() => sizzle('converse-chat-message:last-child .chat-msg__text', view).pop()?.textContent === msg_text);
|
await u.waitUntil(() => sizzle('converse-chat-message:last-child .chat-msg__text', view).pop()?.textContent === msg_text);
|
||||||
|
|
||||||
// A different error message will however render
|
// A different error message will however render
|
||||||
@ -1157,7 +1157,7 @@ describe("A Chat Message", function () {
|
|||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
const view = _converse.chatboxviews.get(contact_jid);
|
const view = _converse.chatboxviews.get(contact_jid);
|
||||||
const msg_text = 'This message will show!';
|
const msg_text = 'This message will show!';
|
||||||
await view.model.sendMessage(msg_text);
|
await view.model.sendMessage({'body': msg_text});
|
||||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
|
||||||
expect(view.querySelectorAll('.chat-error').length).toEqual(0);
|
expect(view.querySelectorAll('.chat-error').length).toEqual(0);
|
||||||
}));
|
}));
|
||||||
|
@ -53,7 +53,7 @@ describe("The <converse-muc> component", function () {
|
|||||||
await mock.returnMemberLists(_converse, muc_jid, [], all_affiliations);
|
await mock.returnMemberLists(_converse, muc_jid, [], all_affiliations);
|
||||||
await model.messages.fetched;
|
await model.messages.fetched;
|
||||||
|
|
||||||
model.sendMessage('hello from the lounge!');
|
model.sendMessage({'body': 'hello from the lounge!'});
|
||||||
|
|
||||||
const span_el = document.createElement('span');
|
const span_el = document.createElement('span');
|
||||||
span_el.classList.add('conversejs');
|
span_el.classList.add('conversejs');
|
||||||
@ -83,7 +83,7 @@ describe("The <converse-muc> component", function () {
|
|||||||
await mock.returnMemberLists(_converse, muc2_jid, [], all_affiliations);
|
await mock.returnMemberLists(_converse, muc2_jid, [], all_affiliations);
|
||||||
await model.messages.fetched;
|
await model.messages.fetched;
|
||||||
|
|
||||||
model2.sendMessage('hello from the bar!');
|
model2.sendMessage({'body': 'hello from the bar!'});
|
||||||
muc_el.setAttribute('jid', muc2_jid);
|
muc_el.setAttribute('jid', muc2_jid);
|
||||||
|
|
||||||
await u.waitUntil(() => muc_el.querySelector('converse-chat-message-body').textContent.trim() === 'hello from the bar!');
|
await u.waitUntil(() => muc_el.querySelector('converse-chat-message-body').textContent.trim() === 'hello from the bar!');
|
||||||
|
@ -411,7 +411,7 @@ describe("A Groupchat Message", function () {
|
|||||||
.c('status').attrs({code:'210'}).nodeTree;
|
.c('status').attrs({code:'210'}).nodeTree;
|
||||||
_converse.connection._dataRecv(mock.createRequest(presence));
|
_converse.connection._dataRecv(mock.createRequest(presence));
|
||||||
|
|
||||||
view.model.sendMessage('hello world');
|
view.model.sendMessage({'body': 'hello world'});
|
||||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 3);
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 3);
|
||||||
|
|
||||||
const occupant = await u.waitUntil(() => view.model.messages.filter(m => m.get('type') === 'groupchat')[2].occupant);
|
const occupant = await u.waitUntil(() => view.model.messages.filter(m => m.get('type') === 'groupchat')[2].occupant);
|
||||||
@ -542,7 +542,7 @@ describe("A Groupchat Message", function () {
|
|||||||
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
|
||||||
const view = _converse.chatboxviews.get(muc_jid);
|
const view = _converse.chatboxviews.get(muc_jid);
|
||||||
|
|
||||||
view.model.sendMessage('hello world');
|
view.model.sendMessage({'body': 'hello world'});
|
||||||
await u.waitUntil(() => view.model.messages.length === 1);
|
await u.waitUntil(() => view.model.messages.length === 1);
|
||||||
const msg = view.model.messages.at(0);
|
const msg = view.model.messages.at(0);
|
||||||
expect(msg.get('stanza_id')).toBeUndefined();
|
expect(msg.get('stanza_id')).toBeUndefined();
|
||||||
|
@ -5,7 +5,7 @@ const u = converse.env.utils;
|
|||||||
|
|
||||||
|
|
||||||
async function sendAndThenRetractMessage (_converse, view) {
|
async function sendAndThenRetractMessage (_converse, view) {
|
||||||
view.model.sendMessage('hello world');
|
view.model.sendMessage({'body': 'hello world'});
|
||||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 1);
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 1);
|
||||||
const msg_obj = view.model.messages.last();
|
const msg_obj = view.model.messages.last();
|
||||||
const reflection_stanza = u.toStanza(`
|
const reflection_stanza = u.toStanza(`
|
||||||
@ -313,7 +313,7 @@ describe("Message Retractions", function () {
|
|||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
const view = await mock.openChatBoxFor(_converse, contact_jid);
|
const view = await mock.openChatBoxFor(_converse, contact_jid);
|
||||||
|
|
||||||
view.model.sendMessage('hello world');
|
view.model.sendMessage({'body': 'hello world'});
|
||||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
|
||||||
|
|
||||||
const message = view.model.messages.at(0);
|
const message = view.model.messages.at(0);
|
||||||
@ -709,7 +709,7 @@ describe("Message Retractions", function () {
|
|||||||
const occupant = view.model.getOwnOccupant();
|
const occupant = view.model.getOwnOccupant();
|
||||||
expect(occupant.get('role')).toBe('moderator');
|
expect(occupant.get('role')).toBe('moderator');
|
||||||
|
|
||||||
view.model.sendMessage('Visit this site to get free bitcoin');
|
view.model.sendMessage({'body': 'Visit this site to get free bitcoin'});
|
||||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
|
||||||
const stanza_id = 'retraction-id-1';
|
const stanza_id = 'retraction-id-1';
|
||||||
const msg_obj = view.model.messages.at(0);
|
const msg_obj = view.model.messages.at(0);
|
||||||
@ -758,7 +758,7 @@ describe("Message Retractions", function () {
|
|||||||
const occupant = view.model.getOwnOccupant();
|
const occupant = view.model.getOwnOccupant();
|
||||||
expect(occupant.get('role')).toBe('moderator');
|
expect(occupant.get('role')).toBe('moderator');
|
||||||
|
|
||||||
view.model.sendMessage('Visit this site to get free bitcoin');
|
view.model.sendMessage({'body': 'Visit this site to get free bitcoin'});
|
||||||
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
|
||||||
const stanza_id = 'retraction-id-1';
|
const stanza_id = 'retraction-id-1';
|
||||||
const msg_obj = view.model.messages.at(0);
|
const msg_obj = view.model.messages.at(0);
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
export const UNDECIDED = 0;
|
export const UNDECIDED = 0;
|
||||||
export const TRUSTED = 1;
|
export const TRUSTED = 1;
|
||||||
export const UNTRUSTED = -1;
|
export const UNTRUSTED = -1;
|
||||||
|
|
||||||
|
export const TAG_LENGTH = 128;
|
||||||
|
|
||||||
|
export const KEY_ALGO = {
|
||||||
|
'name': 'AES-GCM',
|
||||||
|
'length': 128
|
||||||
|
};
|
||||||
|
@ -1,16 +1,5 @@
|
|||||||
import concat from 'lodash-es/concat';
|
import { generateFingerprint, getDevicesForContact, } from '../utils.js';
|
||||||
import { UNTRUSTED } from '../consts.js';
|
|
||||||
import { __ } from 'i18n';
|
|
||||||
import { _converse, converse } from '@converse/headless/core';
|
|
||||||
import {
|
|
||||||
addKeysToMessageStanza,
|
|
||||||
generateFingerprint,
|
|
||||||
getDevicesForContact,
|
|
||||||
getSession,
|
|
||||||
omemo,
|
|
||||||
} from '../utils.js';
|
|
||||||
|
|
||||||
const { Strophe, $msg } = converse.env;
|
|
||||||
|
|
||||||
const ConverseMixins = {
|
const ConverseMixins = {
|
||||||
|
|
||||||
@ -27,92 +16,6 @@ const ConverseMixins = {
|
|||||||
/* Checks whether the contact advertises any OMEMO-compatible devices. */
|
/* Checks whether the contact advertises any OMEMO-compatible devices. */
|
||||||
const devices = await getDevicesForContact(jid);
|
const devices = await getDevicesForContact(jid);
|
||||||
return devices.length > 0;
|
return devices.length > 0;
|
||||||
},
|
|
||||||
|
|
||||||
getBundlesAndBuildSessions: async function (chatbox) {
|
|
||||||
const no_devices_err = __('Sorry, no devices found to which we can send an OMEMO encrypted message.');
|
|
||||||
let devices;
|
|
||||||
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
|
|
||||||
const collections = await Promise.all(chatbox.occupants.map(o => getDevicesForContact(o.get('jid'))));
|
|
||||||
devices = collections.reduce((a, b) => concat(a, b.models), []);
|
|
||||||
} else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
|
||||||
const their_devices = await getDevicesForContact(chatbox.get('jid'));
|
|
||||||
if (their_devices.length === 0) {
|
|
||||||
const err = new Error(no_devices_err);
|
|
||||||
err.user_facing = true;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
const own_devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
|
||||||
devices = [...own_devices.models, ...their_devices.models];
|
|
||||||
}
|
|
||||||
// Filter out our own device
|
|
||||||
const id = _converse.omemo_store.get('device_id');
|
|
||||||
devices = devices.filter(d => d.get('id') !== id);
|
|
||||||
// Fetch bundles if necessary
|
|
||||||
await Promise.all(devices.map(d => d.getBundle()));
|
|
||||||
|
|
||||||
const sessions = devices.filter(d => d).map(d => getSession(d));
|
|
||||||
await Promise.all(sessions);
|
|
||||||
if (sessions.includes(null)) {
|
|
||||||
// We couldn't build a session for certain devices.
|
|
||||||
devices = devices.filter(d => sessions[devices.indexOf(d)]);
|
|
||||||
if (devices.length === 0) {
|
|
||||||
const err = new Error(no_devices_err);
|
|
||||||
err.user_facing = true;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return devices;
|
|
||||||
},
|
|
||||||
|
|
||||||
createOMEMOMessageStanza: function (chatbox, message, devices) {
|
|
||||||
const body = __(
|
|
||||||
'This is an OMEMO encrypted message which your client doesn’t seem to support. ' +
|
|
||||||
'Find more information on https://conversations.im/omemo'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!message.get('message')) {
|
|
||||||
throw new Error('No message body to encrypt!');
|
|
||||||
}
|
|
||||||
const stanza = $msg({
|
|
||||||
'from': _converse.connection.jid,
|
|
||||||
'to': chatbox.get('jid'),
|
|
||||||
'type': chatbox.get('message_type'),
|
|
||||||
'id': message.get('msgid')
|
|
||||||
}).c('body').t(body).up();
|
|
||||||
|
|
||||||
if (message.get('type') === 'chat') {
|
|
||||||
stanza.c('request', { 'xmlns': Strophe.NS.RECEIPTS }).up();
|
|
||||||
}
|
|
||||||
// An encrypted header is added to the message for
|
|
||||||
// each device that is supposed to receive it.
|
|
||||||
// These headers simply contain the key that the
|
|
||||||
// payload message is encrypted with,
|
|
||||||
// and they are separately encrypted using the
|
|
||||||
// session corresponding to the counterpart device.
|
|
||||||
stanza
|
|
||||||
.c('encrypted', { 'xmlns': Strophe.NS.OMEMO })
|
|
||||||
.c('header', { 'sid': _converse.omemo_store.get('device_id') });
|
|
||||||
|
|
||||||
return omemo.encryptMessage(message.get('message')).then(obj => {
|
|
||||||
// The 16 bytes key and the GCM authentication tag (The tag
|
|
||||||
// SHOULD have at least 128 bit) are concatenated and for each
|
|
||||||
// intended recipient device, i.e. both own devices as well as
|
|
||||||
// devices associated with the contact, the result of this
|
|
||||||
// concatenation is encrypted using the corresponding
|
|
||||||
// long-standing SignalProtocol session.
|
|
||||||
const promises = devices
|
|
||||||
.filter(device => device.get('trusted') != UNTRUSTED && device.get('active'))
|
|
||||||
.map(device => chatbox.encryptKey(obj.key_and_tag, device));
|
|
||||||
|
|
||||||
return Promise.all(promises)
|
|
||||||
.then(dicts => addKeysToMessageStanza(stanza, dicts, obj.iv))
|
|
||||||
.then(stanza => {
|
|
||||||
stanza.c('payload').t(obj.payload).up().up();
|
|
||||||
stanza.c('store', { 'xmlns': Strophe.NS.HINTS });
|
|
||||||
return stanza;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { _converse } from '@converse/headless/core';
|
import { _converse } from '@converse/headless/core';
|
||||||
|
import { createOMEMOMessageStanza, getBundlesAndBuildSessions } from '../utils.js';
|
||||||
|
|
||||||
const ChatBox = {
|
const ChatBox = {
|
||||||
async sendMessage (text, spoiler_hint) {
|
async sendMessage (attrs) {
|
||||||
if (this.get('omemo_active') && text) {
|
if (this.get('omemo_active') && attrs?.body) {
|
||||||
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
|
const plaintext = attrs?.body;
|
||||||
|
attrs = this.getOutgoingMessageAttributes(attrs);
|
||||||
attrs['is_encrypted'] = true;
|
attrs['is_encrypted'] = true;
|
||||||
attrs['plaintext'] = attrs.message;
|
attrs['plaintext'] = plaintext;
|
||||||
let message, stanza;
|
let message, stanza;
|
||||||
try {
|
try {
|
||||||
const devices = await _converse.getBundlesAndBuildSessions(this);
|
const devices = await getBundlesAndBuildSessions(this);
|
||||||
message = await this.createMessage(attrs);
|
message = await this.createMessage(attrs);
|
||||||
stanza = await _converse.createOMEMOMessageStanza(this, message, devices);
|
stanza = await createOMEMOMessageStanza(this, message, devices);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.handleMessageSendError(e);
|
this.handleMessageSendError(e);
|
||||||
return null;
|
return null;
|
||||||
|
152
src/plugins/omemo/tests/media-sharing.js
Normal file
152
src/plugins/omemo/tests/media-sharing.js
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*global mock, converse */
|
||||||
|
|
||||||
|
const { $iq, Strophe, u } = converse.env;
|
||||||
|
|
||||||
|
|
||||||
|
describe("The OMEMO module", function() {
|
||||||
|
|
||||||
|
it("implements XEP-0454 to encrypt uploaded files",
|
||||||
|
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
|
||||||
|
|
||||||
|
const base_url = 'https://example.org/';
|
||||||
|
await mock.waitUntilDiscoConfirmed(
|
||||||
|
_converse, _converse.domain,
|
||||||
|
[{'category': 'server', 'type':'IM'}],
|
||||||
|
['http://jabber.org/protocol/disco#items'], [], 'info');
|
||||||
|
|
||||||
|
const send_backup = XMLHttpRequest.prototype.send;
|
||||||
|
const IQ_stanzas = _converse.connection.IQ_stanzas;
|
||||||
|
|
||||||
|
await mock.waitUntilDiscoConfirmed(_converse, _converse.domain, [], [], ['upload.montague.tld'], 'items');
|
||||||
|
await mock.waitUntilDiscoConfirmed(_converse, 'upload.montague.tld', [], [Strophe.NS.HTTPUPLOAD], []);
|
||||||
|
await mock.waitForRoster(_converse, 'current', 3);
|
||||||
|
const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
|
|
||||||
|
await u.waitUntil(() => mock.initializedOMEMO(_converse));
|
||||||
|
|
||||||
|
await mock.openChatBoxFor(_converse, contact_jid);
|
||||||
|
|
||||||
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
|
let stanza = $iq({
|
||||||
|
'from': contact_jid,
|
||||||
|
'id': iq_stanza.getAttribute('id'),
|
||||||
|
'to': _converse.connection.jid,
|
||||||
|
'type': 'result',
|
||||||
|
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||||
|
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||||
|
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||||
|
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||||
|
.c('device', {'id': '555'});
|
||||||
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
await u.waitUntil(() => _converse.omemo_store);
|
||||||
|
const devicelist = _converse.devicelists.get({'jid': contact_jid});
|
||||||
|
await u.waitUntil(() => devicelist.devices.length === 1);
|
||||||
|
|
||||||
|
const view = _converse.chatboxviews.get(contact_jid);
|
||||||
|
const file = new File(['secret'], 'secret.txt', { type: 'text/plain' })
|
||||||
|
view.model.set('omemo_active', true);
|
||||||
|
view.model.sendFiles([file]);
|
||||||
|
|
||||||
|
await u.waitUntil(() => IQ_stanzas.filter(iq => iq.querySelector('iq[to="upload.montague.tld"] request')).length);
|
||||||
|
const iq = IQ_stanzas.pop();
|
||||||
|
const url = base_url+"/secret.txt";
|
||||||
|
stanza = u.toStanza(`
|
||||||
|
<iq from="upload.montague.tld"
|
||||||
|
id="${iq.getAttribute("id")}"
|
||||||
|
to="romeo@montague.lit/orchard"
|
||||||
|
type="result">
|
||||||
|
<slot xmlns="urn:xmpp:http:upload:0">
|
||||||
|
<put url="https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/secret.txt">
|
||||||
|
<header name="Authorization">Basic Base64String==</header>
|
||||||
|
<header name="Cookie">foo=bar; user=romeo</header>
|
||||||
|
</put>
|
||||||
|
<get url="${url}" />
|
||||||
|
</slot>
|
||||||
|
</iq>`);
|
||||||
|
|
||||||
|
spyOn(XMLHttpRequest.prototype, 'send').and.callFake(async function () {
|
||||||
|
const message = view.model.messages.at(0);
|
||||||
|
message.set('progress', 1);
|
||||||
|
await u.waitUntil(() => view.querySelector('.chat-content progress')?.getAttribute('value') === '1')
|
||||||
|
message.save({
|
||||||
|
'upload': _converse.SUCCESS,
|
||||||
|
'oob_url': message.get('get'),
|
||||||
|
'message': message.get('get')
|
||||||
|
});
|
||||||
|
await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
|
||||||
|
});
|
||||||
|
let sent_stanza;
|
||||||
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, contact_jid, '555'));
|
||||||
|
stanza = $iq({
|
||||||
|
'from': contact_jid,
|
||||||
|
'id': iq_stanza.getAttribute('id'),
|
||||||
|
'to': _converse.bare_jid,
|
||||||
|
'type': 'result',
|
||||||
|
}).c('pubsub', {
|
||||||
|
'xmlns': 'http://jabber.org/protocol/pubsub'
|
||||||
|
}).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:555"})
|
||||||
|
.c('item')
|
||||||
|
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||||
|
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('1111')).up()
|
||||||
|
.c('signedPreKeySignature').t(btoa('2222')).up()
|
||||||
|
.c('identityKey').t(btoa('3333')).up()
|
||||||
|
.c('prekeys')
|
||||||
|
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
|
||||||
|
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
|
||||||
|
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
|
||||||
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
|
||||||
|
stanza = $iq({
|
||||||
|
'from': _converse.bare_jid,
|
||||||
|
'id': iq_stanza.getAttribute('id'),
|
||||||
|
'to': _converse.bare_jid,
|
||||||
|
'type': 'result',
|
||||||
|
}).c('pubsub', {
|
||||||
|
'xmlns': 'http://jabber.org/protocol/pubsub'
|
||||||
|
}).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:482886413b977930064a5888b92134fe"})
|
||||||
|
.c('item')
|
||||||
|
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||||
|
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('100000')).up()
|
||||||
|
.c('signedPreKeySignature').t(btoa('200000')).up()
|
||||||
|
.c('identityKey').t(btoa('300000')).up()
|
||||||
|
.c('prekeys')
|
||||||
|
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1991')).up()
|
||||||
|
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1992')).up()
|
||||||
|
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1993'));
|
||||||
|
|
||||||
|
spyOn(_converse.connection, 'send').and.callFake(stanza => (sent_stanza = stanza));
|
||||||
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
|
await u.waitUntil(() => sent_stanza);
|
||||||
|
|
||||||
|
const fallback = 'This is an OMEMO encrypted message which your client doesn’t seem to support. Find more information on https://conversations.im/omemo';
|
||||||
|
expect(Strophe.serialize(sent_stanza)).toBe(
|
||||||
|
`<message from="romeo@montague.lit/orchard" `+
|
||||||
|
`id="${sent_stanza.getAttribute("id")}" `+
|
||||||
|
`to="lady.montague@montague.lit" `+
|
||||||
|
`type="chat" `+
|
||||||
|
`xmlns="jabber:client">`+
|
||||||
|
`<body>${fallback}</body>`+
|
||||||
|
`<request xmlns="urn:xmpp:receipts"/>`+
|
||||||
|
`<encrypted xmlns="eu.siacs.conversations.axolotl">`+
|
||||||
|
`<header sid="123456789">`+
|
||||||
|
`<key rid="482886413b977930064a5888b92134fe">YzFwaDNSNzNYNw==</key>`+
|
||||||
|
`<key rid="555">YzFwaDNSNzNYNw==</key>`+
|
||||||
|
`<iv>${sent_stanza.querySelector('header iv').textContent}</iv>`+
|
||||||
|
`</header>`+
|
||||||
|
`<payload>${sent_stanza.querySelector('payload').textContent}</payload>`+
|
||||||
|
`</encrypted>`+
|
||||||
|
`<store xmlns="urn:xmpp:hints"/>`+
|
||||||
|
`</message>`);
|
||||||
|
|
||||||
|
const link_el = await u.waitUntil(() => view.querySelector('.chat-msg__media'));
|
||||||
|
expect(link_el.textContent.trim()).toBe('Download file "secret.txt"', 1000);
|
||||||
|
|
||||||
|
const message = view.model.messages.at(0);
|
||||||
|
expect(message.get('is_encrypted')).toBe(true);
|
||||||
|
|
||||||
|
XMLHttpRequest.prototype.send = send_backup;
|
||||||
|
}));
|
||||||
|
});
|
@ -3,71 +3,6 @@
|
|||||||
const { $iq, $pres, $msg, omemo, Strophe } = converse.env;
|
const { $iq, $pres, $msg, omemo, Strophe } = converse.env;
|
||||||
const u = converse.env.utils;
|
const u = converse.env.utils;
|
||||||
|
|
||||||
async function deviceListFetched (_converse, jid) {
|
|
||||||
const selector = `iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.devicelist"]`;
|
|
||||||
const stanza = await u.waitUntil(
|
|
||||||
() => Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop()
|
|
||||||
);
|
|
||||||
await u.waitUntil(() => _converse.devicelists.get(jid));
|
|
||||||
return stanza;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ownDeviceHasBeenPublished (_converse) {
|
|
||||||
return Array.from(_converse.connection.IQ_stanzas).filter(
|
|
||||||
iq => iq.querySelector('iq[from="'+_converse.bare_jid+'"] publish[node="eu.siacs.conversations.axolotl.devicelist"]')
|
|
||||||
).pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function bundleHasBeenPublished (_converse) {
|
|
||||||
const selector = 'publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]';
|
|
||||||
return Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function bundleFetched (_converse, jid, device_id) {
|
|
||||||
return Array.from(_converse.connection.IQ_stanzas).filter(
|
|
||||||
iq => iq.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.bundles:${device_id}"]`)
|
|
||||||
).pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initializedOMEMO (_converse) {
|
|
||||||
await mock.waitUntilDiscoConfirmed(
|
|
||||||
_converse, _converse.bare_jid,
|
|
||||||
[{'category': 'pubsub', 'type': 'pep'}],
|
|
||||||
['http://jabber.org/protocol/pubsub#publish-options']
|
|
||||||
);
|
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
|
|
||||||
let stanza = $iq({
|
|
||||||
'from': _converse.bare_jid,
|
|
||||||
'id': iq_stanza.getAttribute('id'),
|
|
||||||
'to': _converse.bare_jid,
|
|
||||||
'type': 'result',
|
|
||||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
|
||||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
|
||||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
|
||||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
|
||||||
.c('device', {'id': '482886413b977930064a5888b92134fe'});
|
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
|
||||||
iq_stanza = await u.waitUntil(() => ownDeviceHasBeenPublished(_converse))
|
|
||||||
|
|
||||||
stanza = $iq({
|
|
||||||
'from': _converse.bare_jid,
|
|
||||||
'id': iq_stanza.getAttribute('id'),
|
|
||||||
'to': _converse.bare_jid,
|
|
||||||
'type': 'result'});
|
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
|
||||||
|
|
||||||
iq_stanza = await u.waitUntil(() => bundleHasBeenPublished(_converse))
|
|
||||||
|
|
||||||
stanza = $iq({
|
|
||||||
'from': _converse.bare_jid,
|
|
||||||
'id': iq_stanza.getAttribute('id'),
|
|
||||||
'to': _converse.bare_jid,
|
|
||||||
'type': 'result'});
|
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
|
||||||
await _converse.api.waitUntil('OMEMOInitialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
describe("The OMEMO module", function() {
|
describe("The OMEMO module", function() {
|
||||||
|
|
||||||
it("adds methods for encrypting and decrypting messages via AES GCM",
|
it("adds methods for encrypting and decrypting messages via AES GCM",
|
||||||
@ -86,9 +21,9 @@ describe("The OMEMO module", function() {
|
|||||||
let sent_stanza;
|
let sent_stanza;
|
||||||
await mock.waitForRoster(_converse, 'current', 1);
|
await mock.waitForRoster(_converse, 'current', 1);
|
||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
await u.waitUntil(() => initializedOMEMO(_converse));
|
await u.waitUntil(() => mock.initializedOMEMO(_converse));
|
||||||
await mock.openChatBoxFor(_converse, contact_jid);
|
await mock.openChatBoxFor(_converse, contact_jid);
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
let stanza = $iq({
|
let stanza = $iq({
|
||||||
'from': contact_jid,
|
'from': contact_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -115,7 +50,7 @@ describe("The OMEMO module", function() {
|
|||||||
preventDefault: function preventDefault () {},
|
preventDefault: function preventDefault () {},
|
||||||
keyCode: 13 // Enter
|
keyCode: 13 // Enter
|
||||||
});
|
});
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, contact_jid, '555'));
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, contact_jid, '555'));
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': contact_jid,
|
'from': contact_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -134,7 +69,7 @@ describe("The OMEMO module", function() {
|
|||||||
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
|
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
|
||||||
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
|
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -233,7 +168,7 @@ describe("The OMEMO module", function() {
|
|||||||
];
|
];
|
||||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
||||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||||
await u.waitUntil(() => initializedOMEMO(_converse));
|
await u.waitUntil(() => mock.initializedOMEMO(_converse));
|
||||||
|
|
||||||
const toolbar = await u.waitUntil(() => view.querySelector('.chat-toolbar'));
|
const toolbar = await u.waitUntil(() => view.querySelector('.chat-toolbar'));
|
||||||
const el = await u.waitUntil(() => toolbar.querySelector('.toggle-omemo'));
|
const el = await u.waitUntil(() => toolbar.querySelector('.toggle-omemo'));
|
||||||
@ -255,7 +190,7 @@ describe("The OMEMO module", function() {
|
|||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
// Wait for Converse to fetch newguy's device list
|
// Wait for Converse to fetch newguy's device list
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -278,7 +213,7 @@ describe("The OMEMO module", function() {
|
|||||||
await u.waitUntil(() => _converse.omemo_store);
|
await u.waitUntil(() => _converse.omemo_store);
|
||||||
expect(_converse.devicelists.length).toBe(2);
|
expect(_converse.devicelists.length).toBe(2);
|
||||||
|
|
||||||
await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
const devicelist = _converse.devicelists.get(contact_jid);
|
const devicelist = _converse.devicelists.get(contact_jid);
|
||||||
expect(devicelist.devices.length).toBe(1);
|
expect(devicelist.devices.length).toBe(1);
|
||||||
expect(devicelist.devices.at(0).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
|
expect(devicelist.devices.at(0).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
|
||||||
@ -296,7 +231,7 @@ describe("The OMEMO module", function() {
|
|||||||
preventDefault: function preventDefault () {},
|
preventDefault: function preventDefault () {},
|
||||||
keyCode: 13 // Enter
|
keyCode: 13 // Enter
|
||||||
});
|
});
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, contact_jid, '4e30f35051b7b8b42abe083742187228'), 1000);
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, contact_jid, '4e30f35051b7b8b42abe083742187228'), 1000);
|
||||||
console.log("Bundle fetched 4e30f35051b7b8b42abe083742187228");
|
console.log("Bundle fetched 4e30f35051b7b8b42abe083742187228");
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': contact_jid,
|
'from': contact_jid,
|
||||||
@ -317,7 +252,7 @@ describe("The OMEMO module", function() {
|
|||||||
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
|
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'), 1000);
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'), 1000);
|
||||||
console.log("Bundle fetched 482886413b977930064a5888b92134fe");
|
console.log("Bundle fetched 482886413b977930064a5888b92134fe");
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
@ -367,9 +302,9 @@ describe("The OMEMO module", function() {
|
|||||||
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], [Strophe.NS.SID]);
|
await mock.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], [Strophe.NS.SID]);
|
||||||
await mock.waitForRoster(_converse, 'current', 1);
|
await mock.waitForRoster(_converse, 'current', 1);
|
||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
await u.waitUntil(() => initializedOMEMO(_converse));
|
await u.waitUntil(() => mock.initializedOMEMO(_converse));
|
||||||
await mock.openChatBoxFor(_converse, contact_jid);
|
await mock.openChatBoxFor(_converse, contact_jid);
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
const my_devicelist = _converse.devicelists.get({'jid': _converse.bare_jid});
|
const my_devicelist = _converse.devicelists.get({'jid': _converse.bare_jid});
|
||||||
expect(my_devicelist.devices.length).toBe(2);
|
expect(my_devicelist.devices.length).toBe(2);
|
||||||
|
|
||||||
@ -429,7 +364,7 @@ describe("The OMEMO module", function() {
|
|||||||
|
|
||||||
// The message received is a prekey message, so missing prekeys are
|
// The message received is a prekey message, so missing prekeys are
|
||||||
// generated and a new bundle published.
|
// generated and a new bundle published.
|
||||||
iq_stanza = await u.waitUntil(() => bundleHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse));
|
||||||
const result_iq = $iq({
|
const result_iq = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -460,7 +395,7 @@ describe("The OMEMO module", function() {
|
|||||||
preventDefault: function preventDefault () {},
|
preventDefault: function preventDefault () {},
|
||||||
keyCode: 13 // Enter
|
keyCode: 13 // Enter
|
||||||
});
|
});
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, _converse.bare_jid, '988349631'));
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, _converse.bare_jid, '988349631'));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${_converse.bare_jid}" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${_converse.bare_jid}" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -486,7 +421,7 @@ describe("The OMEMO module", function() {
|
|||||||
];
|
];
|
||||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
||||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||||
await u.waitUntil(() => initializedOMEMO(_converse));
|
await u.waitUntil(() => mock.initializedOMEMO(_converse));
|
||||||
|
|
||||||
const contact_jid = 'newguy@montague.lit';
|
const contact_jid = 'newguy@montague.lit';
|
||||||
let stanza = $pres({
|
let stanza = $pres({
|
||||||
@ -515,7 +450,7 @@ describe("The OMEMO module", function() {
|
|||||||
preventDefault: function preventDefault () {},
|
preventDefault: function preventDefault () {},
|
||||||
keyCode: 13 // Enter
|
keyCode: 13 // Enter
|
||||||
});
|
});
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -539,11 +474,11 @@ describe("The OMEMO module", function() {
|
|||||||
expect(_converse.devicelists.length).toBe(2);
|
expect(_converse.devicelists.length).toBe(2);
|
||||||
|
|
||||||
const devicelist = _converse.devicelists.get(contact_jid);
|
const devicelist = _converse.devicelists.get(contact_jid);
|
||||||
await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(devicelist.devices.length).toBe(1);
|
expect(devicelist.devices.length).toBe(1);
|
||||||
expect(devicelist.devices.at(0).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
|
expect(devicelist.devices.at(0).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
|
||||||
|
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -561,7 +496,7 @@ describe("The OMEMO module", function() {
|
|||||||
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1991')).up()
|
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1991')).up()
|
||||||
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1992')).up()
|
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1992')).up()
|
||||||
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1993'));
|
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1993'));
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, contact_jid, '4e30f35051b7b8b42abe083742187228'));
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, contact_jid, '4e30f35051b7b8b42abe083742187228'));
|
||||||
|
|
||||||
/* <iq xmlns="jabber:client" to="jc@opkode.com/converse.js-34183907" type="error" id="945c8ab3-b561-4d8a-92da-77c226bb1689:sendIQ" from="joris@konuro.net">
|
/* <iq xmlns="jabber:client" to="jc@opkode.com/converse.js-34183907" type="error" id="945c8ab3-b561-4d8a-92da-77c226bb1689:sendIQ" from="joris@konuro.net">
|
||||||
* <pubsub xmlns="http://jabber.org/protocol/pubsub">
|
* <pubsub xmlns="http://jabber.org/protocol/pubsub">
|
||||||
@ -603,7 +538,7 @@ describe("The OMEMO module", function() {
|
|||||||
await mock.waitForRoster(_converse, 'current', 1);
|
await mock.waitForRoster(_converse, 'current', 1);
|
||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
|
|
||||||
await u.waitUntil(() => initializedOMEMO(_converse));
|
await u.waitUntil(() => mock.initializedOMEMO(_converse));
|
||||||
const obj = await omemo.encryptMessage('This is an encrypted message from the contact');
|
const obj = await omemo.encryptMessage('This is an encrypted message from the contact');
|
||||||
// XXX: Normally the key will be encrypted via libsignal.
|
// XXX: Normally the key will be encrypted via libsignal.
|
||||||
// However, we're mocking libsignal in the tests, so we include
|
// However, we're mocking libsignal in the tests, so we include
|
||||||
@ -635,7 +570,7 @@ describe("The OMEMO module", function() {
|
|||||||
});
|
});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
let iq_stanza = await deviceListFetched(_converse, contact_jid);
|
let iq_stanza = await mock.deviceListFetched(_converse, contact_jid);
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': contact_jid,
|
'from': contact_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -653,7 +588,7 @@ describe("The OMEMO module", function() {
|
|||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
await u.waitUntil(() => _converse.omemo_store);
|
await u.waitUntil(() => _converse.omemo_store);
|
||||||
iq_stanza = await u.waitUntil(() => bundleHasBeenPublished(_converse), 1000);
|
iq_stanza = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse), 1000);
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" type="set" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" type="set" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -703,7 +638,7 @@ describe("The OMEMO module", function() {
|
|||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
|
|
||||||
// Wait until own devices are fetched
|
// Wait until own devices are fetched
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, _converse.bare_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -729,14 +664,14 @@ describe("The OMEMO module", function() {
|
|||||||
expect(devicelist.devices.length).toBe(2);
|
expect(devicelist.devices.length).toBe(2);
|
||||||
expect(devicelist.devices.at(0).get('id')).toBe('555');
|
expect(devicelist.devices.at(0).get('id')).toBe('555');
|
||||||
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
||||||
iq_stanza = await u.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.ownDeviceHasBeenPublished(_converse));
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
'to': _converse.bare_jid,
|
'to': _converse.bare_jid,
|
||||||
'type': 'result'});
|
'type': 'result'});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
iq_stanza = await u.waitUntil(() => bundleHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse));
|
||||||
|
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
@ -822,7 +757,7 @@ describe("The OMEMO module", function() {
|
|||||||
.c('device', {'id': '444'})
|
.c('device', {'id': '444'})
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
iq_stanza = await u.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.ownDeviceHasBeenPublished(_converse));
|
||||||
// Check that our own device is added again, but that removed
|
// Check that our own device is added again, but that removed
|
||||||
// devices are not added.
|
// devices are not added.
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
@ -872,7 +807,7 @@ describe("The OMEMO module", function() {
|
|||||||
|
|
||||||
await mock.waitForRoster(_converse, 'current');
|
await mock.waitForRoster(_converse, 'current');
|
||||||
const contact_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, _converse.bare_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -897,14 +832,14 @@ describe("The OMEMO module", function() {
|
|||||||
expect(devicelist.devices.length).toBe(2);
|
expect(devicelist.devices.length).toBe(2);
|
||||||
expect(devicelist.devices.at(0).get('id')).toBe('555');
|
expect(devicelist.devices.at(0).get('id')).toBe('555');
|
||||||
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
||||||
iq_stanza = await u.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.ownDeviceHasBeenPublished(_converse));
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
'to': _converse.bare_jid,
|
'to': _converse.bare_jid,
|
||||||
'type': 'result'});
|
'type': 'result'});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
iq_stanza = await u.waitUntil(() => bundleHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse));
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -1021,7 +956,7 @@ describe("The OMEMO module", function() {
|
|||||||
|
|
||||||
await mock.waitForRoster(_converse, 'current', 1);
|
await mock.waitForRoster(_converse, 'current', 1);
|
||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, _converse.bare_jid));
|
||||||
let stanza = $iq({
|
let stanza = $iq({
|
||||||
'from': contact_jid,
|
'from': contact_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -1035,7 +970,7 @@ describe("The OMEMO module", function() {
|
|||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
expect(_converse.devicelists.length).toBe(1);
|
expect(_converse.devicelists.length).toBe(1);
|
||||||
await mock.openChatBoxFor(_converse, contact_jid);
|
await mock.openChatBoxFor(_converse, contact_jid);
|
||||||
iq_stanza = await ownDeviceHasBeenPublished(_converse);
|
iq_stanza = await mock.ownDeviceHasBeenPublished(_converse);
|
||||||
stanza = $iq({
|
stanza = $iq({
|
||||||
'from': _converse.bare_jid,
|
'from': _converse.bare_jid,
|
||||||
'id': iq_stanza.getAttribute('id'),
|
'id': iq_stanza.getAttribute('id'),
|
||||||
@ -1043,7 +978,7 @@ describe("The OMEMO module", function() {
|
|||||||
'type': 'result'});
|
'type': 'result'});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
iq_stanza = await u.waitUntil(() => bundleHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" type="set" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" type="set" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -1095,7 +1030,7 @@ describe("The OMEMO module", function() {
|
|||||||
await mock.waitForRoster(_converse, 'current', 1);
|
await mock.waitForRoster(_converse, 'current', 1);
|
||||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
|
||||||
|
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, _converse.bare_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="romeo@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -1121,7 +1056,7 @@ describe("The OMEMO module", function() {
|
|||||||
expect(devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
|
expect(devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
|
||||||
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
||||||
// Check that own device was published
|
// Check that own device was published
|
||||||
iq_stanza = await u.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
iq_stanza = await u.waitUntil(() => mock.ownDeviceHasBeenPublished(_converse));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute(`id`)}" type="set" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute(`id`)}" type="set" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -1153,7 +1088,7 @@ describe("The OMEMO module", function() {
|
|||||||
'type': 'result'});
|
'type': 'result'});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
const iq_el = await u.waitUntil(() => bundleHasBeenPublished(_converse));
|
const iq_el = await u.waitUntil(() => mock.bundleHasBeenPublished(_converse));
|
||||||
expect(iq_el.getAttributeNames().sort().join()).toBe(["from", "type", "xmlns", "id"].sort().join());
|
expect(iq_el.getAttributeNames().sort().join()).toBe(["from", "type", "xmlns", "id"].sort().join());
|
||||||
expect(iq_el.querySelector('prekeys').childNodes.length).toBe(100);
|
expect(iq_el.querySelector('prekeys').childNodes.length).toBe(100);
|
||||||
|
|
||||||
@ -1172,7 +1107,7 @@ describe("The OMEMO module", function() {
|
|||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
await _converse.api.waitUntil('OMEMOInitialized', 1000);
|
await _converse.api.waitUntil('OMEMOInitialized', 1000);
|
||||||
await mock.openChatBoxFor(_converse, contact_jid);
|
await mock.openChatBoxFor(_converse, contact_jid);
|
||||||
iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -1264,7 +1199,7 @@ describe("The OMEMO module", function() {
|
|||||||
];
|
];
|
||||||
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo', features);
|
||||||
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
const view = _converse.chatboxviews.get('lounge@montague.lit');
|
||||||
await u.waitUntil(() => initializedOMEMO(_converse));
|
await u.waitUntil(() => mock.initializedOMEMO(_converse));
|
||||||
|
|
||||||
const toolbar = await u.waitUntil(() => view.querySelector('.chat-toolbar'));
|
const toolbar = await u.waitUntil(() => view.querySelector('.chat-toolbar'));
|
||||||
let toggle = await u.waitUntil(() => toolbar.querySelector('.toggle-omemo'));
|
let toggle = await u.waitUntil(() => toolbar.querySelector('.toggle-omemo'));
|
||||||
@ -1298,7 +1233,7 @@ describe("The OMEMO module", function() {
|
|||||||
}).tree();
|
}).tree();
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
|
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -1321,7 +1256,7 @@ describe("The OMEMO module", function() {
|
|||||||
await u.waitUntil(() => _converse.omemo_store);
|
await u.waitUntil(() => _converse.omemo_store);
|
||||||
expect(_converse.devicelists.length).toBe(2);
|
expect(_converse.devicelists.length).toBe(2);
|
||||||
|
|
||||||
await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
const devicelist = _converse.devicelists.get(contact_jid);
|
const devicelist = _converse.devicelists.get(contact_jid);
|
||||||
expect(devicelist.devices.length).toBe(2);
|
expect(devicelist.devices.length).toBe(2);
|
||||||
expect(devicelist.devices.at(0).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
|
expect(devicelist.devices.at(0).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
|
||||||
@ -1381,7 +1316,7 @@ describe("The OMEMO module", function() {
|
|||||||
'role': 'participant'
|
'role': 'participant'
|
||||||
}).tree();
|
}).tree();
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
@ -1432,7 +1367,7 @@ describe("The OMEMO module", function() {
|
|||||||
show_modal_button.click();
|
show_modal_button.click();
|
||||||
const modal = _converse.api.modal.get('user-details-modal');
|
const modal = _converse.api.modal.get('user-details-modal');
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||||
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
let iq_stanza = await u.waitUntil(() => mock.deviceListFetched(_converse, contact_jid));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="mercutio@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="mercutio@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub>`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub>`+
|
||||||
@ -1449,7 +1384,7 @@ describe("The OMEMO module", function() {
|
|||||||
.c('device', {'id': '555'});
|
.c('device', {'id': '555'});
|
||||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||||
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
await u.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||||
iq_stanza = await u.waitUntil(() => bundleFetched(_converse, contact_jid, '555'));
|
iq_stanza = await u.waitUntil(() => mock.bundleFetched(_converse, contact_jid, '555'));
|
||||||
expect(Strophe.serialize(iq_stanza)).toBe(
|
expect(Strophe.serialize(iq_stanza)).toBe(
|
||||||
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="mercutio@montague.lit" type="get" xmlns="jabber:client">`+
|
`<iq from="romeo@montague.lit" id="${iq_stanza.getAttribute("id")}" to="mercutio@montague.lit" type="get" xmlns="jabber:client">`+
|
||||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||||
|
@ -6,13 +6,15 @@ import tpl_audio from 'templates/audio.js';
|
|||||||
import tpl_file from 'templates/file.js';
|
import tpl_file from 'templates/file.js';
|
||||||
import tpl_image from 'templates/image.js';
|
import tpl_image from 'templates/image.js';
|
||||||
import tpl_video from 'templates/video.js';
|
import tpl_video from 'templates/video.js';
|
||||||
|
import { MIMETYPES_MAP } from 'utils/file.js';
|
||||||
|
import { KEY_ALGO, UNTRUSTED, TAG_LENGTH } from './consts.js';
|
||||||
import { __ } from 'i18n';
|
import { __ } from 'i18n';
|
||||||
import { _converse, converse, api } from '@converse/headless/core';
|
import { _converse, converse, api } from '@converse/headless/core';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||||
import { isAudioURL, isImageURL, isVideoURL, getURI } from 'utils/html.js';
|
import { isAudioURL, isImageURL, isVideoURL, getURI } from 'utils/html.js';
|
||||||
|
import concat from 'lodash-es/concat';
|
||||||
import { until } from 'lit/directives/until.js';
|
import { until } from 'lit/directives/until.js';
|
||||||
import { MIMETYPES_MAP } from 'utils/file.js';
|
|
||||||
import {
|
import {
|
||||||
appendArrayBuffer,
|
appendArrayBuffer,
|
||||||
arrayBufferToBase64,
|
arrayBufferToBase64,
|
||||||
@ -23,13 +25,7 @@ import {
|
|||||||
stringToArrayBuffer
|
stringToArrayBuffer
|
||||||
} from '@converse/headless/utils/arraybuffer.js';
|
} from '@converse/headless/utils/arraybuffer.js';
|
||||||
|
|
||||||
const { Strophe, sizzle, u } = converse.env;
|
const { $msg, Strophe, sizzle, u } = converse.env;
|
||||||
|
|
||||||
const TAG_LENGTH = 128;
|
|
||||||
const KEY_ALGO = {
|
|
||||||
'name': 'AES-GCM',
|
|
||||||
'length': 128
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
async function encryptMessage (plaintext) {
|
async function encryptMessage (plaintext) {
|
||||||
@ -368,11 +364,7 @@ export function addKeysToMessageStanza (stanza, dicts, iv) {
|
|||||||
}
|
}
|
||||||
stanza.up();
|
stanza.up();
|
||||||
if (i == dicts.length - 1) {
|
if (i == dicts.length - 1) {
|
||||||
stanza
|
stanza.c('iv').t(iv).up().up();
|
||||||
.c('iv')
|
|
||||||
.t(iv)
|
|
||||||
.up()
|
|
||||||
.up();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -687,3 +679,91 @@ export function getOMEMOToolbarButton (toolbar_el, buttons) {
|
|||||||
`);
|
`);
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function getBundlesAndBuildSessions (chatbox) {
|
||||||
|
const no_devices_err = __('Sorry, no devices found to which we can send an OMEMO encrypted message.');
|
||||||
|
let devices;
|
||||||
|
if (chatbox.get('type') === _converse.CHATROOMS_TYPE) {
|
||||||
|
const collections = await Promise.all(chatbox.occupants.map(o => getDevicesForContact(o.get('jid'))));
|
||||||
|
devices = collections.reduce((a, b) => concat(a, b.models), []);
|
||||||
|
} else if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
|
||||||
|
const their_devices = await getDevicesForContact(chatbox.get('jid'));
|
||||||
|
if (their_devices.length === 0) {
|
||||||
|
const err = new Error(no_devices_err);
|
||||||
|
err.user_facing = true;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
const own_devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
||||||
|
devices = [...own_devices.models, ...their_devices.models];
|
||||||
|
}
|
||||||
|
// Filter out our own device
|
||||||
|
const id = _converse.omemo_store.get('device_id');
|
||||||
|
devices = devices.filter(d => d.get('id') !== id);
|
||||||
|
// Fetch bundles if necessary
|
||||||
|
await Promise.all(devices.map(d => d.getBundle()));
|
||||||
|
|
||||||
|
const sessions = devices.filter(d => d).map(d => getSession(d));
|
||||||
|
await Promise.all(sessions);
|
||||||
|
if (sessions.includes(null)) {
|
||||||
|
// We couldn't build a session for certain devices.
|
||||||
|
devices = devices.filter(d => sessions[devices.indexOf(d)]);
|
||||||
|
if (devices.length === 0) {
|
||||||
|
const err = new Error(no_devices_err);
|
||||||
|
err.user_facing = true;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function createOMEMOMessageStanza (chatbox, message, devices) {
|
||||||
|
const body = __(
|
||||||
|
'This is an OMEMO encrypted message which your client doesn’t seem to support. ' +
|
||||||
|
'Find more information on https://conversations.im/omemo'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!message.get('message')) {
|
||||||
|
throw new Error('No message body to encrypt!');
|
||||||
|
}
|
||||||
|
const stanza = $msg({
|
||||||
|
'from': _converse.connection.jid,
|
||||||
|
'to': chatbox.get('jid'),
|
||||||
|
'type': chatbox.get('message_type'),
|
||||||
|
'id': message.get('msgid')
|
||||||
|
}).c('body').t(body).up();
|
||||||
|
|
||||||
|
if (message.get('type') === 'chat') {
|
||||||
|
stanza.c('request', { 'xmlns': Strophe.NS.RECEIPTS }).up();
|
||||||
|
}
|
||||||
|
// An encrypted header is added to the message for
|
||||||
|
// each device that is supposed to receive it.
|
||||||
|
// These headers simply contain the key that the
|
||||||
|
// payload message is encrypted with,
|
||||||
|
// and they are separately encrypted using the
|
||||||
|
// session corresponding to the counterpart device.
|
||||||
|
stanza
|
||||||
|
.c('encrypted', { 'xmlns': Strophe.NS.OMEMO })
|
||||||
|
.c('header', { 'sid': _converse.omemo_store.get('device_id') });
|
||||||
|
|
||||||
|
return omemo.encryptMessage(message.get('message')).then(obj => {
|
||||||
|
// The 16 bytes key and the GCM authentication tag (The tag
|
||||||
|
// SHOULD have at least 128 bit) are concatenated and for each
|
||||||
|
// intended recipient device, i.e. both own devices as well as
|
||||||
|
// devices associated with the contact, the result of this
|
||||||
|
// concatenation is encrypted using the corresponding
|
||||||
|
// long-standing SignalProtocol session.
|
||||||
|
const promises = devices
|
||||||
|
.filter(device => device.get('trusted') != UNTRUSTED && device.get('active'))
|
||||||
|
.map(device => chatbox.encryptKey(obj.key_and_tag, device));
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
.then(dicts => addKeysToMessageStanza(stanza, dicts, obj.iv))
|
||||||
|
.then(stanza => {
|
||||||
|
stanza.c('payload').t(obj.payload).up().up();
|
||||||
|
stanza.c('store', { 'xmlns': Strophe.NS.HINTS });
|
||||||
|
return stanza;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user