Identd + SSL fixes
[KiwiIRC.git] / server / client.js
CommitLineData
f3dbbd91
D
1var 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,
d99e7823
D
7 ClientCommands = require('./clientcommands.js'),
8 WebsocketRpc = require('./websocketrpc.js');
a8bf3ea4 9
772a4bb6 10
a8bf3ea4 11var Client = function (websocket) {
772a4bb6 12 var that = this;
d99e7823 13
a8bf3ea4
JA
14 events.EventEmitter.call(this);
15 this.websocket = websocket;
d99e7823 16 this.rpc = new WebsocketRpc(this.websocket);
26322e8f
D
17
18 // Clients address
a0bf87be 19 this.real_address = this.websocket.meta.real_address;
26322e8f
D
20
21 // A hash to identify this client instance
ad01b1a7
D
22 this.hash = crypto.createHash('sha256')
23 .update(this.real_address)
24 .update('' + Date.now())
25 .update(Math.floor(Math.random() * 100000).toString())
26 .digest('hex');
d99e7823 27
76ff1063 28 this.state = new State(this);
d99e7823 29
a8bf3ea4
JA
30 this.buffer = {
31 list: [],
32 motd: ''
33 };
d99e7823 34
f3dbbd91 35 // Handler for any commands sent from the client
2a8e95d1 36 this.client_commands = new ClientCommands(this);
f3dbbd91 37
d99e7823
D
38 this.rpc.on('irc', function (response, data) {
39 handleClientMessage.call(that, data, response);
a8bf3ea4 40 });
d99e7823
D
41 this.rpc.on('kiwi', function (response, data) {
42 kiwiCommand.call(that, data, response);
a8bf3ea4 43 });
d99e7823 44 websocket.on('close', function () {
772a4bb6 45 websocketDisconnect.apply(that, arguments);
a8bf3ea4
JA
46 });
47 websocket.on('error', function () {
772a4bb6 48 websocketError.apply(that, arguments);
a8bf3ea4 49 });
7d1ad2df
JA
50
51 this.disposed = false;
a8bf3ea4
JA
52};
53util.inherits(Client, events.EventEmitter);
54
55module.exports.Client = Client;
56
57// Callback API:
58// Callbacks SHALL accept 2 arguments, error and response, in that order.
59// error MUST be null where the command is successul.
60// error MUST otherwise be a truthy value and SHOULD be a string where the cause of the error is known.
61// response MAY be given even if error is truthy
62
2f1e8a71 63Client.prototype.sendIrcCommand = function (command, data, callback) {
a8bf3ea4 64 var c = {command: command, data: data};
d99e7823 65 this.rpc.call('irc', c, callback);
a8bf3ea4
JA
66};
67
d2d91c10
D
68Client.prototype.sendKiwiCommand = function (command, data, callback) {
69 var c = {command: command, data: data};
d99e7823 70 this.rpc.call('kiwi', c, callback);
a8bf3ea4
JA
71};
72
c08717da 73Client.prototype.dispose = function () {
7d1ad2df 74 this.disposed = true;
0fe7c000 75 this.rpc.dispose();
5befab98 76 this.emit('dispose');
c08717da
D
77 this.removeAllListeners();
78};
79
772a4bb6 80function handleClientMessage(msg, callback) {
6c7b2006
D
81 var that = this,
82 server;
f3dbbd91
D
83
84 // Make sure we have a server number specified
85 if ((msg.server === null) || (typeof msg.server !== 'number')) {
40af34d4 86 return (typeof callback === 'function') ? callback('server not specified') : undefined;
76ff1063 87 } else if (!this.state.irc_connections[msg.server]) {
40af34d4 88 return (typeof callback === 'function') ? callback('not connected to server') : undefined;
a8bf3ea4 89 }
f3dbbd91
D
90
91 // The server this command is directed to
76ff1063 92 server = this.state.irc_connections[msg.server];
f3dbbd91
D
93
94 if (typeof callback !== 'function') {
95 callback = null;
40ce1c01 96 }
f3dbbd91
D
97
98 try {
99 msg.data = JSON.parse(msg.data);
100 } catch (e) {
101 kiwi.log('[handleClientMessage] JSON parsing error ' + msg.data);
102 return;
a8bf3ea4 103 }
f3dbbd91
D
104
105 // Run the client command
6c7b2006
D
106 global.modules.emit('client command', {
107 command: msg.data,
108 server: server
109 })
110 .done(function() {
111 that.client_commands.run(msg.data.method, msg.data.args, server, callback);
112 });
d2d91c10 113}
a8bf3ea4 114
f3dbbd91
D
115
116
117
772a4bb6 118function kiwiCommand(command, callback) {
a8bf3ea4
JA
119 if (typeof callback !== 'function') {
120 callback = function () {};
121 }
760fa05a 122
a8bf3ea4 123 switch (command.command) {
93e84f75
D
124 case 'connect':
125 if (command.hostname && command.port && command.nick) {
cc54c0a1
D
126 var options = {};
127
128 // Get any optional parameters that may have been passed
129 if (command.encoding)
130 options.encoding = command.encoding;
131
132 options.password = global.config.restrict_server_password || command.password;
93e84f75 133
c3204224
PV
134 this.state.connect(
135 (global.config.restrict_server || command.hostname),
136 (global.config.restrict_server_port || command.port),
52b27d90
D
137 (typeof global.config.restrict_server_ssl !== 'undefined' ?
138 global.config.restrict_server_ssl :
139 command.ssl),
c3204224 140 command.nick,
a0bf87be 141 {hostname: this.websocket.meta.revdns, address: this.websocket.meta.real_address},
cc54c0a1 142 options,
c3204224 143 callback);
93e84f75
D
144 } else {
145 return callback('Hostname, port and nickname must be specified');
146 }
147 break;
148 default:
149 callback();
a8bf3ea4 150 }
c08717da 151}
a8bf3ea4 152
a8bf3ea4 153
772a4bb6
D
154// Websocket has disconnected, so quit all the IRC connections
155function websocketDisconnect() {
76ff1063 156 this.emit('disconnect');
d99e7823 157
e6b973da 158 this.dispose();
c08717da 159}
a8bf3ea4 160
772a4bb6
D
161
162// TODO: Should this close all the websocket connections too?
163function websocketError() {
c36ed4eb 164 this.dispose();
c3204224 165}