diff --git a/js/modules/loki_app_dot_net_api.js b/js/modules/loki_app_dot_net_api.js index 4004aae91..bbc934ec6 100644 --- a/js/modules/loki_app_dot_net_api.js +++ b/js/modules/loki_app_dot_net_api.js @@ -648,7 +648,13 @@ class LokiPublicChannelAPI { } } - static getSigData(sigVer, noteValue, adnMessage) { + static getSigData( + sigVer, + noteValue, + attachmentAnnotations, + previewAnnotations, + adnMessage + ) { let sigString = ''; sigString += adnMessage.text.trim(); sigString += noteValue.timestamp; @@ -660,6 +666,12 @@ class LokiPublicChannelAPI { sigString += adnMessage.reply_to; } } + attachmentAnnotations + .concat(previewAnnotations) + .map(data => data.id || data.image.id) + .sort() + // eslint-disable-next-line no-return-assign + .forEach(id => sigString += id); sigString += sigVer; return dcodeIO.ByteBuffer.wrap(sigString, 'utf8').toArrayBuffer(); } @@ -682,17 +694,26 @@ class LokiPublicChannelAPI { const { timestamp, quote } = noteValue; if (quote) { + // TODO: Enable quote attachments again using proper ADN style quote.attachments = []; } // try to verify signature const { sig, sigver } = noteValue; const annoCopy = [...adnMessage.annotations]; + const attachments = annoCopy + .filter(anno => anno.value.lokiType === LOKI_ATTACHMENT_TYPE) + .map(attachment => ({ isRaw: true, ...attachment.value })); + const preview = annoCopy + .filter(anno => anno.value.lokiType === LOKI_PREVIEW_TYPE) + .map(LokiPublicChannelAPI.getPreviewFromAnnotation); // strip out sig and sigver annoCopy[0] = _.omit(annoCopy[0], ['value.sig', 'value.sigver']); const sigData = LokiPublicChannelAPI.getSigData( sigver, noteValue, + attachments, + preview, adnMessage ); @@ -730,6 +751,8 @@ class LokiPublicChannelAPI { return { timestamp, + attachments, + preview, quote, }; } @@ -789,7 +812,7 @@ class LokiPublicChannelAPI { return; } - const { timestamp, quote } = messengerData; + const { timestamp, quote, attachments, preview } = messengerData; if (!timestamp) { return; // Invalid message } @@ -836,7 +859,7 @@ class LokiPublicChannelAPI { isPublic: true, message: { body: adnMessage.text, - attachments: [], + attachments, group: { id: this.conversationId, type: textsecure.protobuf.GroupContext.Type.DELIVER, @@ -849,7 +872,7 @@ class LokiPublicChannelAPI { sent_at: timestamp, quote, contact: [], - preview: [], + preview, profile: { displayName: from, }, @@ -956,6 +979,8 @@ class LokiPublicChannelAPI { timestamp: messageTimeStamp, }, }, + ...attachmentAnnotations, + ...previewAnnotations, ], }; if (quote && quote.id) { @@ -988,6 +1013,8 @@ class LokiPublicChannelAPI { const sigData = LokiPublicChannelAPI.getSigData( sigVer, payload.annotations[0].value, + attachmentAnnotations.map(anno => anno.value), + previewAnnotations.map(anno => anno.value), mockAdnMessage ); const sig = await libsignal.Curve.async.calculateSignature( diff --git a/js/modules/loki_file_server_api.js b/js/modules/loki_file_server_api.js index 06e062aa5..e6f1ec518 100644 --- a/js/modules/loki_file_server_api.js +++ b/js/modules/loki_file_server_api.js @@ -2,8 +2,6 @@ const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); -/* global log */ - const DEVICE_MAPPING_ANNOTATION_KEY = 'network.loki.messenger.devicemapping'; class LokiFileServerAPI { diff --git a/js/modules/loki_message_api.js b/js/modules/loki_message_api.js index 2c52ea4a3..d3f10a9f1 100644 --- a/js/modules/loki_message_api.js +++ b/js/modules/loki_message_api.js @@ -93,11 +93,7 @@ class LokiMessageAPI { 'Missing public send data for public chat message' ); } - const res = await publicSendData.sendMessage( - data.body, - data.quote, - messageTimeStamp - ); + const res = await publicSendData.sendMessage(data, messageTimeStamp); if (res === false) { throw new window.textsecure.PublicChatError( 'Failed to send public chat message' diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index 4ce31886d..613954c56 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -168,7 +168,7 @@ MessageSender.prototype = { constructor: MessageSender, // makeAttachmentPointer :: Attachment -> Promise AttachmentPointerProto - makeAttachmentPointer(attachment) { + async makeAttachmentPointer(attachment, publicServer = null) { if (typeof attachment !== 'object' || attachment == null) { return Promise.resolve(undefined); } @@ -185,42 +185,49 @@ MessageSender.prototype = { } const proto = new textsecure.protobuf.AttachmentPointer(); - proto.key = libsignal.crypto.getRandomBytes(64); - - const iv = libsignal.crypto.getRandomBytes(16); - return textsecure.crypto - .encryptAttachment(attachment.data, proto.key, iv) - .then(result => - this.server - .putAttachment(result.ciphertext) - .then(async ({ url, id }) => { - proto.id = id; - proto.url = url; - proto.contentType = attachment.contentType; - proto.digest = result.digest; - - if (attachment.size) { - proto.size = attachment.size; - } - if (attachment.fileName) { - proto.fileName = attachment.fileName; - } - if (attachment.flags) { - proto.flags = attachment.flags; - } - if (attachment.width) { - proto.width = attachment.width; - } - if (attachment.height) { - proto.height = attachment.height; - } - if (attachment.caption) { - proto.caption = attachment.caption; - } - - return proto; - }) + let attachmentData; + let server; + if (publicServer) { + attachmentData = attachment.data; + server = publicServer; + } else { + proto.key = libsignal.crypto.getRandomBytes(64); + const iv = libsignal.crypto.getRandomBytes(16); + const result = await textsecure.crypto.encryptAttachment( + attachment.data, + proto.key, + iv ); + proto.digest = result.digest; + attachmentData = result.ciphertext; + ({ server } = this); + } + + const { url, id } = await server.putAttachment(attachmentData); + proto.id = id; + proto.url = url; + proto.contentType = attachment.contentType; + + if (attachment.size) { + proto.size = attachment.size; + } + if (attachment.fileName) { + proto.fileName = attachment.fileName; + } + if (attachment.flags) { + proto.flags = attachment.flags; + } + if (attachment.width) { + proto.width = attachment.width; + } + if (attachment.height) { + proto.height = attachment.height; + } + if (attachment.caption) { + proto.caption = attachment.caption; + } + + return proto; }, queueJobForNumber(number, runJob) { @@ -243,9 +250,11 @@ MessageSender.prototype = { }); }, - uploadAttachments(message) { + uploadAttachments(message, publicServer) { return Promise.all( - message.attachments.map(this.makeAttachmentPointer.bind(this)) + message.attachments.map(attachment => + this.makeAttachmentPointer(attachment, publicServer) + ) ) .then(attachmentPointers => { // eslint-disable-next-line no-param-reassign @@ -260,12 +269,12 @@ MessageSender.prototype = { }); }, - async uploadLinkPreviews(message) { + async uploadLinkPreviews(message, publicServer) { try { const preview = await Promise.all( (message.preview || []).map(async item => ({ ...item, - image: await this.makeAttachmentPointer(item.image), + image: await this.makeAttachmentPointer(item.image, publicServer), })) ); // eslint-disable-next-line no-param-reassign @@ -279,7 +288,7 @@ MessageSender.prototype = { } }, - uploadThumbnails(message) { + uploadThumbnails(message, publicServer) { const makePointer = this.makeAttachmentPointer.bind(this); const { quote } = message; @@ -294,7 +303,7 @@ MessageSender.prototype = { return null; } - return makePointer(thumbnail).then(pointer => { + return makePointer(thumbnail, publicServer).then(pointer => { // eslint-disable-next-line no-param-reassign attachment.attachmentPointer = pointer; }); @@ -311,11 +320,13 @@ MessageSender.prototype = { sendMessage(attrs, options) { const message = new Message(attrs); const silent = false; + const publicServer = + options.publicSendData && options.publicSendData.serverAPI; return Promise.all([ - this.uploadAttachments(message), - this.uploadThumbnails(message), - this.uploadLinkPreviews(message), + this.uploadAttachments(message, publicServer), + this.uploadThumbnails(message, publicServer), + this.uploadLinkPreviews(message, publicServer), ]).then( () => new Promise((resolve, reject) => {