|
@@ -1,5 +1,8 @@
|
|
|
/**
|
|
|
* @typedef {module:plugins-omemo-index.WindowWithLibsignal} WindowWithLibsignal
|
|
|
+ * @typedef {module:plugin-chat-parsers.MessageAttributes} MessageAttributes
|
|
|
+ * @typedef {module:plugin-muc-parsers.MUCMessageAttributes} MUCMessageAttributes
|
|
|
+ * @typedef {import('@converse/headless/plugins/chat/model.js').default} ChatBox
|
|
|
*/
|
|
|
import tplAudio from 'templates/audio.js';
|
|
|
import tplFile from 'templates/file.js';
|
|
@@ -24,6 +27,8 @@ import {
|
|
|
hexToArrayBuffer,
|
|
|
stringToArrayBuffer
|
|
|
} from '@converse/headless/utils/arraybuffer.js';
|
|
|
+import MUC from 'headless/plugins/muc/muc.js';
|
|
|
+import {IQError, UserFacingError} from 'shared/errors.js';
|
|
|
|
|
|
const { Strophe, URI, sizzle, u } = converse.env;
|
|
|
|
|
@@ -36,8 +41,12 @@ export function formatFingerprint (fp) {
|
|
|
return fp;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * @param {Error|IQError|UserFacingError} e
|
|
|
+ * @param {ChatBox} chat
|
|
|
+ */
|
|
|
export function handleMessageSendError (e, chat) {
|
|
|
- if (e.name === 'IQError') {
|
|
|
+ if (e instanceof IQError) {
|
|
|
chat.save('omemo_supported', false);
|
|
|
|
|
|
const err_msgs = [];
|
|
@@ -61,7 +70,7 @@ export function handleMessageSendError (e, chat) {
|
|
|
err_msgs.push(e.iq.outerHTML);
|
|
|
}
|
|
|
api.alert('error', __('Error'), err_msgs);
|
|
|
- } else if (e.user_facing) {
|
|
|
+ } else if (e instanceof UserFacingError) {
|
|
|
api.alert('error', __('Error'), [e.message]);
|
|
|
}
|
|
|
throw e;
|
|
@@ -79,6 +88,9 @@ export function getOutgoingMessageAttributes (chat, attrs) {
|
|
|
return attrs;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * @param {string} plaintext
|
|
|
+ */
|
|
|
async function encryptMessage (plaintext) {
|
|
|
// The client MUST use fresh, randomly generated key/IV pairs
|
|
|
// with AES-128 in Galois/Counter Mode (GCM).
|
|
@@ -122,13 +134,18 @@ async function decryptMessage (obj) {
|
|
|
return arrayBufferToString(await crypto.subtle.decrypt(algo, key_obj, cipher));
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * @param {File} file
|
|
|
+ * @returns {Promise<File>}
|
|
|
+ */
|
|
|
export async function encryptFile (file) {
|
|
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
|
const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256, }, true, ['encrypt', 'decrypt']);
|
|
|
const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv, }, key, await file.arrayBuffer());
|
|
|
const exported_key = await window.crypto.subtle.exportKey('raw', key);
|
|
|
const encrypted_file = new File([encrypted], file.name, { type: file.type, lastModified: file.lastModified });
|
|
|
- encrypted_file.xep454_ivkey = arrayBufferToHex(iv) + arrayBufferToHex(exported_key);
|
|
|
+
|
|
|
+ Object.assign(encrypted_file, { xep454_ivkey: arrayBufferToHex(iv) + arrayBufferToHex(exported_key) });
|
|
|
return encrypted_file;
|
|
|
}
|
|
|
|
|
@@ -327,8 +344,9 @@ export function onChatInitialized (el) {
|
|
|
}
|
|
|
|
|
|
export function getSessionCipher (jid, id) {
|
|
|
+ const { libsignal } = /** @type WindowWithLibsignal */(window);
|
|
|
const address = new libsignal.SignalProtocolAddress(jid, id);
|
|
|
- return new window.libsignal.SessionCipher(_converse.omemo_store, address);
|
|
|
+ return new libsignal.SessionCipher(_converse.omemo_store, address);
|
|
|
}
|
|
|
|
|
|
function getJIDForDecryption (attrs) {
|
|
@@ -780,18 +798,19 @@ export function getOMEMOToolbarButton (toolbar_el, buttons) {
|
|
|
}
|
|
|
|
|
|
|
|
|
+/**
|
|
|
+ * @param {MUC|ChatBox} chatbox
|
|
|
+ */
|
|
|
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') === CHATROOMS_TYPE) {
|
|
|
+ if (chatbox instanceof MUC) {
|
|
|
const collections = await Promise.all(chatbox.occupants.map(o => getDevicesForContact(o.get('jid'))));
|
|
|
devices = collections.reduce((a, b) => a.concat(b.models), []);
|
|
|
} else if (chatbox.get('type') === 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;
|
|
|
+ throw new UserFacingError(no_devices_err);
|
|
|
}
|
|
|
const own_list = await api.omemo.devicelists.get(_converse.bare_jid)
|
|
|
const own_devices = own_list.devices;
|
|
@@ -809,9 +828,7 @@ async function getBundlesAndBuildSessions (chatbox) {
|
|
|
// 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;
|
|
|
+ throw new UserFacingError(no_devices_err);
|
|
|
}
|
|
|
}
|
|
|
return devices;
|