You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/js/modules/loki_message_api.js

111 lines
3.5 KiB
JavaScript

/* eslint-disable no-await-in-loop */
/* eslint-disable no-loop-func */
/* global log, dcodeIO, window, callWorker, textsecure */
const _ = require('lodash');
const primitives = require('./loki_primitives');
const DEFAULT_CONNECTIONS = 3;
const calcNonce = (messageEventData, pubKey, data64, timestamp, ttl) => {
const difficulty = window.storage.get('PoWDifficulty', null);
// Nonce is returned as a base64 string to include in header
window.Whisper.events.trigger('calculatingPoW', messageEventData);
return callWorker('calcPoW', timestamp, ttl, pubKey, data64, difficulty);
};
async function _openSendConnection(snode, params) {
// TODO: Revert back to using snode address instead of IP
const successfulSend = await window.NewSnodeAPI.storeOnNode(snode, params);
if (successfulSend) {
return snode;
}
// should we mark snode as bad if it can't store our message?
return false;
}
class LokiMessageAPI {
/**
* Refactor note: We should really clean this up ... it's very messy
*
* We need to split it into 2 sends:
* - Snodes
* - Open Groups
*
* Mikunj:
* Temporarily i've made it so `MessageSender` handles open group sends and calls this function for regular sends.
*/
async sendMessage(pubKey, data, messageTimeStamp, ttl, options = {}) {
const { isPublic = false, numConnections = DEFAULT_CONNECTIONS } = options;
// Data required to identify a message in a conversation
const messageEventData = {
pubKey,
timestamp: messageTimeStamp,
};
if (isPublic) {
window.log.warn('this sendMessage() should not be called anymore with an open group message');
return;
}
const data64 = dcodeIO.ByteBuffer.wrap(data).toString('base64');
const timestamp = Date.now();
const nonce = await calcNonce(
messageEventData,
window.getStoragePubKey(pubKey),
data64,
timestamp,
ttl
);
// Using timestamp as a unique identifier
const swarm = await window.SnodePool.getSnodesFor(pubKey);
// send parameters
const params = {
pubKey,
ttl: ttl.toString(),
nonce,
timestamp: timestamp.toString(),
data: data64,
};
const usedNodes = _.slice(swarm, 0, numConnections);
const promises = usedNodes.map(snode => _openSendConnection(snode, params));
let snode;
try {
// eslint-disable-next-line more/no-then
snode = await primitives.firstTrue(promises);
} catch (e) {
const snodeStr = snode ? `${snode.ip}:${snode.port}` : 'null';
log.warn(
`loki_message:::sendMessage - ${e.code} ${e.message} to ${pubKey} via snode:${snodeStr}`
);
if (e instanceof textsecure.WrongDifficultyError) {
// Force nonce recalculation
// NOTE: Currently if there are snodes with conflicting difficulties we
// will send the message twice (or more). Won't affect client side but snodes
// could store the same message multiple times because they will have different
// timestamps (and therefore nonces)
await this.sendMessage(pubKey, data, messageTimeStamp, ttl, options);
return;
}
throw e;
}
if (!snode) {
throw new window.textsecure.EmptySwarmError(pubKey, 'Ran out of swarm nodes to query');
} else {
log.info(
`loki_message:::sendMessage - Successfully stored message to ${pubKey} via ${snode.ip}:${snode.port}`
);
}
}
}
// These files are expected to be in commonjs so we can't use es6 syntax :(
// If we move these to TS then we should be able to use es6
module.exports = LokiMessageAPI;