Source: core/SocketServer.js

const path      = require('path');
const fs        = require('fs-extra');
const socketIO  = require('socket.io');

const Utils     = require('../Utils.js');
const Client    = require('../Client.js');
const Msgs      = require('./Msgs.js');

/** Handles socket connections */
class SocketServer {

    constructor(jt) {
        this.io = socketIO(jt.staticServer.server);
        this.jt = jt;
        this.ADMIN_TYPE = 'ADMIN';
        this.msgs         = new Msgs.new(jt); // MESSAGES TO LISTEN FOR FROM CLIENTS
        jt.io = this.io;
        this.io.on('connection', this.onConnection.bind(this));
    }

    /**
     * onConnection - called when a socket connects to the server.
     *
     * @param  {Socket} socket description
     */
    onConnection(socket) {

        var id          = socket.request._query.id; // participant or admin ID
        var pwd         = socket.request._query.pwd;
        var type        = socket.request._query.type; // ADMIN or PARTI
        var sessionId   = socket.request._query.sessionId;
        var roomId      = socket.request._query.roomId;

        if (id === null || id === undefined || id === '') {
            id = socket.request.connection._peername.address;
            id = id.substring(id.lastIndexOf(':')+1);
            console.log('new id = ' + id);
        }

        var admin = this.jt.data.getAdmin(id, pwd);

        this.jt.log('socket connection: ' + socket.id + ', id=' + id + ', pwd=' + pwd + ', session=' + sessionId);

        if (type === this.ADMIN_TYPE && (admin !== null || this.jt.settings.adminLoginReq === false)) {
            this.addAdminClient(socket);
        } else if (roomId !== 'null') {
            this.addRoomClient(socket, id, roomId);
        } else {
            this.addParticipantClient(socket, id, sessionId, roomId);
        }
    }

    addAdminClient(socket) {
        var sock = socket;
        var self = this;
        var log = this.jt.log;
        log('socket connection to ' + this.ADMIN_TYPE);
        socket.join(this.ADMIN_TYPE);
        socket.join('socket_' + sock.id);

        // syc.connect(socket);

        var functionList = Object.getOwnPropertyNames(Object.getPrototypeOf(this.msgs));
        for (var i in functionList) {
            var fnI = functionList[i];
            (function(fnI) {
                socket.on(fnI, function(d, cb) {
                    try {
                        log('received message ' + fnI + ': ' + JSON.stringify(d));
                        eval('self.msgs.' + fnI + "(d, sock)");
                        if (cb != null) {
                            cb(true);
                        }
                    } catch (err) {
                        console.log("Error: " + err + "\n" + err.stack);
                        if (cb != null) {
                            cb(false);
                        }
                    }
                });
            })(fnI);
        }

        socket.on('refreshAdmin', function(msg) {
            log('Server.refreshAdmin: socket_' + sock.id);
            self.refreshAdmin(null, 'socket_' + sock.id, msg.userId);
        });

        socket.on('reloadApps', function(msg) {
            self.jt.data.reloadApps();
            if (msg == null) {
                msg = {
                    userId: ''
                }
            }
            self.refreshAdmin(null, 'socket_' + sock.id, msg.userId);
        });

        socket.on('get-var', function(a) {
            log('getting variable ' + a + ': ' + global[a]);
        });

        socket.on('get-app', function(id) {
            var toSend = self.jt.data.apps[id].shell();
            self.io.to(sock.id).emit('get-app', toSend);
        });

        socket.on('session-set-active', function(msg) {
            var session = self.jt.data.getSession(msg.sId);
            session.setActive(msg.active);
        });

        socket.on('refresh-apps', function(msg) {
            self.jt.data.apps = self.jt.data.loadApps();
        });

        socket.on('add-app-folder', function(folder) {
            self.data.addAppFolder(folder);
        });

    }

    addRoomClient(socket, pId, roomId) {
        this.jt.log('server.addRoomClient: ' + socket.id + ', pId=' + pId + ', roomId=' + roomId);
        try {
            var room = this.jt.data.room(roomId);
            room.addClient(socket, pId);
        } catch (err) {
            this.jt.log('server.addRoomClient error:' + err + '\n' + err.stack);
        }

    }

    addParticipantClient(socket, pId, sessionId, roomId) {
        this.jt.log('server.addClient: ' + socket.id + ', pId=' + pId + ', sId=' + sessionId + ', roomId=' + roomId);
        try {
            var session = this.jt.data.getActiveSession(sessionId);
            if (session === null) {
                session = this.jt.data.getMostRecentActiveSession();
            }

            let client = null;
            if (session != null) {
                // Add client to session (automatic notification of admins + client).
                client = session.addClient(socket, pId);
            } else {
                // No session, so notify manually.
                client = new Client.new(socket, null);
                client.pId = pId;
                this.sendOrQueueAdminMsg(null, 'addClient', client.shell());
                let participant = {
                    id: pId,
                    session: {
                        id: 'none'
                    }
                };
                socket.emit('logged-in', participant);
                let socketServer = this;
                socket.on('disconnect', function() {
                    console.log('disconnect for ' + JSON.stringify(client.shell()));
                    socketServer.sendOrQueueAdminMsg(null, 'removeClient', client.shell());
                    Utils.deleteById(socketServer.jt.data.participantClients, client.id);
                });
            }

            // If no fixed session ID, add to list of clients to reset.
            if ((session == null || session.id !== sessionId) && client !== null) {
                this.jt.data.participantClients.push(client);
            }

        } catch (err) {
            this.jt.log('server.addClient error:' + err + '\n' + err.stack);
        }
    }

    adminsRefreshApps() {
        this.io.to(this.ADMIN_TYPE).emit('refresh-apps', system.apps);
    }

    refreshAdmin(msgs, id, userId) {
        this.jt.log('refreshAdmin: ' + id + ', userId = ' + userId);

        var ag = {};
        ag.appFolders           = this.jt.settings.appFolders;
        ag.apps                 = this.jt.data.appsMetaData;
        ag.appsFull             = Utils.shells(this.jt.data.apps);
        ag.predefinedQueues     = this.jt.settings.predefinedQueues;
        ag.rooms                = Utils.shells(this.jt.data.rooms);
        ag.queues                = Utils.shells(this.jt.data.queues);
        ag.sessions             = this.jt.data.sessionsForUser(userId);
        ag.jtreeLocalPath       = this.jt.path;
        ag.settings             = this.jt.settings.shell();
        ag.users                = Utils.shells(this.jt.data.users);

        this.sendOrQueueAdminMsg(msgs, 'refreshAdmin', ag, id);
    }

    // If msgs is null, sends the message immediately.
    // Otherwise, the messages is just added to msgs.
    sendOrQueueAdminMsg(msgs, msgName, msgData, channel) {
        if (msgs == null || msgs == undefined) {
            if (channel == null || channel == undefined) {
                channel = this.ADMIN_TYPE;
            }
            this.io.to(channel).emit(msgName, msgData);
        } else {
            // TODO: add channel information
            msgs.push(new jt.Message(msgName, msgData));
        }
    }

    emitMessages(channel, msgs) {
        this.io.to(channel).emit('messages', msgs);
    }

    refreshAdmins(msgs) {
        this.refreshAdmin(msgs, this.ADMIN_TYPE, null);
    }

    emitToAdmins(msgName, msgData) {
        this.io.to(this.ADMIN_TYPE).emit(msgName, msgData);
    }

}

var exports = module.exports = {};
exports.new = SocketServer;