Commit | Line | Data |
---|---|---|
f3dbbd91 D |
1 | var 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 | 11 | var 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 | }; | |
47 | util.inherits(Client, events.EventEmitter); | |
48 | ||
49 | module.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 | 57 | Client.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 |
62 | Client.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 | 67 | Client.prototype.dispose = function () { |
e6b973da | 68 | this.emit('destroy'); |
c08717da D |
69 | this.removeAllListeners(); |
70 | }; | |
71 | ||
772a4bb6 | 72 | function 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 | 103 | function 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 |
147 | function 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? | |
161 | function websocketError() { | |
c36ed4eb | 162 | this.dispose(); |
c08717da | 163 | } |