Client: Encoding within the URL
[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 ClientCommands = require('./clientcommands.js');
8
9
10 var Client = function (websocket) {
11 var that = this;
12
13 events.EventEmitter.call(this);
14 this.websocket = websocket;
15
16 // Clients address
17 this.real_address = this.websocket.handshake.real_address;
18
19 // A hash to identify this client instance
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');
25
26 this.state = new State(this);
27
28 this.buffer = {
29 list: [],
30 motd: ''
31 };
32
33 // Handler for any commands sent from the client
34 this.client_commands = new ClientCommands(this);
35
36 websocket.on('irc', function () {
37 handleClientMessage.apply(that, arguments);
38 });
39 websocket.on('kiwi', function () {
40 kiwiCommand.apply(that, arguments);
41 });
42 websocket.on('disconnect', function () {
43 websocketDisconnect.apply(that, arguments);
44 });
45 websocket.on('error', function () {
46 websocketError.apply(that, arguments);
47 });
48
49 this.disposed = false;
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
61 Client.prototype.sendIrcCommand = function (command, data, callback) {
62 var c = {command: command, data: data};
63 this.websocket.emit('irc', c, callback);
64 };
65
66 Client.prototype.sendKiwiCommand = function (command, data, callback) {
67 var c = {command: command, data: data};
68 this.websocket.emit('kiwi', c, callback);
69 };
70
71 Client.prototype.dispose = function () {
72 this.disposed = true;
73 this.emit('dispose');
74 this.removeAllListeners();
75 };
76
77 function handleClientMessage(msg, callback) {
78 var server;
79
80 // Make sure we have a server number specified
81 if ((msg.server === null) || (typeof msg.server !== 'number')) {
82 return (typeof callback === 'function') ? callback('server not specified') : undefined;
83 } else if (!this.state.irc_connections[msg.server]) {
84 return (typeof callback === 'function') ? callback('not connected to server') : undefined;
85 }
86
87 // The server this command is directed to
88 server = this.state.irc_connections[msg.server];
89
90 if (typeof callback !== 'function') {
91 callback = null;
92 }
93
94 try {
95 msg.data = JSON.parse(msg.data);
96 } catch (e) {
97 kiwi.log('[handleClientMessage] JSON parsing error ' + msg.data);
98 return;
99 }
100
101 // Run the client command
102 this.client_commands.run(msg.data.method, msg.data.args, server, callback);
103 }
104
105
106
107
108 function kiwiCommand(command, callback) {
109 if (typeof callback !== 'function') {
110 callback = function () {};
111 }
112
113 switch (command.command) {
114 case 'connect':
115 if (command.hostname && command.port && command.nick) {
116 var options = {};
117
118 // Get any optional parameters that may have been passed
119 if (command.encoding)
120 options.encoding = command.encoding;
121
122 options.password = global.config.restrict_server_password || command.password;
123
124 this.state.connect(
125 (global.config.restrict_server || command.hostname),
126 (global.config.restrict_server_port || command.port),
127 (typeof global.config.restrict_server_ssl !== 'undefined' ?
128 global.config.restrict_server_ssl :
129 command.ssl),
130 command.nick,
131 {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.real_address},
132 options,
133 callback);
134 } else {
135 return callback('Hostname, port and nickname must be specified');
136 }
137 break;
138 default:
139 callback();
140 }
141 }
142
143
144 // Websocket has disconnected, so quit all the IRC connections
145 function websocketDisconnect() {
146 this.emit('disconnect');
147
148 this.dispose();
149 }
150
151
152 // TODO: Should this close all the websocket connections too?
153 function websocketError() {
154 this.dispose();
155 }