client/socket/events.js

module.exports = function (JIFFClient) {
  /**
   * Initialize socket listeners and events
   * @memberof module:jiff-client.JIFFClient
   * @method
   */
  JIFFClient.prototype.initSocket = function () {
    var jiffClient = this;

    // set on('connect') handler once!
    this.socket.on('connect', jiffClient.handlers.connected);

    // Store the id when server sends it back
    this.socket.on('initialization', jiffClient.handlers.initialized);

    // Public keys were updated on the server, and it sent us the updates
    jiffClient.socket.on('public_keys', function (msg, callback) {
      callback(true);

      msg = JSON.parse(msg);
      msg = jiffClient.hooks.execute_array_hooks('afterOperation', [jiffClient, 'public_keys', msg], 2);

      jiffClient.handlers.store_public_keys(msg.public_keys);
    });

    // Setup receiving matching shares
    this.socket.on('share', function (msg, callback) {
      callback(true); // send ack to server

      // parse message
      var json_msg = JSON.parse(msg);
      var sender_id = json_msg['party_id'];

      if (jiffClient.keymap[sender_id] != null) {
        jiffClient.handlers.receive_share(json_msg);
      } else {
        if (jiffClient.messagesWaitingKeys[sender_id] == null) {
          jiffClient.messagesWaitingKeys[sender_id] = [];
        }
        jiffClient.messagesWaitingKeys[sender_id].push({label: 'share', msg: json_msg});
      }
    });

    this.socket.on('open', function (msg, callback) {
      callback(true); // send ack to server

      // parse message
      var json_msg = JSON.parse(msg);
      var sender_id = json_msg['party_id'];

      if (jiffClient.keymap[sender_id] != null) {
        jiffClient.handlers.receive_open(json_msg);
      } else {
        if (jiffClient.messagesWaitingKeys[sender_id] == null) {
          jiffClient.messagesWaitingKeys[sender_id] = [];
        }
        jiffClient.messagesWaitingKeys[sender_id].push({label: 'open', msg: json_msg});
      }
    });

    // handle custom messages
    this.socket.on('custom', function (msg, callback) {
      callback(true); // send ack to server

      var json_msg = JSON.parse(msg);
      var sender_id = json_msg['party_id'];
      var encrypted = json_msg['encrypted'];

      if (jiffClient.keymap[sender_id] != null || encrypted !== true) {
        jiffClient.handlers.receive_custom(json_msg);
      } else {
        // key must not exist yet for sender_id, and encrypted must be true
        if (jiffClient.messagesWaitingKeys[sender_id] == null) {
          jiffClient.messagesWaitingKeys[sender_id] = [];
        }
        jiffClient.messagesWaitingKeys[sender_id].push({label: 'custom', msg: json_msg});
      }
    });

    this.socket.on('crypto_provider', function (msg, callback) {
      callback(true); // send ack to server
      jiffClient.handlers.receive_crypto_provider(JSON.parse(msg));
    });

    this.socket.on('error', function (msg) {
      try {
        msg = JSON.parse(msg);
        jiffClient.handlers.error(msg['label'], msg['error']);
      } catch (error) {
        jiffClient.handlers.error('socket.io', msg);
      }
    });

    this.socket.on('disconnect', function (reason) {
      if (reason !== 'io client disconnect') {
        // check that the reason is an error and not a user initiated disconnect
        console.log('Disconnected!', jiffClient.id, reason);
      }

      jiffClient.hooks.execute_array_hooks('afterOperation', [jiffClient, 'disconnect', reason], -1);
    });
  };

  /**
   * Executes all callbacks for which the wait condition has been satisfied.
   * Remove all executed callbacks so that they would not be executed in the future.
   * @memberof module:jiff-client.JIFFClient
   * @method
   */
  JIFFClient.prototype.execute_wait_callbacks = function () {
    var copy_callbacks = this.wait_callbacks;
    this.wait_callbacks = [];
    for (var i = 0; i < copy_callbacks.length; i++) {
      var wait = copy_callbacks[i];
      var parties = wait.parties;
      var callback = wait.callback;
      var initialization = wait.initialization;

      // Check if the parties to wait for are now known
      var parties_satisfied = this.__initialized || !initialization;
      for (var j = 0; j < parties.length; j++) {
        var party_id = parties[j];
        if (this.keymap == null || this.keymap[party_id] == null) {
          parties_satisfied = false;
          break;
        }
      }

      if (parties_satisfied) {
        callback(this);
      } else {
        this.wait_callbacks.push(wait);
      }
    }
  };

  /**
   * Resolves all messages that were pending because their senders primary key was previously unknown.
   * These messages are decrypted and verified and handled appropriatly before being removed from the wait queue.
   * @memberof module:jiff-client.JIFFClient
   * @method
   */
  JIFFClient.prototype.resolve_messages_waiting_for_keys = function () {
    for (var party_id in this.keymap) {
      if (!this.keymap.hasOwnProperty(party_id)) {
        continue;
      }

      var messageQueue = this.messagesWaitingKeys[party_id];
      if (messageQueue == null) {
        continue;
      }
      for (var i = 0; i < messageQueue.length; i++) {
        var msg = messageQueue[i];
        if (msg.label === 'share') {
          this.handlers.receive_share(msg.msg);
        } else if (msg.label === 'open') {
          this.handlers.receive_open(msg.msg);
        } else if (msg.label === 'custom') {
          this.handlers.receive_custom(msg.msg);
        } else {
          throw new Error('Error resolving pending message: unknown label ' + msg.label);
        }
      }

      this.messagesWaitingKeys[party_id] = null;
    }
  };
};