01f92e8f013ef954b4559f61f1ba263abd4f9c38
[KiwiIRC.git] / client / src / models / gateway.js
1 _kiwi.model.Gateway = function () {
2
3 // Set to a reference to this object within initialize()
4 var that = null;
5
6 this.initialize = function () {
7 that = this;
8
9 // For ease of access. The socket.io object
10 this.socket = this.get('socket');
11
12 // Used to check if a disconnection was unplanned
13 this.disconnect_requested = false;
14 };
15
16
17
18 this.reconnect = function (callback) {
19 var that = this,
20 transport_path;
21
22 this.disconnect_requested = true;
23 this.socket.close();
24
25 this.socket = null;
26 this.connect(callback);
27 };
28
29
30
31 /**
32 * Connects to the server
33 * @param {Function} callback A callback function to be invoked once Kiwi's server has connected to the IRC server
34 */
35 this.connect = function (callback) {
36 this.connect_callback = callback;
37
38 // Keep note of the server we are connecting to
39 this.set('kiwi_server', _kiwi.app.kiwi_server);
40
41 this.socket = new EngineioTools.ReconnectingSocket(this.get('kiwi_server'), {
42 transports: _kiwi.app.server_settings.transports || ['polling', 'websocket'],
43 path: _kiwi.app.get('base_path') + '/transport',
44 reconnect_max_attempts: 5,
45 reconnect_delay: 2000
46 });
47
48 this.rpc = new EngineioTools.Rpc(this.socket);
49
50 this.socket.on('connect_failed', function (reason) {
51 this.socket.disconnect();
52 this.trigger("connect_fail", {reason: reason});
53 });
54
55 this.socket.on('error', function (e) {
56 console.log("_kiwi.gateway.socket.on('error')", {reason: e});
57 if (that.connect_callback) {
58 that.connect_callback(e);
59 delete that.connect_callback;
60 }
61
62 that.trigger("connect_fail", {reason: e});
63 });
64
65 this.socket.on('connecting', function (transport_type) {
66 console.log("_kiwi.gateway.socket.on('connecting')");
67 that.trigger("connecting");
68 });
69
70 /**
71 * Once connected to the kiwi server send the IRC connect command along
72 * with the IRC server details.
73 * A `connect` event is sent from the kiwi server once connected to the
74 * IRCD and the nick has been accepted.
75 */
76 this.socket.on('open', function () {
77 // Reset the disconnect_requested flag
78 that.disconnect_requested = false;
79
80 console.log("_kiwi.gateway.socket.on('open')");
81 });
82
83 this.rpc.on('too_many_connections', function () {
84 that.trigger("connect_fail", {reason: 'too_many_connections'});
85 });
86
87 this.rpc.on('irc', function (response, data) {
88 that.parse(data.command, data.data);
89 });
90
91 this.rpc.on('kiwi', function (response, data) {
92 that.parseKiwi(data.command, data.data);
93 });
94
95 this.socket.on('close', function () {
96 that.trigger("disconnect", {});
97 console.log("_kiwi.gateway.socket.on('close')");
98 });
99
100 this.socket.on('reconnecting', function (status) {
101 console.log("_kiwi.gateway.socket.on('reconnecting')");
102 that.trigger("reconnecting", {delay: status.delay, attempts: status.attempts});
103 });
104
105 this.socket.on('reconnecting_failed', function () {
106 console.log("_kiwi.gateway.socket.on('reconnect_failed')");
107 });
108 };
109
110
111 /**
112 * Return a new network object with the new connection details
113 */
114 this.newConnection = function(connection_info, callback_fn) {
115 var that = this;
116
117 // If not connected, connect first then re-call this function
118 if (!this.isConnected()) {
119 this.connect(function(err) {
120 if (err) {
121 callback_fn(err);
122 return;
123 }
124
125 that.newConnection(connection_info, callback_fn);
126 });
127
128 return;
129 }
130
131 this.makeIrcConnection(connection_info, function(err, server_num) {
132 var connection;
133
134 if (!err) {
135 if (!_kiwi.app.connections.getByConnectionId(server_num)){
136 var inf = {
137 connection_id: server_num,
138 nick: connection_info.nick,
139 address: connection_info.host,
140 port: connection_info.port,
141 ssl: connection_info.ssl,
142 password: connection_info.password
143 };
144 connection = new _kiwi.model.Network(inf);
145 _kiwi.app.connections.add(connection);
146 }
147
148 console.log("_kiwi.gateway.socket.on('connect')", connection);
149 callback_fn && callback_fn(err, connection);
150
151 } else {
152 console.log("_kiwi.gateway.socket.on('error')", {reason: err});
153 callback_fn && callback_fn(err);
154 }
155 });
156 };
157
158
159 /**
160 * Make a new IRC connection and return its connection ID
161 */
162 this.makeIrcConnection = function(connection_info, callback_fn) {
163 var server_info = {
164 nick: connection_info.nick,
165 hostname: connection_info.host,
166 port: connection_info.port,
167 ssl: connection_info.ssl,
168 password: connection_info.password
169 };
170
171 connection_info.options = connection_info.options || {};
172
173 // A few optional parameters
174 if (connection_info.options.encoding)
175 server_info.encoding = connection_info.options.encoding;
176
177 this.rpc.call('kiwi.connect_irc', server_info, function (err, server_num) {
178 if (!err) {
179 callback_fn && callback_fn(err, server_num);
180
181 } else {
182 callback_fn && callback_fn(err);
183 }
184 });
185 };
186
187
188 this.isConnected = function () {
189 // TODO: Check this. Might want to use .readyState
190 return this.socket;
191 };
192
193
194
195 this.parseKiwi = function (command, data) {
196 var args;
197
198 switch (command) {
199 case 'connected':
200 // Send some info on this client to the server
201 args = {
202 build_version: _kiwi.global.build_version
203 };
204 this.rpc.call('kiwi.client_info', args);
205
206 this.connect_callback && this.connect_callback();
207 delete this.connect_callback;
208
209 break;
210 }
211
212 this.trigger('kiwi:' + command, data);
213 this.trigger('kiwi', data);
214 };
215
216 /**
217 * Parses the response from the server
218 */
219 this.parse = function (command, data) {
220
221 // Trigger the connection specific events (used by Network objects)
222 if (typeof data.connection_id !== 'undefined') {
223 that.trigger('connection:' + data.connection_id.toString(), {
224 event_name: command,
225 event_data: data
226 });
227
228 // Some events trigger a more in-depth event name
229 if (command == 'message' && data.type) {
230 that.trigger('connection:' + data.connection_id.toString(), {
231 event_name: 'message:' + data.type,
232 event_data: data
233 });
234 }
235
236 if (command == 'channel' && data.type) {
237 that.trigger('connection:' + data.connection_id.toString(), {
238 event_name: 'channel:' + data.type,
239 event_data: data
240 });
241 }
242 }
243
244 // Trigger the global events
245 that.trigger('connection', {event_name: command, event_data: data});
246 that.trigger('connection:' + command, data);
247 };
248
249 this.rpcCall = function(method, connection_id) {
250 var args = Array.prototype.slice.call(arguments, 0);
251
252 if (typeof args[1] === 'undefined' || args[1] === null)
253 args[1] = _kiwi.app.connections.active_connection.get('connection_id');
254
255 return this.rpc.call.apply(this.rpc, args);
256 };
257
258 /**
259 * Sends a PRIVMSG message
260 * @param {String} target The target of the message (e.g. a channel or nick)
261 * @param {String} msg The message to send
262 * @param {Function} callback A callback function
263 */
264 this.privmsg = function (connection_id, target, msg, callback) {
265 var args = {
266 target: target,
267 msg: msg
268 };
269
270 this.rpcCall('irc.privmsg', connection_id, args, callback);
271 };
272
273 /**
274 * Sends a NOTICE message
275 * @param {String} target The target of the message (e.g. a channel or nick)
276 * @param {String} msg The message to send
277 * @param {Function} callback A callback function
278 */
279 this.notice = function (connection_id, target, msg, callback) {
280 var args = {
281 target: target,
282 msg: msg
283 };
284
285 this.rpcCall('irc.notice', connection_id, args, callback);
286 };
287
288 /**
289 * Sends a CTCP message
290 * @param {Boolean} request Indicates whether this is a CTCP request (true) or reply (false)
291 * @param {String} type The type of CTCP message, e.g. 'VERSION', 'TIME', 'PING' etc.
292 * @param {String} target The target of the message, e.g a channel or nick
293 * @param {String} params Additional paramaters
294 * @param {Function} callback A callback function
295 */
296 this.ctcp = function (connection_id, is_request, type, target, params, callback) {
297 var args = {
298 is_request: is_request,
299 type: type,
300 target: target,
301 params: params
302 };
303
304 this.rpcCall('irc.ctcp', connection_id, args, callback);
305 };
306
307 this.ctcpRequest = function (connection_id, type, target, params, callback) {
308 this.ctcp(connection_id, true, type, target, params, callback);
309 };
310 this.ctcpResponse = function (connection_id, type, target, params, callback) {
311 this.ctcp(connection_id, false, type, target, params, callback);
312 };
313
314 /**
315 * @param {String} target The target of the message (e.g. a channel or nick)
316 * @param {String} msg The message to send
317 * @param {Function} callback A callback function
318 */
319 this.action = function (connection_id, target, msg, callback) {
320 this.ctcp(connection_id, true, 'ACTION', target, msg, callback);
321 };
322
323 /**
324 * Joins a channel
325 * @param {String} channel The channel to join
326 * @param {String} key The key to the channel
327 * @param {Function} callback A callback function
328 */
329 this.join = function (connection_id, channel, key, callback) {
330 var args = {
331 channel: channel,
332 key: key
333 };
334
335 this.rpcCall('irc.join', connection_id, args, callback);
336 };
337
338 /**
339 * Retrieves channel information
340 */
341 this.channelInfo = function (connection_id, channel, callback) {
342 var args = {
343 channel: channel
344 };
345
346 this.rpcCall('irc.channel_info', connection_id, args, callback);
347 };
348
349 /**
350 * Leaves a channel
351 * @param {String} channel The channel to part
352 * @param {String} message Optional part message
353 * @param {Function} callback A callback function
354 */
355 this.part = function (connection_id, channel, message, callback) {
356 "use strict";
357
358 // The message param is optional, so juggle args if it is missing
359 if (typeof arguments[2] === 'function') {
360 callback = arguments[2];
361 message = undefined;
362 }
363 var args = {
364 channel: channel,
365 message: message
366 };
367
368 this.rpcCall('irc.part', connection_id, args, callback);
369 };
370
371 /**
372 * Queries or modifies a channell topic
373 * @param {String} channel The channel to query or modify
374 * @param {String} new_topic The new topic to set
375 * @param {Function} callback A callback function
376 */
377 this.topic = function (connection_id, channel, new_topic, callback) {
378 var args = {
379 channel: channel,
380 topic: new_topic
381 };
382
383 this.rpcCall('irc.topic', connection_id, args, callback);
384 };
385
386 /**
387 * Kicks a user from a channel
388 * @param {String} channel The channel to kick the user from
389 * @param {String} nick The nick of the user to kick
390 * @param {String} reason The reason for kicking the user
391 * @param {Function} callback A callback function
392 */
393 this.kick = function (connection_id, channel, nick, reason, callback) {
394 var args = {
395 channel: channel,
396 nick: nick,
397 reason: reason
398 };
399
400 this.rpcCall('irc.kick', connection_id, args, callback);
401 };
402
403 /**
404 * Disconnects us from the server
405 * @param {String} msg The quit message to send to the IRC server
406 * @param {Function} callback A callback function
407 */
408 this.quit = function (connection_id, msg, callback) {
409 msg = msg || "";
410
411 var args = {
412 message: msg
413 };
414
415 this.rpcCall('irc.quit', connection_id, args, callback);
416 };
417
418 /**
419 * Sends a string unmodified to the IRC server
420 * @param {String} data The data to send to the IRC server
421 * @param {Function} callback A callback function
422 */
423 this.raw = function (connection_id, data, callback) {
424 var args = {
425 data: data
426 };
427
428 this.rpcCall('irc.raw', connection_id, args, callback);
429 };
430
431 /**
432 * Changes our nickname
433 * @param {String} new_nick Our new nickname
434 * @param {Function} callback A callback function
435 */
436 this.changeNick = function (connection_id, new_nick, callback) {
437 var args = {
438 nick: new_nick
439 };
440
441 this.rpcCall('irc.nick', connection_id, args, callback);
442 };
443
444 /**
445 * Sets a mode for a target
446 */
447 this.mode = function (connection_id, target, mode_string, callback) {
448 var args = {
449 data: 'MODE ' + target + ' ' + mode_string
450 };
451
452 this.rpcCall('irc.raw', connection_id, args, callback);
453 };
454
455 /**
456 * Sends ENCODING change request to server.
457 * @param {String} new_encoding The new proposed encode
458 * @param {Fucntion} callback A callback function
459 */
460 this.setEncoding = function (connection_id, new_encoding, callback) {
461 var args = {
462 encoding: new_encoding
463 };
464
465 this.rpcCall('irc.encoding', connection_id, args, callback);
466 };
467
468
469 return new (Backbone.Model.extend(this))(arguments);
470 };