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