Merge branch 'development' of https://github.com/crtaylor123/KiwiIRC into crtaylor123...
[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 this.applyEventHandlers();
13
14 // Used to check if a disconnection was unplanned
15 this.disconnect_requested = false;
16 };
17
18
19 this.applyEventHandlers = function () {
20 /*
21 kiwi.gateway.on('message:#channel', my_function);
22 kiwi.gateway.on('message:somenick', my_function);
23
24 kiwi.gateway.on('notice:#channel', my_function);
25 kiwi.gateway.on('action:somenick', my_function);
26
27 kiwi.gateway.on('join:#channel', my_function);
28 kiwi.gateway.on('part:#channel', my_function);
29 kiwi.gateway.on('quit', my_function);
30 */
31 var that = this;
32
33 // Some easier handler events
34 this.on('onmsg', function (event) {
35 var source,
36 connection = _kiwi.app.connections.getByConnectionId(event.server),
37 is_pm = (event.channel.toLowerCase() == connection.get('nick').toLowerCase());
38
39 source = is_pm ? event.nick : event.channel;
40
41 that.trigger('message:' + source, event);
42 that.trigger('message', event);
43
44 if (is_pm) {
45 that.trigger('pm:' + source, event);
46 that.trigger('pm', event);
47 }
48 }, this);
49
50
51 this.on('onnotice', function (event) {
52 // The notice towards a channel or a query window?
53 var source = event.target || event.nick;
54
55 this.trigger('notice:' + source, event);
56 this.trigger('notice', event);
57 }, this);
58
59
60 this.on('onaction', function (event) {
61 var source,
62 connection = _kiwi.app.connections.getByConnectionId(event.server),
63 is_pm = (event.channel.toLowerCase() == connection.get('nick').toLowerCase());
64
65 source = is_pm ? event.nick : event.channel;
66
67 that.trigger('action:' + source, event);
68
69 if (is_pm) {
70 that.trigger('action:' + source, event);
71 that.trigger('action', event);
72 }
73 }, this);
74
75
76 this.on('ontopic', function (event) {
77 that.trigger('topic:' + event.channel, event);
78 that.trigger('topic', event);
79 });
80
81
82 this.on('onjoin', function (event) {
83 that.trigger('join:' + event.channel, event);
84 that.trigger('join', event);
85 });
86
87 };
88
89
90
91 this.reconnect = function (callback) {
92 var that = this,
93 transport_path;
94
95 this.disconnect_requested = true;
96 this.socket.close();
97
98 this.socket = null;
99 this.connect(callback);
100 };
101
102
103
104 /**
105 * Connects to the server
106 * @param {Function} callback A callback function to be invoked once Kiwi's server has connected to the IRC server
107 */
108 this.connect = function (callback) {
109 this.connect_callback = callback;
110
111 // Keep note of the server we are connecting to
112 this.set('kiwi_server', _kiwi.app.kiwi_server);
113
114 this.socket = new EngineioTools.ReconnectingSocket(this.get('kiwi_server'), {
115 path: _kiwi.app.get('base_path') + '/transport',
116 reconnect_max_attempts: 5,
117 reconnect_delay: 2000
118 });
119
120 this.rpc = new EngineioTools.Rpc(this.socket);
121
122 this.socket.on('connect_failed', function (reason) {
123 this.socket.disconnect();
124 this.trigger("connect_fail", {reason: reason});
125 });
126
127 this.socket.on('error', function (e) {
128 console.log("_kiwi.gateway.socket.on('error')", {reason: e});
129 if (that.connect_callback) {
130 that.connect_callback(e);
131 delete that.connect_callback;
132 }
133
134 that.trigger("connect_fail", {reason: e});
135 });
136
137 this.socket.on('connecting', function (transport_type) {
138 console.log("_kiwi.gateway.socket.on('connecting')");
139 that.trigger("connecting");
140 });
141
142 /**
143 * Once connected to the kiwi server send the IRC connect command along
144 * with the IRC server details.
145 * A `connect` event is sent from the kiwi server once connected to the
146 * IRCD and the nick has been accepted.
147 */
148 this.socket.on('open', function () {
149 // Reset the disconnect_requested flag
150 that.disconnect_requested = false;
151
152 console.log("_kiwi.gateway.socket.on('open')");
153 });
154
155 this.rpc.on('too_many_connections', function () {
156 that.trigger("connect_fail", {reason: 'too_many_connections'});
157 });
158
159 this.rpc.on('irc', function (response, data) {
160 that.parse(data.command, data.data);
161 });
162
163 this.rpc.on('kiwi', function (response, data) {
164 that.parseKiwi(data.command, data.data);
165 });
166
167 this.socket.on('close', function () {
168 that.trigger("disconnect", {});
169 console.log("_kiwi.gateway.socket.on('close')");
170 });
171
172 this.socket.on('reconnecting', function (status) {
173 console.log("_kiwi.gateway.socket.on('reconnecting')");
174 that.trigger("reconnecting", {delay: status.delay, attempts: status.attempts});
175 });
176
177 this.socket.on('reconnecting_failed', function () {
178 console.log("_kiwi.gateway.socket.on('reconnect_failed')");
179 });
180 };
181
182
183 /**
184 * Return a new network object with the new connection details
185 */
186 this.newConnection = function(connection_info, callback_fn) {
187 var that = this;
188
189 // If not connected, connect first then re-call this function
190 if (!this.isConnected()) {
191 this.connect(function(err) {
192 if (err) {
193 callback_fn(err);
194 return;
195 }
196
197 that.newConnection(connection_info, callback_fn);
198 });
199
200 return;
201 }
202
203 this.makeIrcConnection(connection_info, function(err, server_num) {
204 var connection;
205
206 if (!err) {
207 if (!_kiwi.app.connections.getByConnectionId(server_num)){
208 var inf = {
209 connection_id: server_num,
210 nick: connection_info.nick,
211 address: connection_info.host,
212 port: connection_info.port,
213 ssl: connection_info.ssl,
214 password: connection_info.password
215 };
216 connection = new _kiwi.model.Network(inf);
217 _kiwi.app.connections.add(connection);
218 }
219
220 console.log("_kiwi.gateway.socket.on('connect')", connection);
221 callback_fn && callback_fn(err, connection);
222
223 } else {
224 console.log("_kiwi.gateway.socket.on('error')", {reason: err});
225 callback_fn && callback_fn(err);
226 }
227 });
228 };
229
230
231 /**
232 * Make a new IRC connection and return its connection ID
233 */
234 this.makeIrcConnection = function(connection_info, callback_fn) {
235 var server_info = {
236 command: 'connect',
237 nick: connection_info.nick,
238 hostname: connection_info.host,
239 port: connection_info.port,
240 ssl: connection_info.ssl,
241 password: connection_info.password
242 };
243
244 connection_info.options = connection_info.options || {};
245
246 // A few optional parameters
247 if (connection_info.options.encoding)
248 server_info.encoding = connection_info.options.encoding;
249
250 this.rpc.call('kiwi', server_info, function (err, server_num) {
251 if (!err) {
252 callback_fn && callback_fn(err, server_num);
253
254 } else {
255 callback_fn && callback_fn(err);
256 }
257 });
258 };
259
260
261 this.isConnected = function () {
262 // TODO: Check this. Might want to use .readyState
263 return this.socket;
264 };
265
266
267
268 this.parseKiwi = function (command, data) {
269 var client_info_data;
270
271 this.trigger('kiwi:' + command, data);
272 this.trigger('kiwi', data);
273
274 switch (command) {
275 case 'connected':
276 // Send some info on this client to the server
277 client_info_data = {
278 command: 'client_info',
279 build_version: _kiwi.global.build_version
280 };
281 this.rpc.call('kiwi', client_info_data);
282
283 this.connect_callback && this.connect_callback();
284 delete this.connect_callback;
285
286 break;
287 }
288 };
289 /*
290 Events:
291 msg
292 action
293 server_connect
294 options
295 motd
296 notice
297 userlist
298 nick
299 join
300 topic
301 part
302 kick
303 quit
304 whois
305 syncchannel_redirect
306 debug
307 */
308 /**
309 * Parses the response from the server
310 */
311 this.parse = function (command, data) {
312 //console.log('gateway event', command, data);
313
314 if (command !== undefined) {
315 switch (command) {
316 case 'options':
317 $.each(data.options, function (name, value) {
318 switch (name) {
319 case 'CHANTYPES':
320 that.set('channel_prefix', value.join(''));
321 break;
322 case 'NETWORK':
323 that.set('name', value);
324 break;
325 case 'PREFIX':
326 that.set('user_prefixes', value);
327 break;
328 }
329 });
330 that.set('cap', data.cap);
331 break;
332
333 /*
334 case 'sync':
335 if (_kiwi.gateway.onSync && _kiwi.gateway.syncing) {
336 _kiwi.gateway.syncing = false;
337 _kiwi.gateway.onSync(item);
338 }
339 break;
340 */
341
342 case 'kiwi':
343 this.emit('_kiwi.' + data.namespace, data.data);
344 break;
345 }
346 }
347
348
349 if (typeof data.server !== 'undefined') {
350 that.trigger('connection:' + data.server.toString(), {
351 event_name: command,
352 event_data: data
353 });
354 }
355
356 // Trigger the global events (Mainly legacy now)
357 that.trigger('on' + command, data);
358 };
359
360 /**
361 * Sends data to the server
362 * @private
363 * @param {Object} data The data to send
364 * @param {Function} callback A callback function
365 */
366 this.sendData = function (connection_id, data, callback) {
367 if (typeof connection_id === 'undefined' || connection_id === null)
368 connection_id = _kiwi.app.connections.active_connection.get('connection_id');
369
370 var data_buffer = {
371 server: connection_id,
372 data: JSON.stringify(data)
373 };
374 this.rpc.call('irc', data_buffer, callback);
375 };
376
377 /**
378 * Sends a PRIVMSG message
379 * @param {String} target The target of the message (e.g. a channel or nick)
380 * @param {String} msg The message to send
381 * @param {Function} callback A callback function
382 */
383 this.privmsg = function (connection_id, target, msg, callback) {
384 var data = {
385 method: 'privmsg',
386 args: {
387 target: target,
388 msg: msg
389 }
390 };
391
392 this.sendData(connection_id, data, callback);
393 };
394
395 /**
396 * Sends a NOTICE message
397 * @param {String} target The target of the message (e.g. a channel or nick)
398 * @param {String} msg The message to send
399 * @param {Function} callback A callback function
400 */
401 this.notice = function (connection_id, target, msg, callback) {
402 var data = {
403 method: 'notice',
404 args: {
405 target: target,
406 msg: msg
407 }
408 };
409
410 this.sendData(connection_id, data, callback);
411 };
412
413 /**
414 * Sends a CTCP message
415 * @param {Boolean} request Indicates whether this is a CTCP request (true) or reply (false)
416 * @param {String} type The type of CTCP message, e.g. 'VERSION', 'TIME', 'PING' etc.
417 * @param {String} target The target of the message, e.g a channel or nick
418 * @param {String} params Additional paramaters
419 * @param {Function} callback A callback function
420 */
421 this.ctcp = function (connection_id, request, type, target, params, callback) {
422 var data = {
423 method: 'ctcp',
424 args: {
425 request: request,
426 type: type,
427 target: target,
428 params: params
429 }
430 };
431
432 this.sendData(connection_id, data, callback);
433 };
434
435 /**
436 * @param {String} target The target of the message (e.g. a channel or nick)
437 * @param {String} msg The message to send
438 * @param {Function} callback A callback function
439 */
440 this.action = function (connection_id, target, msg, callback) {
441 this.ctcp(connection_id, true, 'ACTION', target, msg, callback);
442 };
443
444 /**
445 * Joins a channel
446 * @param {String} channel The channel to join
447 * @param {String} key The key to the channel
448 * @param {Function} callback A callback function
449 */
450 this.join = function (connection_id, channel, key, callback) {
451 var data = {
452 method: 'join',
453 args: {
454 channel: channel,
455 key: key
456 }
457 };
458
459 this.sendData(connection_id, data, callback);
460 };
461
462 /**
463 * Retrieves channel information
464 */
465 this.channelInfo = function (connection_id, channel, callback) {
466 var data = {
467 method: 'channel_info',
468 args: {
469 channel: channel
470 }
471 };
472
473 this.sendData(connection_id, data, callback);
474 };
475
476 /**
477 * Leaves a channel
478 * @param {String} channel The channel to part
479 * @param {Function} callback A callback function
480 */
481 this.part = function (connection_id, channel, callback) {
482 var data = {
483 method: 'part',
484 args: {
485 channel: channel
486 }
487 };
488
489 this.sendData(connection_id, data, callback);
490 };
491
492 /**
493 * Queries or modifies a channell topic
494 * @param {String} channel The channel to query or modify
495 * @param {String} new_topic The new topic to set
496 * @param {Function} callback A callback function
497 */
498 this.topic = function (connection_id, channel, new_topic, callback) {
499 var data = {
500 method: 'topic',
501 args: {
502 channel: channel,
503 topic: new_topic
504 }
505 };
506
507 this.sendData(connection_id, data, callback);
508 };
509
510 /**
511 * Kicks a user from a channel
512 * @param {String} channel The channel to kick the user from
513 * @param {String} nick The nick of the user to kick
514 * @param {String} reason The reason for kicking the user
515 * @param {Function} callback A callback function
516 */
517 this.kick = function (connection_id, channel, nick, reason, callback) {
518 var data = {
519 method: 'kick',
520 args: {
521 channel: channel,
522 nick: nick,
523 reason: reason
524 }
525 };
526
527 this.sendData(connection_id, data, callback);
528 };
529
530 /**
531 * Disconnects us from the server
532 * @param {String} msg The quit message to send to the IRC server
533 * @param {Function} callback A callback function
534 */
535 this.quit = function (connection_id, msg, callback) {
536 msg = msg || "";
537 var data = {
538 method: 'quit',
539 args: {
540 message: msg
541 }
542 };
543
544 this.sendData(connection_id, data, callback);
545 };
546
547 /**
548 * Sends a string unmodified to the IRC server
549 * @param {String} data The data to send to the IRC server
550 * @param {Function} callback A callback function
551 */
552 this.raw = function (connection_id, data, callback) {
553 data = {
554 method: 'raw',
555 args: {
556 data: data
557 }
558 };
559
560 this.sendData(connection_id, data, callback);
561 };
562
563 /**
564 * Changes our nickname
565 * @param {String} new_nick Our new nickname
566 * @param {Function} callback A callback function
567 */
568 this.changeNick = function (connection_id, new_nick, callback) {
569 var data = {
570 method: 'nick',
571 args: {
572 nick: new_nick
573 }
574 };
575
576 this.sendData(connection_id, data, callback);
577 };
578
579 /**
580 * Sets a mode for a target
581 */
582 this.mode = function (connection_id, target, mode_string, callback) {
583 data = {
584 method: 'raw',
585 args: {
586 data: 'MODE ' + target + ' ' + mode_string
587 }
588 };
589
590 this.sendData(connection_id, data, callback);
591 };
592
593
594 /**
595 * Sends ENCODING change request to server.
596 * @param {String} new_encoding The new proposed encode
597 * @param {Fucntion} callback A callback function
598 */
599 this.setEncoding = function (connection_id, new_encoding, callback) {
600 var data = {
601 method: 'encoding',
602 args: {
603 encoding: new_encoding
604 }
605 };
606 this.sendData(connection_id, data, callback);
607 };
608
609 /**
610 * Sends data to a fellow Kiwi IRC user
611 * @param {String} target The nick of the Kiwi IRC user to send to
612 * @param {String} data The data to send
613 * @param {Function} callback A callback function
614 */
615 this.kiwi = function (target, data, callback) {
616 data = {
617 method: 'kiwi',
618 args: {
619 target: target,
620 data: data
621 }
622 };
623
624 this.sendData(data, callback);
625 };
626
627
628 return new (Backbone.Model.extend(this))(arguments);
629 };