Client: Default theme set to 'relaxed'
[KiwiIRC.git] / server / client.js
CommitLineData
f3dbbd91
D
1var util = require('util'),
2 events = require('events'),
c36ed4eb 3 crypto = require('crypto'),
44197e59 4 _ = require('underscore'),
2a8e95d1
D
5 IrcConnection = require('./irc/connection.js').IrcConnection,
6 IrcCommands = require('./irc/commands.js'),
c36ed4eb 7 ClientCommands = require('./clientcommands.js');
a8bf3ea4 8
772a4bb6 9
a8bf3ea4 10var Client = function (websocket) {
772a4bb6 11 var that = this;
a8bf3ea4
JA
12
13 events.EventEmitter.call(this);
14 this.websocket = websocket;
26322e8f
D
15
16 // Clients address
c36ed4eb 17 this.real_address = this.websocket.handshake.real_address;
26322e8f
D
18
19 // A hash to identify this client instance
ad01b1a7
D
20 this.hash = crypto.createHash('sha256')
21 .update(this.real_address)
22 .update('' + Date.now())
23 .update(Math.floor(Math.random() * 100000).toString())
24 .digest('hex');
a8bf3ea4 25
772a4bb6 26 this.irc_connections = [];
a8bf3ea4
JA
27 this.next_connection = 0;
28
29 this.buffer = {
30 list: [],
31 motd: ''
32 };
33
f3dbbd91 34 // Handler for any commands sent from the client
2a8e95d1 35 this.client_commands = new ClientCommands(this);
f3dbbd91 36
a8bf3ea4 37 websocket.on('irc', function () {
772a4bb6 38 handleClientMessage.apply(that, arguments);
a8bf3ea4
JA
39 });
40 websocket.on('kiwi', function () {
772a4bb6 41 kiwiCommand.apply(that, arguments);
a8bf3ea4
JA
42 });
43 websocket.on('disconnect', function () {
772a4bb6 44 websocketDisconnect.apply(that, arguments);
a8bf3ea4
JA
45 });
46 websocket.on('error', function () {
772a4bb6 47 websocketError.apply(that, arguments);
a8bf3ea4
JA
48 });
49};
50util.inherits(Client, events.EventEmitter);
51
52module.exports.Client = Client;
53
54// Callback API:
55// Callbacks SHALL accept 2 arguments, error and response, in that order.
56// error MUST be null where the command is successul.
57// error MUST otherwise be a truthy value and SHOULD be a string where the cause of the error is known.
58// response MAY be given even if error is truthy
59
2f1e8a71 60Client.prototype.sendIrcCommand = function (command, data, callback) {
a8bf3ea4 61 var c = {command: command, data: data};
a8bf3ea4
JA
62 this.websocket.emit('irc', c, callback);
63};
64
d2d91c10
D
65Client.prototype.sendKiwiCommand = function (command, data, callback) {
66 var c = {command: command, data: data};
67 this.websocket.emit('kiwi', c, callback);
a8bf3ea4
JA
68};
69
c08717da 70Client.prototype.dispose = function () {
e6b973da 71 this.emit('destroy');
c08717da
D
72 this.removeAllListeners();
73};
74
772a4bb6 75function handleClientMessage(msg, callback) {
f3dbbd91
D
76 var server, args, obj, channels, keys;
77
78 // Make sure we have a server number specified
79 if ((msg.server === null) || (typeof msg.server !== 'number')) {
40af34d4 80 return (typeof callback === 'function') ? callback('server not specified') : undefined;
772a4bb6 81 } else if (!this.irc_connections[msg.server]) {
40af34d4 82 return (typeof callback === 'function') ? callback('not connected to server') : undefined;
a8bf3ea4 83 }
f3dbbd91
D
84
85 // The server this command is directed to
772a4bb6 86 server = this.irc_connections[msg.server];
f3dbbd91
D
87
88 if (typeof callback !== 'function') {
89 callback = null;
40ce1c01 90 }
f3dbbd91
D
91
92 try {
93 msg.data = JSON.parse(msg.data);
94 } catch (e) {
95 kiwi.log('[handleClientMessage] JSON parsing error ' + msg.data);
96 return;
a8bf3ea4 97 }
f3dbbd91
D
98
99 // Run the client command
100 this.client_commands.run(msg.data.method, msg.data.args, server, callback);
d2d91c10 101}
a8bf3ea4 102
f3dbbd91
D
103
104
105
772a4bb6 106function kiwiCommand(command, callback) {
b8e4d9f7 107 var that = this;
772a4bb6 108
a8bf3ea4
JA
109 if (typeof callback !== 'function') {
110 callback = function () {};
111 }
112 switch (command.command) {
113 case 'connect':
2a8e95d1 114 if (command.hostname && command.port && command.nick) {
772a4bb6 115 var con = new IrcConnection(command.hostname, command.port, command.ssl,
ec67171b 116 command.nick, {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address},
15fefff7 117 command.password);
a8bf3ea4
JA
118
119 var con_num = this.next_connection++;
772a4bb6 120 this.irc_connections[con_num] = con;
a8bf3ea4 121
2a8e95d1
D
122 var irc_commands = new IrcCommands(con, con_num, this);
123 irc_commands.bindEvents();
a8bf3ea4
JA
124
125 con.on('connected', function () {
a8bf3ea4
JA
126 return callback(null, con_num);
127 });
128
129 con.on('error', function (err) {
ad01b1a7 130 console.log('irc_connection error (' + command.hostname + '):', err);
d2d91c10
D
131 // TODO: Once multiple servers implemented, specify which server failed
132 //that.sendKiwiCommand('error', {server: con_num, error: err});
133 return callback(err.code, null);
a8bf3ea4 134 });
b8e4d9f7
JA
135
136 con.on('close', function () {
772a4bb6 137 that.irc_connections[con_num] = null;
b8e4d9f7 138 });
a8bf3ea4
JA
139 } else {
140 return callback('Hostname, port and nickname must be specified');
141 }
142 break;
143 default:
144 callback();
145 }
c08717da 146}
a8bf3ea4 147
a8bf3ea4 148
772a4bb6
D
149// Websocket has disconnected, so quit all the IRC connections
150function websocketDisconnect() {
151 _.each(this.irc_connections, function (irc_connection, i, cons) {
b8e4d9f7 152 if (irc_connection) {
b737610b 153 irc_connection.end('QUIT :' + (global.config.quit_message || ''));
c08717da 154 irc_connection.dispose();
b8e4d9f7
JA
155 cons[i] = null;
156 }
157 });
e6b973da
D
158
159 this.dispose();
c08717da 160}
a8bf3ea4 161
772a4bb6
D
162
163// TODO: Should this close all the websocket connections too?
164function websocketError() {
c36ed4eb 165 this.dispose();
c08717da 166}