Emit 'dispose' on a dispose call instead of 'destroy'
[KiwiIRC.git] / server / client.js
1 var util = require('util'),
2 events = require('events'),
3 crypto = require('crypto'),
4 _ = require('lodash'),
5 State = require('./irc/state.js');
6 IrcConnection = require('./irc/connection.js').IrcConnection,
7 IrcCommands = require('./irc/commands.js'),
8 ClientCommands = require('./clientcommands.js');
9
10
11 var Client = function (websocket) {
12 var that = this;
13
14 events.EventEmitter.call(this);
15 this.websocket = websocket;
16
17 // Clients address
18 this.real_address = this.websocket.handshake.real_address;
19
20 // A hash to identify this client instance
21 this.hash = crypto.createHash('sha256')
22 .update(this.real_address)
23 .update('' + Date.now())
24 .update(Math.floor(Math.random() * 100000).toString())
25 .digest('hex');
26
27 this.state = new State(this);
28
29 this.buffer = {
30 list: [],
31 motd: ''
32 };
33
34 // Handler for any commands sent from the client
35 this.client_commands = new ClientCommands(this);
36
37 websocket.on('irc', function () {
38 handleClientMessage.apply(that, arguments);
39 });
40 websocket.on('kiwi', function () {
41 kiwiCommand.apply(that, arguments);
42 });
43 websocket.on('disconnect', function () {
44 websocketDisconnect.apply(that, arguments);
45 });
46 websocket.on('error', function () {
47 websocketError.apply(that, arguments);
48 });
49 };
50 util.inherits(Client, events.EventEmitter);
51
52 module.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
60 Client.prototype.sendIrcCommand = function (command, data, callback) {
61 var c = {command: command, data: data};
62 this.websocket.emit('irc', c, callback);
63 };
64
65 Client.prototype.sendKiwiCommand = function (command, data, callback) {
66 var c = {command: command, data: data};
67 this.websocket.emit('kiwi', c, callback);
68 };
69
70 Client.prototype.dispose = function () {
71 this.emit('dispose');
72 this.removeAllListeners();
73 };
74
75 function handleClientMessage(msg, callback) {
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')) {
80 return (typeof callback === 'function') ? callback('server not specified') : undefined;
81 } else if (!this.state.irc_connections[msg.server]) {
82 return (typeof callback === 'function') ? callback('not connected to server') : undefined;
83 }
84
85 // The server this command is directed to
86 server = this.state.irc_connections[msg.server];
87
88 if (typeof callback !== 'function') {
89 callback = null;
90 }
91
92 try {
93 msg.data = JSON.parse(msg.data);
94 } catch (e) {
95 kiwi.log('[handleClientMessage] JSON parsing error ' + msg.data);
96 return;
97 }
98
99 // Run the client command
100 this.client_commands.run(msg.data.method, msg.data.args, server, callback);
101 }
102
103
104
105
106 function kiwiCommand(command, callback) {
107 var that = this;
108
109 if (typeof callback !== 'function') {
110 callback = function () {};
111 }
112 switch (command.command) {
113 case 'connect':
114 if (command.hostname && command.port && command.nick) {
115 var con;
116
117 if (global.config.restrict_server) {
118 this.state.connect(
119 global.config.restrict_server,
120 global.config.restrict_server_port,
121 global.config.restrict_server_ssl,
122 command.nick,
123 {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address},
124 global.config.restrict_server_password,
125 callback);
126
127 } else {
128 this.state.connect(
129 command.hostname,
130 command.port,
131 command.ssl,
132 command.nick,
133 {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address},
134 command.password,
135 callback);
136 }
137 } else {
138 return callback('Hostname, port and nickname must be specified');
139 }
140 break;
141 default:
142 callback();
143 }
144 }
145
146
147 // Websocket has disconnected, so quit all the IRC connections
148 function websocketDisconnect() {
149 this.emit('disconnect');
150
151 this.dispose();
152 }
153
154
155 // TODO: Should this close all the websocket connections too?
156 function websocketError() {
157 this.dispose();
158 }