764d600ece700e61b7a5729f8b9b84e4e6e528c7
[KiwiIRC.git] / server / client.js
1 var util = require('util'),
2 events = require('events'),
3 _ = require('underscore'),
4 config = require('./configuration.js'),
5 IrcConnection = require('./irc/connection.js').IrcConnection,
6 IrcCommands = require('./irc/commands.js'),
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 this.irc_connections = [];
17 this.next_connection = 0;
18
19 this.buffer = {
20 list: [],
21 motd: ''
22 };
23
24 // Handler for any commands sent from the client
25 this.client_commands = new ClientCommands(this);
26
27 websocket.on('irc', function () {
28 handleClientMessage.apply(that, arguments);
29 });
30 websocket.on('kiwi', function () {
31 kiwiCommand.apply(that, arguments);
32 });
33 websocket.on('disconnect', function () {
34 websocketDisconnect.apply(that, arguments);
35 });
36 websocket.on('error', function () {
37 websocketError.apply(that, arguments);
38 });
39 };
40 util.inherits(Client, events.EventEmitter);
41
42 module.exports.Client = Client;
43
44 // Callback API:
45 // Callbacks SHALL accept 2 arguments, error and response, in that order.
46 // error MUST be null where the command is successul.
47 // error MUST otherwise be a truthy value and SHOULD be a string where the cause of the error is known.
48 // response MAY be given even if error is truthy
49
50 Client.prototype.sendIrcCommand = function (command, data, callback) {
51 var c = {command: command, data: data};
52 this.websocket.emit('irc', c, callback);
53 };
54
55 Client.prototype.sendKiwiCommand = function (command, data, callback) {
56 var c = {command: command, data: data};
57 this.websocket.emit('kiwi', c, callback);
58 };
59
60 function handleClientMessage(msg, callback) {
61 var server, args, obj, channels, keys;
62
63 // Make sure we have a server number specified
64 if ((msg.server === null) || (typeof msg.server !== 'number')) {
65 return (typeof callback === 'function') ? callback('server not specified') : undefined;
66 } else if (!this.irc_connections[msg.server]) {
67 return (typeof callback === 'function') ? callback('not connected to server') : undefined;
68 }
69
70 // The server this command is directed to
71 server = this.irc_connections[msg.server];
72
73 if (typeof callback !== 'function') {
74 callback = null;
75 }
76
77 try {
78 msg.data = JSON.parse(msg.data);
79 } catch (e) {
80 kiwi.log('[handleClientMessage] JSON parsing error ' + msg.data);
81 return;
82 }
83
84 // Run the client command
85 this.client_commands.run(msg.data.method, msg.data.args, server, callback);
86 }
87
88
89
90
91 function kiwiCommand(command, callback) {
92 var that = this;
93
94 if (typeof callback !== 'function') {
95 callback = function () {};
96 }
97 switch (command.command) {
98 case 'connect':
99 if (command.hostname && command.port && command.nick) {
100 var con = new IrcConnection(command.hostname, command.port, command.ssl,
101 command.nick, {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.address.address},
102 command.password);
103
104 var con_num = this.next_connection++;
105 this.irc_connections[con_num] = con;
106
107 var irc_commands = new IrcCommands(con, con_num, this);
108 irc_commands.bindEvents();
109
110 con.on('connected', function () {
111 console.log("con.on('connected')");
112 return callback(null, con_num);
113 });
114
115 con.on('error', function (err) {
116 // TODO: Once multiple servers implemented, specify which server failed
117 //that.sendKiwiCommand('error', {server: con_num, error: err});
118 return callback(err.code, null);
119 });
120
121 con.on('close', function () {
122 that.irc_connections[con_num] = null;
123 });
124 } else {
125 return callback('Hostname, port and nickname must be specified');
126 }
127 break;
128 default:
129 callback();
130 }
131 };
132
133
134 // Websocket has disconnected, so quit all the IRC connections
135 function websocketDisconnect() {
136 _.each(this.irc_connections, function (irc_connection, i, cons) {
137 if (irc_connection) {
138 irc_connection.end('QUIT :' + (config.get().quit_message || ''));
139 cons[i] = null;
140 }
141 });
142 this.emit('destroy');
143 };
144
145
146 // TODO: Should this close all the websocket connections too?
147 function websocketError() {
148 this.emit('destroy');
149 };