Server stats/events module
[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 WebsocketRpc = require('./websocketrpc.js'),
9 Stats = require('./stats.js');
10
11
12 var Client = function (websocket) {
13 var that = this;
14
15 Stats.incr('client.created');
16
17 events.EventEmitter.call(this);
18 this.websocket = websocket;
19
20 this.rpc = new WebsocketRpc(this.websocket);
21 this.rpc.on('all', function(func_name, return_fn) {
22 if (typeof func_name === 'string' && typeof return_fn === 'function') {
23 Stats.incr('client.command');
24 Stats.incr('client.command.' + func_name);
25 }
26 });
27
28 // Clients address
29 this.real_address = this.websocket.meta.real_address;
30
31 // A hash to identify this client instance
32 this.hash = crypto.createHash('sha256')
33 .update(this.real_address)
34 .update('' + Date.now())
35 .update(Math.floor(Math.random() * 100000).toString())
36 .digest('hex');
37
38 this.state = new State(this);
39
40 this.buffer = {
41 list: [],
42 motd: ''
43 };
44
45 // Handler for any commands sent from the client
46 this.client_commands = new ClientCommands(this);
47 this.client_commands.addRpcEvents(this, this.rpc);
48
49 // Handles the kiwi.* RPC functions
50 this.attachKiwiCommands();
51
52 websocket.on('close', function () {
53 websocketDisconnect.apply(that, arguments);
54 });
55 websocket.on('error', function () {
56 websocketError.apply(that, arguments);
57 });
58
59 this.disposed = false;
60
61 // Let the client know it's finished connecting
62 this.sendKiwiCommand('connected');
63 };
64 util.inherits(Client, events.EventEmitter);
65
66 module.exports.Client = Client;
67
68 // Callback API:
69 // Callbacks SHALL accept 2 arguments, error and response, in that order.
70 // error MUST be null where the command is successul.
71 // error MUST otherwise be a truthy value and SHOULD be a string where the cause of the error is known.
72 // response MAY be given even if error is truthy
73
74 Client.prototype.sendIrcCommand = function (command, data, callback) {
75 var c = {command: command, data: data};
76 this.rpc.call('irc', c, callback);
77 };
78
79 Client.prototype.sendKiwiCommand = function (command, data, callback) {
80 var c = {command: command, data: data};
81 this.rpc.call('kiwi', c, callback);
82 };
83
84 Client.prototype.dispose = function () {
85 Stats.incr('client.disposed');
86
87 this.disposed = true;
88 this.rpc.dispose();
89 this.emit('dispose');
90 this.removeAllListeners();
91 };
92
93
94
95 Client.prototype.attachKiwiCommands = function() {
96 var that = this;
97
98 this.rpc.on('kiwi.connect_irc', function(callback, command) {
99 if (command.hostname && command.port && command.nick) {
100 var options = {};
101
102 // Get any optional parameters that may have been passed
103 if (command.encoding)
104 options.encoding = command.encoding;
105
106 options.password = global.config.restrict_server_password || command.password;
107
108 that.state.connect(
109 (global.config.restrict_server || command.hostname),
110 (global.config.restrict_server_port || command.port),
111 (typeof global.config.restrict_server_ssl !== 'undefined' ?
112 global.config.restrict_server_ssl :
113 command.ssl),
114 command.nick,
115 {hostname: that.websocket.meta.revdns, address: that.websocket.meta.real_address},
116 options,
117 callback);
118 } else {
119 return callback('Hostname, port and nickname must be specified');
120 }
121 });
122
123
124 this.rpc.on('kiwi.client_info', function(callback, args) {
125 // keep hold of selected parts of the client_info
126 that.client_info = {
127 build_version: args.build_version.toString() || undefined
128 };
129 });
130 };
131
132
133
134 // Websocket has disconnected, so quit all the IRC connections
135 function websocketDisconnect() {
136 this.emit('disconnect');
137
138 this.dispose();
139 }
140
141
142 // TODO: Should this close all the websocket connections too?
143 function websocketError() {
144 this.dispose();
145 }