UNPKG

@jsxc/jsxc

Version:

Real-time XMPP chat application with video calls, file transfer and encrypted communication

194 lines (149 loc) 6.09 kB
import Account from '../../Account'; import PersistentMap from '../../util/PersistentMap'; import Log from '../../util/Log'; import JID from '../../JID'; import * as ConnectHelper from './ConnectHelper'; import StorageConnection from '../storage/Connection'; import XMPPConnection from './Connection'; import { Strophe } from '../../vendor/Strophe'; import BaseError from '../../errors/BaseError'; export enum TYPE { BOSH, WEBSOCKET, } export default class Connector { private connectionParameters; private connectionArgs: string[]; constructor(account: Account, url: string, jid: string, sid: string, rid: string); constructor(account: Account, url: string, jid: string, password: string); constructor(account: Account); constructor(private account: Account, ...connectionArgs: string[]) { let storage = account.getStorage(); this.connectionParameters = new PersistentMap(storage, 'connection'); connectionArgs = connectionArgs.filter(arg => typeof arg === 'string'); if (connectionArgs.length < 3) { let type = this.connectionParameters.get('type'); if (type === TYPE.WEBSOCKET) { throw new Error('Cannt attach to websocket connection.'); } this.connectionArgs = [ this.connectionParameters.get('url'), this.connectionParameters.get('jid'), this.connectionParameters.get('sid'), this.connectionParameters.get('rid'), ]; } else if (connectionArgs.length === 3 || connectionArgs.length === 4) { this.connectionArgs = connectionArgs; let type = /^wss?:/.test(connectionArgs[1]) ? TYPE.WEBSOCKET : TYPE.BOSH; this.connectionParameters.set('type', type); this.connectionParameters.remove('inactivity'); this.connectionParameters.remove('timestamp'); } else { throw new BaseError('Unsupported number of arguments'); } } public connect() { let inactivity = this.connectionParameters.get('inactivity'); let timestamp = this.connectionParameters.get('timestamp'); let isConnectionExpired = inactivity && timestamp && new Date().getTime() - timestamp > inactivity; if (isConnectionExpired) { Log.debug( `Inactivity: ${inactivity}, Last timestamp: ${timestamp}, Time diff: ${new Date().getTime() - timestamp}` ); Log.warn('Credentials expired'); this.account.triggerConnectionHook(Strophe.Status.CONNTIMEOUT); this.account.triggerConnectionHook(Strophe.Status.DISCONNECTED, 'timeout'); throw new BaseError('Credentials expired'); } return ConnectHelper.login.apply(this, this.connectionArgs).then(this.successfulConnected); } public getJID(): JID { return new JID(this.connectionParameters.get('jid')); } public getUrl(): string { return this.connectionParameters.get('url'); } public getPassword(): string { if (this.connectionArgs.length === 3) { return this.connectionArgs[2]; } } public clearPassword() { if (this.connectionArgs.length === 3) { delete this.connectionArgs[2]; } } private successfulConnected = data => { let stropheConnection = data.connection; let status = data.status; let condition = data.condition; this.storeConnectionParameters(stropheConnection); this.replaceConnectionHandler(stropheConnection); this.addRidHandler(stropheConnection); this.addRidUnloadHandler(stropheConnection); let accountConnection = this.replaceStorageConnectionWithXMPPConnection(stropheConnection); if (stropheConnection.features) { this.storeConnectionFeatures(stropheConnection); } Log.debug('XMPP connection ready'); this.account.triggerConnectionHook(status, condition); return [status, accountConnection]; }; private storeConnectionParameters(connection) { this.connectionParameters.set({ url: connection.service, jid: connection.jid, sid: connection._proto.sid, rid: connection._proto.rid, timestamp: new Date().getTime(), }); if (connection._proto.inactivity) { let inactivity = connection._proto.inactivity * 1000; this.connectionParameters.set('inactivity', inactivity); } } private replaceConnectionHandler(connection) { connection.connect_callback = (status, condition) => { this.account.triggerConnectionHook(status, condition); if (status === Strophe.Status.DISCONNECTED) { this.account.connectionDisconnected(); } }; } private addRidHandler(connection) { connection.nextValidRid = rid => { let timestamp = new Date().getTime(); this.connectionParameters.set('timestamp', timestamp); this.connectionParameters.set('rid', rid); }; } private addRidUnloadHandler(connection) { $(window).on('unload', () => { connection.nextValidRid(connection._proto.rid); }); } private replaceStorageConnectionWithXMPPConnection(stropheConnection) { let accountConnection = this.account.getConnection(); let handlers = (<StorageConnection>accountConnection).getHandlers(); accountConnection.close(); accountConnection = new XMPPConnection(this.account, stropheConnection); for (let handler of handlers) { accountConnection.registerHandler.apply(accountConnection, handler); } return accountConnection; } private storeConnectionFeatures(connection) { let from = new JID('', connection.domain, ''); let stanza = connection.features; if (!stanza) { return; } let capsElement = stanza.querySelector('c'); if (!capsElement) { return; } let ver = capsElement.getAttribute('ver'); let discoInfoRepository = this.account.getDiscoInfoRepository(); discoInfoRepository.addRelation(from, ver); } }