Commit | Line | Data |
---|---|---|
f3dbbd91 D |
1 | var util = require('util'), |
2 | events = require('events'), | |
c36ed4eb | 3 | crypto = require('crypto'), |
f9ff7686 | 4 | _ = require('lodash'), |
ce6a4656 | 5 | State = require('./irc/state.js'), |
2a8e95d1 | 6 | IrcConnection = require('./irc/connection.js').IrcConnection, |
c36ed4eb | 7 | ClientCommands = require('./clientcommands.js'); |
a8bf3ea4 | 8 | |
772a4bb6 | 9 | |
a8bf3ea4 | 10 | var 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 | |
76ff1063 | 26 | this.state = new State(this); |
a8bf3ea4 JA |
27 | |
28 | this.buffer = { | |
29 | list: [], | |
30 | motd: '' | |
31 | }; | |
32 | ||
f3dbbd91 | 33 | // Handler for any commands sent from the client |
2a8e95d1 | 34 | this.client_commands = new ClientCommands(this); |
f3dbbd91 | 35 | |
a8bf3ea4 | 36 | websocket.on('irc', function () { |
772a4bb6 | 37 | handleClientMessage.apply(that, arguments); |
a8bf3ea4 JA |
38 | }); |
39 | websocket.on('kiwi', function () { | |
772a4bb6 | 40 | kiwiCommand.apply(that, arguments); |
a8bf3ea4 JA |
41 | }); |
42 | websocket.on('disconnect', function () { | |
772a4bb6 | 43 | websocketDisconnect.apply(that, arguments); |
a8bf3ea4 JA |
44 | }); |
45 | websocket.on('error', function () { | |
772a4bb6 | 46 | websocketError.apply(that, arguments); |
a8bf3ea4 | 47 | }); |
7d1ad2df JA |
48 | |
49 | this.disposed = false; | |
a8bf3ea4 JA |
50 | }; |
51 | util.inherits(Client, events.EventEmitter); | |
52 | ||
53 | module.exports.Client = Client; | |
54 | ||
55 | // Callback API: | |
56 | // Callbacks SHALL accept 2 arguments, error and response, in that order. | |
57 | // error MUST be null where the command is successul. | |
58 | // error MUST otherwise be a truthy value and SHOULD be a string where the cause of the error is known. | |
59 | // response MAY be given even if error is truthy | |
60 | ||
2f1e8a71 | 61 | Client.prototype.sendIrcCommand = function (command, data, callback) { |
a8bf3ea4 | 62 | var c = {command: command, data: data}; |
a8bf3ea4 JA |
63 | this.websocket.emit('irc', c, callback); |
64 | }; | |
65 | ||
d2d91c10 D |
66 | Client.prototype.sendKiwiCommand = function (command, data, callback) { |
67 | var c = {command: command, data: data}; | |
68 | this.websocket.emit('kiwi', c, callback); | |
a8bf3ea4 JA |
69 | }; |
70 | ||
c08717da | 71 | Client.prototype.dispose = function () { |
7d1ad2df | 72 | this.disposed = true; |
5befab98 | 73 | this.emit('dispose'); |
c08717da D |
74 | this.removeAllListeners(); |
75 | }; | |
76 | ||
772a4bb6 | 77 | function handleClientMessage(msg, callback) { |
760fa05a | 78 | var server; |
f3dbbd91 D |
79 | |
80 | // Make sure we have a server number specified | |
81 | if ((msg.server === null) || (typeof msg.server !== 'number')) { | |
40af34d4 | 82 | return (typeof callback === 'function') ? callback('server not specified') : undefined; |
76ff1063 | 83 | } else if (!this.state.irc_connections[msg.server]) { |
40af34d4 | 84 | return (typeof callback === 'function') ? callback('not connected to server') : undefined; |
a8bf3ea4 | 85 | } |
f3dbbd91 D |
86 | |
87 | // The server this command is directed to | |
76ff1063 | 88 | server = this.state.irc_connections[msg.server]; |
f3dbbd91 D |
89 | |
90 | if (typeof callback !== 'function') { | |
91 | callback = null; | |
40ce1c01 | 92 | } |
f3dbbd91 D |
93 | |
94 | try { | |
95 | msg.data = JSON.parse(msg.data); | |
96 | } catch (e) { | |
97 | kiwi.log('[handleClientMessage] JSON parsing error ' + msg.data); | |
98 | return; | |
a8bf3ea4 | 99 | } |
f3dbbd91 D |
100 | |
101 | // Run the client command | |
102 | this.client_commands.run(msg.data.method, msg.data.args, server, callback); | |
d2d91c10 | 103 | } |
a8bf3ea4 | 104 | |
f3dbbd91 D |
105 | |
106 | ||
107 | ||
772a4bb6 | 108 | function kiwiCommand(command, callback) { |
a8bf3ea4 JA |
109 | if (typeof callback !== 'function') { |
110 | callback = function () {}; | |
111 | } | |
760fa05a | 112 | |
a8bf3ea4 | 113 | switch (command.command) { |
93e84f75 D |
114 | case 'connect': |
115 | if (command.hostname && command.port && command.nick) { | |
116 | var con; | |
117 | ||
c3204224 PV |
118 | this.state.connect( |
119 | (global.config.restrict_server || command.hostname), | |
120 | (global.config.restrict_server_port || command.port), | |
52b27d90 D |
121 | (typeof global.config.restrict_server_ssl !== 'undefined' ? |
122 | global.config.restrict_server_ssl : | |
123 | command.ssl), | |
c3204224 PV |
124 | command.nick, |
125 | {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address}, | |
126 | (global.config.restrict_server_password || command.password), | |
127 | callback); | |
93e84f75 D |
128 | } else { |
129 | return callback('Hostname, port and nickname must be specified'); | |
130 | } | |
131 | break; | |
132 | default: | |
133 | callback(); | |
a8bf3ea4 | 134 | } |
c08717da | 135 | } |
a8bf3ea4 | 136 | |
a8bf3ea4 | 137 | |
772a4bb6 D |
138 | // Websocket has disconnected, so quit all the IRC connections |
139 | function websocketDisconnect() { | |
76ff1063 | 140 | this.emit('disconnect'); |
e6b973da D |
141 | |
142 | this.dispose(); | |
c08717da | 143 | } |
a8bf3ea4 | 144 | |
772a4bb6 D |
145 | |
146 | // TODO: Should this close all the websocket connections too? | |
147 | function websocketError() { | |
c36ed4eb | 148 | this.dispose(); |
c3204224 | 149 | } |