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.
		
		
		
		
		
			
		
			
				
	
	
		
			131 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			131 lines
		
	
	
		
			4.1 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,
 | 
						|
      publicSendData = null,
 | 
						|
    } = options;
 | 
						|
    // Data required to identify a message in a conversation
 | 
						|
    const messageEventData = {
 | 
						|
      pubKey,
 | 
						|
      timestamp: messageTimeStamp,
 | 
						|
    };
 | 
						|
 | 
						|
    if (isPublic) {
 | 
						|
      if (!publicSendData) {
 | 
						|
        throw new window.textsecure.PublicChatError(
 | 
						|
          'Missing public send data for public chat message'
 | 
						|
        );
 | 
						|
      }
 | 
						|
      const res = await publicSendData.sendMessage(data, messageTimeStamp);
 | 
						|
      if (res === false) {
 | 
						|
        throw new window.textsecure.PublicChatError(
 | 
						|
          'Failed to send public chat message'
 | 
						|
        );
 | 
						|
      }
 | 
						|
      messageEventData.serverId = res.serverId;
 | 
						|
      messageEventData.serverTimestamp = res.serverTimestamp;
 | 
						|
      window.Whisper.events.trigger('publicMessageSent', messageEventData);
 | 
						|
      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;
 |