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