rebuild-ui
							parent
							
								
									4b517ffd92
								
							
						
					
					
						commit
						95c5a881e1
					
				@ -1,535 +0,0 @@
 | 
			
		||||
/* global
 | 
			
		||||
  textsecure,
 | 
			
		||||
  libsignal,
 | 
			
		||||
  window,
 | 
			
		||||
  libloki,
 | 
			
		||||
  StringView,
 | 
			
		||||
  lokiMessageAPI,
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* eslint-disable more/no-then */
 | 
			
		||||
/* eslint-disable no-unreachable */
 | 
			
		||||
const NUM_SEND_CONNECTIONS = 3;
 | 
			
		||||
 | 
			
		||||
const getTTLForType = type => {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case 'pairing-request':
 | 
			
		||||
      return window.libsession.Constants.TTL_DEFAULT.PAIRING_REQUEST;
 | 
			
		||||
    case 'device-unpairing':
 | 
			
		||||
      return window.libsession.Constants.TTL_DEFAULT.DEVICE_UNPAIRING;
 | 
			
		||||
    case 'onlineBroadcast':
 | 
			
		||||
      return window.libsession.Constants.TTL_DEFAULT.ONLINE_BROADCAST;
 | 
			
		||||
    default:
 | 
			
		||||
      return window.libsession.Constants.TTL_DEFAULT.REGULAR_MESSAGE;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _getPaddedMessageLength(messageLength) {
 | 
			
		||||
  const messageLengthWithTerminator = messageLength + 1;
 | 
			
		||||
  let messagePartCount = Math.floor(messageLengthWithTerminator / 160);
 | 
			
		||||
 | 
			
		||||
  if (messageLengthWithTerminator % 160 !== 0) {
 | 
			
		||||
    messagePartCount += 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return messagePartCount * 160;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _convertMessageToText(messageBuffer) {
 | 
			
		||||
  const plaintext = new Uint8Array(
 | 
			
		||||
    _getPaddedMessageLength(messageBuffer.byteLength + 1) - 1
 | 
			
		||||
  );
 | 
			
		||||
  plaintext.set(new Uint8Array(messageBuffer));
 | 
			
		||||
  plaintext[messageBuffer.byteLength] = 0x80;
 | 
			
		||||
 | 
			
		||||
  return plaintext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getPlaintext(messageBuffer) {
 | 
			
		||||
  return _convertMessageToText(messageBuffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function wrapInWebsocketMessage(outgoingObject, timestamp) {
 | 
			
		||||
  const source =
 | 
			
		||||
    outgoingObject.type ===
 | 
			
		||||
    textsecure.protobuf.Envelope.Type.UNIDENTIFIED_SENDER
 | 
			
		||||
      ? null
 | 
			
		||||
      : outgoingObject.ourKey;
 | 
			
		||||
 | 
			
		||||
  const messageEnvelope = new textsecure.protobuf.Envelope({
 | 
			
		||||
    type: outgoingObject.type,
 | 
			
		||||
    source,
 | 
			
		||||
    sourceDevice: outgoingObject.sourceDevice,
 | 
			
		||||
    timestamp,
 | 
			
		||||
    content: outgoingObject.content,
 | 
			
		||||
  });
 | 
			
		||||
  const requestMessage = new textsecure.protobuf.WebSocketRequestMessage({
 | 
			
		||||
    id: new Uint8Array(libsignal.crypto.getRandomBytes(1))[0], // random ID for now
 | 
			
		||||
    verb: 'PUT',
 | 
			
		||||
    path: '/api/v1/message',
 | 
			
		||||
    body: messageEnvelope.encode().toArrayBuffer(),
 | 
			
		||||
  });
 | 
			
		||||
  const websocketMessage = new textsecure.protobuf.WebSocketMessage({
 | 
			
		||||
    type: textsecure.protobuf.WebSocketMessage.Type.REQUEST,
 | 
			
		||||
    request: requestMessage,
 | 
			
		||||
  });
 | 
			
		||||
  const bytes = new Uint8Array(websocketMessage.encode().toArrayBuffer());
 | 
			
		||||
  return bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getStaleDeviceIdsForNumber(number) {
 | 
			
		||||
  return textsecure.storage.protocol.getDeviceIds(number).then(deviceIds => {
 | 
			
		||||
    if (deviceIds.length === 0) {
 | 
			
		||||
      return [1];
 | 
			
		||||
    }
 | 
			
		||||
    const updateDevices = [];
 | 
			
		||||
    return Promise.all(
 | 
			
		||||
      deviceIds.map(deviceId => {
 | 
			
		||||
        const address = new libsignal.SignalProtocolAddress(number, deviceId);
 | 
			
		||||
        const sessionCipher = new libsignal.SessionCipher(
 | 
			
		||||
          textsecure.storage.protocol,
 | 
			
		||||
          address
 | 
			
		||||
        );
 | 
			
		||||
        return sessionCipher.hasOpenSession().then(hasSession => {
 | 
			
		||||
          if (!hasSession) {
 | 
			
		||||
            updateDevices.push(deviceId);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      })
 | 
			
		||||
    ).then(() => updateDevices);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function OutgoingMessage(
 | 
			
		||||
  server,
 | 
			
		||||
  timestamp,
 | 
			
		||||
  numbers,
 | 
			
		||||
  message,
 | 
			
		||||
  silent,
 | 
			
		||||
  callback,
 | 
			
		||||
  options = {}
 | 
			
		||||
) {
 | 
			
		||||
  if (message instanceof textsecure.protobuf.DataMessage) {
 | 
			
		||||
    const content = new textsecure.protobuf.Content();
 | 
			
		||||
    content.dataMessage = message;
 | 
			
		||||
    // eslint-disable-next-line no-param-reassign
 | 
			
		||||
    message = content;
 | 
			
		||||
  }
 | 
			
		||||
  this.server = server;
 | 
			
		||||
  this.timestamp = timestamp;
 | 
			
		||||
  this.numbers = numbers;
 | 
			
		||||
  this.message = message; // ContentMessage proto
 | 
			
		||||
  this.callback = callback;
 | 
			
		||||
  this.silent = silent;
 | 
			
		||||
 | 
			
		||||
  this.numbersCompleted = 0;
 | 
			
		||||
  this.errors = [];
 | 
			
		||||
  this.successfulNumbers = [];
 | 
			
		||||
  this.fallBackEncryption = false;
 | 
			
		||||
  this.failoverNumbers = [];
 | 
			
		||||
  this.unidentifiedDeliveries = [];
 | 
			
		||||
 | 
			
		||||
  const {
 | 
			
		||||
    numberInfo,
 | 
			
		||||
    senderCertificate,
 | 
			
		||||
    online,
 | 
			
		||||
    messageType,
 | 
			
		||||
    isPublic,
 | 
			
		||||
    isMediumGroup,
 | 
			
		||||
    publicSendData,
 | 
			
		||||
    autoSession,
 | 
			
		||||
  } = options || {};
 | 
			
		||||
  this.numberInfo = numberInfo;
 | 
			
		||||
  this.isPublic = isPublic;
 | 
			
		||||
  this.isMediumGroup = !!isMediumGroup;
 | 
			
		||||
  this.isGroup = !!(
 | 
			
		||||
    this.message &&
 | 
			
		||||
    this.message.dataMessage &&
 | 
			
		||||
    this.message.dataMessage.group
 | 
			
		||||
  );
 | 
			
		||||
  this.publicSendData = publicSendData;
 | 
			
		||||
  this.senderCertificate = senderCertificate;
 | 
			
		||||
  this.online = online;
 | 
			
		||||
  this.messageType = messageType || 'outgoing';
 | 
			
		||||
  this.autoSession = autoSession || false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OutgoingMessage.prototype = {
 | 
			
		||||
  constructor: OutgoingMessage,
 | 
			
		||||
  numberCompleted() {
 | 
			
		||||
    this.numbersCompleted += 1;
 | 
			
		||||
    if (this.numbersCompleted >= this.numbers.length) {
 | 
			
		||||
      this.callback({
 | 
			
		||||
        successfulNumbers: this.successfulNumbers,
 | 
			
		||||
        failoverNumbers: this.failoverNumbers,
 | 
			
		||||
        errors: this.errors,
 | 
			
		||||
        unidentifiedDeliveries: this.unidentifiedDeliveries,
 | 
			
		||||
        messageType: this.messageType,
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  registerError(number, reason, error) {
 | 
			
		||||
    if (!error || (error.name === 'HTTPError' && error.code !== 404)) {
 | 
			
		||||
      // eslint-disable-next-line no-param-reassign
 | 
			
		||||
      error = new textsecure.OutgoingMessageError(
 | 
			
		||||
        number,
 | 
			
		||||
        this.message.toArrayBuffer(),
 | 
			
		||||
        this.timestamp,
 | 
			
		||||
        error
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // eslint-disable-next-line no-param-reassign
 | 
			
		||||
    error.number = number;
 | 
			
		||||
    // eslint-disable-next-line no-param-reassign
 | 
			
		||||
    error.reason = reason;
 | 
			
		||||
    this.errors[this.errors.length] = error;
 | 
			
		||||
    this.numberCompleted();
 | 
			
		||||
  },
 | 
			
		||||
  reloadDevicesAndSend(primaryPubKey, multiDevice = true) {
 | 
			
		||||
    const ourNumber = textsecure.storage.user.getNumber();
 | 
			
		||||
 | 
			
		||||
    if (!multiDevice) {
 | 
			
		||||
      if (primaryPubKey === ourNumber) {
 | 
			
		||||
        return Promise.resolve();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return this.doSendMessage(primaryPubKey, [primaryPubKey]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      window.libsession.Protocols.MultiDeviceProtocol.getAllDevices(
 | 
			
		||||
        primaryPubKey
 | 
			
		||||
      )
 | 
			
		||||
        // Don't send to ourselves
 | 
			
		||||
        .then(devicesPubKeys =>
 | 
			
		||||
          devicesPubKeys.filter(pubKey => pubKey.key !== ourNumber)
 | 
			
		||||
        )
 | 
			
		||||
        .then(devicesPubKeys => {
 | 
			
		||||
          if (devicesPubKeys.length === 0) {
 | 
			
		||||
            // No need to start the sending of message without a recipient
 | 
			
		||||
            return Promise.resolve();
 | 
			
		||||
          }
 | 
			
		||||
          return this.doSendMessage(primaryPubKey, devicesPubKeys);
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  getKeysForNumber(number, updateDevices) {
 | 
			
		||||
    const handleResult = response =>
 | 
			
		||||
      Promise.all(
 | 
			
		||||
        response.devices.map(device => {
 | 
			
		||||
          // eslint-disable-next-line no-param-reassign
 | 
			
		||||
          device.identityKey = response.identityKey;
 | 
			
		||||
          if (
 | 
			
		||||
            updateDevices === undefined ||
 | 
			
		||||
            updateDevices.indexOf(device.deviceId) > -1
 | 
			
		||||
          ) {
 | 
			
		||||
            const address = new libsignal.SignalProtocolAddress(
 | 
			
		||||
              number,
 | 
			
		||||
              device.deviceId
 | 
			
		||||
            );
 | 
			
		||||
            const builder = new libsignal.SessionBuilder(
 | 
			
		||||
              textsecure.storage.protocol,
 | 
			
		||||
              address
 | 
			
		||||
            );
 | 
			
		||||
            if (device.registrationId === 0) {
 | 
			
		||||
              window.log.info('device registrationId 0!');
 | 
			
		||||
            }
 | 
			
		||||
            return builder
 | 
			
		||||
              .processPreKey(device)
 | 
			
		||||
              .then(async () => {
 | 
			
		||||
                // TODO: only remove the keys that were used above!
 | 
			
		||||
                await libloki.storage.removeContactPreKeyBundle(number);
 | 
			
		||||
                return true;
 | 
			
		||||
              })
 | 
			
		||||
              .catch(error => {
 | 
			
		||||
                if (error.message === 'Identity key changed') {
 | 
			
		||||
                  // eslint-disable-next-line no-param-reassign
 | 
			
		||||
                  error.timestamp = this.timestamp;
 | 
			
		||||
                  // eslint-disable-next-line no-param-reassign
 | 
			
		||||
                  error.originalMessage = this.message.toArrayBuffer();
 | 
			
		||||
                  // eslint-disable-next-line no-param-reassign
 | 
			
		||||
                  error.identityKey = device.identityKey;
 | 
			
		||||
                }
 | 
			
		||||
                throw error;
 | 
			
		||||
              });
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return false;
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    let promise = Promise.resolve(true);
 | 
			
		||||
    updateDevices.forEach(device => {
 | 
			
		||||
      promise = promise.then(() =>
 | 
			
		||||
        Promise.all([
 | 
			
		||||
          textsecure.storage.protocol.loadContactPreKey(number),
 | 
			
		||||
          textsecure.storage.protocol.loadContactSignedPreKey(number),
 | 
			
		||||
        ])
 | 
			
		||||
          .then(keys => {
 | 
			
		||||
            const [preKey, signedPreKey] = keys;
 | 
			
		||||
            if (preKey === undefined || signedPreKey === undefined) {
 | 
			
		||||
              return false;
 | 
			
		||||
            }
 | 
			
		||||
            const identityKey = StringView.hexToArrayBuffer(number);
 | 
			
		||||
            return handleResult({
 | 
			
		||||
              identityKey,
 | 
			
		||||
              devices: [
 | 
			
		||||
                { deviceId: device, preKey, signedPreKey, registrationId: 0 },
 | 
			
		||||
              ],
 | 
			
		||||
            }).then(results => results.every(value => value === true));
 | 
			
		||||
          })
 | 
			
		||||
          .catch(e => {
 | 
			
		||||
            throw e;
 | 
			
		||||
          })
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return promise;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  // Default ttl to 24 hours if no value provided
 | 
			
		||||
  async transmitMessage(number, data, timestamp, ttl = 24 * 60 * 60 * 1000) {
 | 
			
		||||
    const pubKey = number;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      // TODO: Make NUM_CONCURRENT_CONNECTIONS a global constant
 | 
			
		||||
      const options = {
 | 
			
		||||
        numConnections: NUM_SEND_CONNECTIONS,
 | 
			
		||||
      };
 | 
			
		||||
      options.isPublic = this.isPublic;
 | 
			
		||||
      if (this.isPublic) {
 | 
			
		||||
        options.publicSendData = this.publicSendData;
 | 
			
		||||
      }
 | 
			
		||||
      await lokiMessageAPI.sendMessage(pubKey, data, timestamp, ttl, options);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (e.name === 'HTTPError' && e.code !== 409 && e.code !== 410) {
 | 
			
		||||
        throw new textsecure.SendMessageNetworkError(number, '', e, timestamp);
 | 
			
		||||
      } else if (e.name === 'TimedOutError') {
 | 
			
		||||
        throw new textsecure.PoWError(number, e);
 | 
			
		||||
      }
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  async buildMessage(devicePubKey) {
 | 
			
		||||
    const updatedDevices = await getStaleDeviceIdsForNumber(devicePubKey);
 | 
			
		||||
    const keysFound = await this.getKeysForNumber(devicePubKey, updatedDevices);
 | 
			
		||||
 | 
			
		||||
    // Check if we need to attach the preKeys
 | 
			
		||||
    const enableFallBackEncryption = !keysFound;
 | 
			
		||||
    const flags = this.message.dataMessage
 | 
			
		||||
      ? this.message.dataMessage.get_flags()
 | 
			
		||||
      : null;
 | 
			
		||||
    // END_SESSION means Session reset message
 | 
			
		||||
    const isEndSession =
 | 
			
		||||
      flags === textsecure.protobuf.DataMessage.Flags.END_SESSION;
 | 
			
		||||
    const isSessionRequest = false;
 | 
			
		||||
 | 
			
		||||
    if (enableFallBackEncryption || isEndSession) {
 | 
			
		||||
      // Encrypt them with the fallback
 | 
			
		||||
      const pkb = await libloki.storage.getPreKeyBundleForContact(devicePubKey);
 | 
			
		||||
      this.message.preKeyBundleMessage = new textsecure.protobuf.PreKeyBundleMessage(
 | 
			
		||||
        pkb
 | 
			
		||||
      );
 | 
			
		||||
      window.log.info('attaching prekeys to outgoing message');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const messageBuffer = this.message.toArrayBuffer();
 | 
			
		||||
    const logDetails = {
 | 
			
		||||
      message: this.message,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const ourPubKey = textsecure.storage.user.getNumber();
 | 
			
		||||
    const ourPrimaryPubkey = window.storage.get('primaryDevicePubKey');
 | 
			
		||||
    const secondaryPubKeys =
 | 
			
		||||
      (await window.libsession.Protocols.MultiDeviceProtocol.getSecondaryDevices(
 | 
			
		||||
        ourPubKey
 | 
			
		||||
      )) || [];
 | 
			
		||||
    let aliasedPubkey = devicePubKey;
 | 
			
		||||
    if (devicePubKey === ourPubKey) {
 | 
			
		||||
      aliasedPubkey = 'OUR_PUBKEY'; // should not happen
 | 
			
		||||
    } else if (devicePubKey === ourPrimaryPubkey) {
 | 
			
		||||
      aliasedPubkey = 'OUR_PRIMARY_PUBKEY';
 | 
			
		||||
    } else if (secondaryPubKeys.some(device => device.key === devicePubKey)) {
 | 
			
		||||
      aliasedPubkey = 'OUR SECONDARY PUBKEY';
 | 
			
		||||
    }
 | 
			
		||||
    libloki.api.debug.logSessionMessageSending(
 | 
			
		||||
      `Sending :${this.messageType} message to ${aliasedPubkey} details:`,
 | 
			
		||||
      logDetails
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const plaintext = _getPlaintext(messageBuffer);
 | 
			
		||||
 | 
			
		||||
    // No limit on message keys if we're communicating with our other devices
 | 
			
		||||
    // FIXME options not used at all; if (ourPubkey === number) {
 | 
			
		||||
    //   options.messageKeysLimit = false;
 | 
			
		||||
    // }
 | 
			
		||||
    const ttl = getTTLForType(this.messageType);
 | 
			
		||||
    const ourKey = textsecure.storage.user.getNumber();
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      ttl,
 | 
			
		||||
      ourKey,
 | 
			
		||||
      sourceDevice: 1,
 | 
			
		||||
      plaintext,
 | 
			
		||||
      pubKey: devicePubKey,
 | 
			
		||||
      isSessionRequest,
 | 
			
		||||
      enableFallBackEncryption,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  async encryptMessage(clearMessage) {
 | 
			
		||||
    if (clearMessage === null) {
 | 
			
		||||
      window.log.warn(
 | 
			
		||||
        'clearMessage is null on encryptMessage... Returning null'
 | 
			
		||||
      );
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    const {
 | 
			
		||||
      ttl,
 | 
			
		||||
      ourKey,
 | 
			
		||||
      sourceDevice,
 | 
			
		||||
      plaintext,
 | 
			
		||||
      pubKey,
 | 
			
		||||
      isSessionRequest,
 | 
			
		||||
      enableFallBackEncryption,
 | 
			
		||||
    } = clearMessage;
 | 
			
		||||
    // Session doesn't use the deviceId scheme, it's always 1.
 | 
			
		||||
    // Instead, there are multiple device public keys.
 | 
			
		||||
    const deviceId = 1;
 | 
			
		||||
 | 
			
		||||
    const address = new libsignal.SignalProtocolAddress(pubKey, deviceId);
 | 
			
		||||
 | 
			
		||||
    let sessionCipher;
 | 
			
		||||
 | 
			
		||||
    if (enableFallBackEncryption) {
 | 
			
		||||
      sessionCipher = new libloki.crypto.FallBackSessionCipher(address);
 | 
			
		||||
    } else {
 | 
			
		||||
      sessionCipher = new libsignal.SessionCipher(
 | 
			
		||||
        textsecure.storage.protocol,
 | 
			
		||||
        address
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const innerCiphertext = await sessionCipher.encrypt(plaintext);
 | 
			
		||||
 | 
			
		||||
    const secretSessionCipher = new window.Signal.Metadata.SecretSessionCipher(
 | 
			
		||||
      textsecure.storage.protocol
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const senderCert = new textsecure.protobuf.SenderCertificate();
 | 
			
		||||
 | 
			
		||||
    senderCert.sender = ourKey;
 | 
			
		||||
    senderCert.senderDevice = deviceId;
 | 
			
		||||
 | 
			
		||||
    const ciphertext = await secretSessionCipher.encrypt(
 | 
			
		||||
      address.getName(),
 | 
			
		||||
      senderCert,
 | 
			
		||||
      innerCiphertext
 | 
			
		||||
    );
 | 
			
		||||
    const type = textsecure.protobuf.Envelope.Type.UNIDENTIFIED_SENDER;
 | 
			
		||||
    const content = window.Signal.Crypto.arrayBufferToBase64(ciphertext);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      type,
 | 
			
		||||
      ttl,
 | 
			
		||||
      ourKey,
 | 
			
		||||
      sourceDevice,
 | 
			
		||||
      content,
 | 
			
		||||
      pubKey,
 | 
			
		||||
      isSessionRequest,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  // Send a message to a public group
 | 
			
		||||
  async sendPublicMessage(number) {
 | 
			
		||||
    await this.transmitMessage(
 | 
			
		||||
      number,
 | 
			
		||||
      this.message.dataMessage,
 | 
			
		||||
      this.timestamp,
 | 
			
		||||
      0 // ttl
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    this.successfulNumbers[this.successfulNumbers.length] = number;
 | 
			
		||||
    this.numberCompleted();
 | 
			
		||||
  },
 | 
			
		||||
  // Send a message to a private group member or a session chat (one to one)
 | 
			
		||||
  async sendSessionMessage(outgoingObjects) {
 | 
			
		||||
    // TODO: handle multiple devices/messages per transmit
 | 
			
		||||
    const promises = outgoingObjects.map(async outgoingObject => {
 | 
			
		||||
      if (!outgoingObject) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      const { pubKey: destination, ttl } = outgoingObject;
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        const socketMessage = wrapInWebsocketMessage(
 | 
			
		||||
          outgoingObject,
 | 
			
		||||
          this.timestamp
 | 
			
		||||
        );
 | 
			
		||||
        await this.transmitMessage(
 | 
			
		||||
          destination,
 | 
			
		||||
          socketMessage,
 | 
			
		||||
          this.timestamp,
 | 
			
		||||
          ttl
 | 
			
		||||
        );
 | 
			
		||||
        this.successfulNumbers.push(destination);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        e.number = destination;
 | 
			
		||||
        this.errors.push(e);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
    this.numbersCompleted += this.successfulNumbers.length;
 | 
			
		||||
    this.numberCompleted();
 | 
			
		||||
  },
 | 
			
		||||
  async buildAndEncrypt(devicePubKey) {
 | 
			
		||||
    const clearMessage = await this.buildMessage(devicePubKey);
 | 
			
		||||
    return this.encryptMessage(clearMessage);
 | 
			
		||||
  },
 | 
			
		||||
  // eslint-disable-next-line no-unused-vars
 | 
			
		||||
  async doSendMessage(primaryPubKey, devicesPubKeys) {
 | 
			
		||||
    if (this.isPublic) {
 | 
			
		||||
      await this.sendPublicMessage(primaryPubKey);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.numbers = devicesPubKeys;
 | 
			
		||||
 | 
			
		||||
    if (this.isMediumGroup) {
 | 
			
		||||
      await this.sendMediumGroupMessage(primaryPubKey);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const outgoingObjects = await Promise.all(
 | 
			
		||||
      devicesPubKeys.map(pk => this.buildAndEncrypt(pk, primaryPubKey))
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    this.sendSessionMessage(outgoingObjects);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  sendToNumber(number, multiDevice = true) {
 | 
			
		||||
    return this.reloadDevicesAndSend(number, multiDevice).catch(error => {
 | 
			
		||||
      if (error.message === 'Identity key changed') {
 | 
			
		||||
        // eslint-disable-next-line no-param-reassign
 | 
			
		||||
        error = new textsecure.OutgoingIdentityKeyError(
 | 
			
		||||
          number,
 | 
			
		||||
          error.originalMessage,
 | 
			
		||||
          error.timestamp,
 | 
			
		||||
          error.identityKey
 | 
			
		||||
        );
 | 
			
		||||
        this.registerError(number, 'Identity key changed', error);
 | 
			
		||||
      } else {
 | 
			
		||||
        this.registerError(
 | 
			
		||||
          number,
 | 
			
		||||
          `Failed to retrieve new device keys for number ${number}`,
 | 
			
		||||
          error
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
window.textsecure = window.textsecure || {};
 | 
			
		||||
window.textsecure.OutgoingMessage = OutgoingMessage;
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue