diff --git a/js/modules/loki_app_dot_net_api.js b/js/modules/loki_app_dot_net_api.js index a7174f948..c4945fd76 100644 --- a/js/modules/loki_app_dot_net_api.js +++ b/js/modules/loki_app_dot_net_api.js @@ -5,6 +5,7 @@ const nodeFetch = require('node-fetch'); const { URL, URLSearchParams } = require('url'); const FormData = require('form-data'); const https = require('https'); +const path = require('path'); // Can't be less than 1200 if we have unauth'd requests const PUBLICCHAT_MSG_POLL_EVERY = 1.5 * 1000; // 1.5s @@ -627,6 +628,12 @@ class LokiAppDotNetServerAPI { url ); } + if (mode === '_sendToProxy') { + // if we can detect, certain types of failures, we can retry... + if (e.code === 'ECONNRESET') { + // retry with counter? + } + } return { err: e, }; @@ -1248,38 +1255,50 @@ class LokiPublicChannelAPI { this.conversation.setGroupName(note.value.name); } if (note.value && note.value.avatar) { - const avatarAbsUrl = this.serverAPI.baseServerUrl + note.value.avatar; - const { - writeNewAttachmentData, - deleteAttachmentData, - } = window.Signal.Migrations; - // do we already have this image? no, then - - // download a copy and save it - const imageData = await nodeFetch(avatarAbsUrl); - // eslint-disable-next-line no-inner-declarations - function toArrayBuffer(buf) { - const ab = new ArrayBuffer(buf.length); - const view = new Uint8Array(ab); - // eslint-disable-next-line no-plusplus - for (let i = 0; i < buf.length; i++) { - view[i] = buf[i]; + if (note.value.avatar.match(/^images\//)) { + // local file avatar + const resolvedAvatar = path.normalize(note.value.avatar); + const base = path.normalize('images/'); + const re = new RegExp(`^${base}`); + // do we at least ends up inside images/ somewhere? + if (re.test(resolvedAvatar)) { + this.conversation.set('avatar', resolvedAvatar); } - return ab; - } - // eslint-enable-next-line no-inner-declarations - - const buffer = await imageData.buffer(); - const newAttributes = await window.Signal.Types.Conversation.maybeUpdateAvatar( - this.conversation.attributes, - toArrayBuffer(buffer), - { + } else { + // relative URL avatar + const avatarAbsUrl = this.serverAPI.baseServerUrl + note.value.avatar; + const { writeNewAttachmentData, deleteAttachmentData, + } = window.Signal.Migrations; + // do we already have this image? no, then + + // download a copy and save it + const imageData = await nodeFetch(avatarAbsUrl); + // eslint-disable-next-line no-inner-declarations + function toArrayBuffer(buf) { + const ab = new ArrayBuffer(buf.length); + const view = new Uint8Array(ab); + // eslint-disable-next-line no-plusplus + for (let i = 0; i < buf.length; i++) { + view[i] = buf[i]; + } + return ab; } - ); - // update group - this.conversation.set('avatar', newAttributes.avatar); + // eslint-enable-next-line no-inner-declarations + + const buffer = await imageData.buffer(); + const newAttributes = await window.Signal.Types.Conversation.maybeUpdateAvatar( + this.conversation.attributes, + toArrayBuffer(buffer), + { + writeNewAttachmentData, + deleteAttachmentData, + } + ); + // update group + this.conversation.set('avatar', newAttributes.avatar); + } } // is it mutable? // who are the moderators? @@ -1414,7 +1433,7 @@ class LokiPublicChannelAPI { } if (quote) { - // TODO: Enable quote attachments again using proper ADN style + // Disable quote attachments quote.attachments = []; } @@ -1518,6 +1537,14 @@ class LokiPublicChannelAPI { }); if (res.err || !res.response) { + log.error( + 'Could not get messages from', + this.serverAPI.baseServerUrl, + this.baseChannelUrl + ); + if (res.err) { + log.error('pollOnceForMessages receive error', res.err); + } return; } @@ -1705,18 +1732,31 @@ class LokiPublicChannelAPI { // filter out invalid messages pendingMessages = pendingMessages.filter(messageData => !!messageData); // separate messages coming from primary and secondary devices - const [primaryMessages, slaveMessages] = _.partition( + let [primaryMessages, slaveMessages] = _.partition( pendingMessages, message => !(message.source in slavePrimaryMap) ); - // process primary devices' message directly - primaryMessages.forEach(message => - this.chatAPI.emit('publicMessage', { - message, - }) - ); - - pendingMessages = []; // allow memory to be freed + // get minimum ID for primaryMessages and slaveMessages + const firstPrimaryId = _.min(primaryMessages.map(msg => msg.serverId)); + const firstSlaveId = _.min(slaveMessages.map(msg => msg.serverId)); + if (firstPrimaryId < firstSlaveId) { + // early send + // split off count from pendingMessages + let sendNow = []; + [sendNow, pendingMessages] = _.partition( + pendingMessages, + message => message.serverId < firstSlaveId + ); + sendNow.forEach(message => { + // send them out now + log.info('emitting primary message', message.serverId); + this.chatAPI.emit('publicMessage', { + message, + }); + }); + sendNow = false; + } + primaryMessages = false; // free memory // get actual chat server data (mainly the name rn) of primary device const verifiedDeviceResults = await this.serverAPI.getUsers( @@ -1773,11 +1813,25 @@ class LokiPublicChannelAPI { messageData.message.profileKey = profileKey; } } - /* eslint-enable no-param-reassign */ + }); + slaveMessages = false; // free memory + + // process all messages in the order received + pendingMessages.forEach(message => { + // if slave device + if (message.source in slavePrimaryMap) { + // prevent our own device sent messages from coming back in + if (message.source === ourNumberDevice) { + // we originally sent these + return; + } + } + log.info('emitting pending message', message.serverId); this.chatAPI.emit('publicMessage', { - message: messageData, + message, }); }); + /* eslint-enable no-param-reassign */ // if we received one of our own messages