1 var util
= require('util'),
2 events
= require('events'),
3 crypto
= require('crypto'),
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');
12 var Client = function (websocket
, opts
) {
15 Stats
.incr('client.created');
17 events
.EventEmitter
.call(this);
18 this.websocket
= websocket
;
20 // Keep a record of how this client connected
21 this.server_config
= opts
.server_config
;
23 this.rpc
= new WebsocketRpc(this.websocket
);
24 this.rpc
.on('all', function(func_name
, return_fn
) {
25 if (typeof func_name
=== 'string' && typeof return_fn
=== 'function') {
26 Stats
.incr('client.command');
27 Stats
.incr('client.command.' + func_name
);
32 this.real_address
= this.websocket
.meta
.real_address
;
34 // A hash to identify this client instance
35 this.hash
= crypto
.createHash('sha256')
36 .update(this.real_address
)
37 .update('' + Date
.now())
38 .update(Math
.floor(Math
.random() * 100000).toString())
41 this.state
= new State(this);
48 // Handler for any commands sent from the client
49 this.client_commands
= new ClientCommands(this);
50 this.client_commands
.addRpcEvents(this, this.rpc
);
52 // Handles the kiwi.* RPC functions
53 this.attachKiwiCommands();
55 websocket
.on('message', function() {
56 // A message from the client is a sure sign the client is still alive, so consider it a heartbeat
60 websocket
.on('close', function () {
61 websocketDisconnect
.apply(that
, arguments
);
63 websocket
.on('error', function () {
64 websocketError
.apply(that
, arguments
);
67 this.disposed
= false;
69 // Let the client know it's finished connecting
70 this.sendKiwiCommand('connected');
72 util
.inherits(Client
, events
.EventEmitter
);
74 module
.exports
.Client
= Client
;
77 // Callbacks SHALL accept 2 arguments, error and response, in that order.
78 // error MUST be null where the command is successul.
79 // error MUST otherwise be a truthy value and SHOULD be a string where the cause of the error is known.
80 // response MAY be given even if error is truthy
82 Client
.prototype.sendIrcCommand = function (command
, data
, callback
) {
83 var c
= {command
: command
, data
: data
};
84 this.rpc('irc', c
, callback
);
87 Client
.prototype.sendKiwiCommand = function (command
, data
, callback
) {
88 var c
= {command
: command
, data
: data
};
89 this.rpc('kiwi', c
, callback
);
92 Client
.prototype.dispose = function () {
93 Stats
.incr('client.disposed');
95 if (this._heartbeat_tmr
) {
96 clearTimeout(this._heartbeat_tmr
);
100 this.websocket
.removeAllListeners();
102 this.disposed
= true;
103 this.emit('dispose');
105 this.removeAllListeners();
110 Client
.prototype.heartbeat = function() {
111 if (this._heartbeat_tmr
) {
112 clearTimeout(this._heartbeat_tmr
);
115 // After 2 minutes of this heartbeat not being called again, assume the client has disconnected
116 this._heartbeat_tmr
= setTimeout(_
.bind(this._heartbeat_timeout
, this), 120000);
120 Client
.prototype._heartbeat_timeout = function() {
121 Stats
.incr('client.timeout');
127 Client
.prototype.attachKiwiCommands = function() {
130 this.rpc
.on('kiwi.connect_irc', function(callback
, command
) {
131 if (command
.hostname
&& command
.port
&& command
.nick
) {
134 // Get any optional parameters that may have been passed
135 if (command
.encoding
)
136 options
.encoding
= command
.encoding
;
138 options
.password
= global
.config
.restrict_server_password
|| command
.password
;
141 (global
.config
.restrict_server
|| command
.hostname
),
142 (global
.config
.restrict_server_port
|| command
.port
),
143 (typeof global
.config
.restrict_server_ssl
!== 'undefined' ?
144 global
.config
.restrict_server_ssl
:
147 {hostname
: that
.websocket
.meta
.revdns
, address
: that
.websocket
.meta
.real_address
},
151 return callback('Hostname, port and nickname must be specified');
156 this.rpc
.on('kiwi.client_info', function(callback
, args
) {
157 // keep hold of selected parts of the client_info
159 build_version
: args
.build_version
.toString() || undefined
164 // Just to let us know the client is still there
165 this.rpc
.on('kiwi.heartbeat', function(callback
, args
) {
172 // Websocket has disconnected, so quit all the IRC connections
173 function websocketDisconnect() {
174 this.emit('disconnect');
180 // TODO: Should this close all the websocket connections too?
181 function websocketError() {