1
/* global module, require */
2
3
/**
4
* Public interface for interacting with EasyRTC. Contains the public object returned by the EasyRTC listen() function.
5
*
6
* @module easyrtc_public_obj
7
* @author Priologic Software, info@easyrtc.com
8
* @copyright Copyright 2016 Priologic Software. All rights reserved.
9
* @license BSD v2, see LICENSE file in module root folder.
10
*/
11
12
var events = require("events");
13
var async = require("async");
14
var _ = require("underscore"); // General utility functions external module
15
var g = require("./general_util"); // General utility functions local module
16
17
var e = require("./easyrtc_private_obj"); // EasyRTC private object
18
var eventListener = require("./easyrtc_default_event_listeners"); // EasyRTC default event listeners
19
var eu = require("./easyrtc_util"); // EasyRTC utility functions
20
21
/**
22
* The public object which is returned by the EasyRTC listen() function. Contains all public methods for interacting with EasyRTC server.
23
*
24
* @class
25
*/
26
var pub = module.exports;
27
28
/**
29
* Alias for Socket.io server object. Set during Listen().
30
*
31
* @member {Object} pub.socketServer
32
* @example <caption>Dump of all Socket.IO clients to server console</caption>
33
* console.log(pub.socketServer.connected);
34
*/
35
pub.socketServer = null;
36
37
38
/**
39
* Alias for Express app object. Set during Listen()
40
*
41
* @member {Object} pub.httpApp
42
*/
43
pub.httpApp = null;
44
45
46
/**
47
* Sends an array of all application names to a callback.
48
*
49
* @param {function(Error, Array.<string>)} callback Callback with error and array containing all application names.
50
*/
51
pub.getAppNames = function(callback) {
52
var appNames = [];
53
for (var key in e.app) {
54
appNames.push(key);
55
}
56
callback(null, appNames);
57
};
58
59
60
/**
61
* Gets app object for application which has an authenticated client with a given easyrtcid
62
*
63
* @param {String} easyrtcid Unique identifier for an EasyRTC connection.
64
* @param {function(?Error, Object=)} callback Callback with error and application object
65
*/
66
pub.getAppWithEasyrtcid = function(easyrtcid, callback) {
67
for (var key in e.app) {
68
if (e.app[key].connection[easyrtcid] && e.app[key].connection[easyrtcid].isAuthenticated) {
69
pub.app(key, callback);
70
return;
71
}
72
}
73
pub.util.logWarning("Can not find connection [" + easyrtcid + "]");
74
callback(new pub.util.ConnectionWarning("Can not find connection [" + easyrtcid + "]"));
75
};
76
77
78
/**
79
* Sends the count of the number of connections to the server to a provided callback.
80
*
81
* @param {function(?Error, Number)} callback Callback with error and array containing all easyrtcids.
82
*/
83
pub.getConnectionCount = function(callback) {
84
callback(null, pub.getConnectionCountSync());
85
};
86
87
88
/**
89
* Sends the count of the number of connections to the server to a provided callback.
90
*
91
* @returns {Number} The current number of connections in a room.
92
*/
93
pub.getConnectionCountSync = function() {
94
var connectionCount = 0;
95
for (var key in e.app) {
96
connectionCount = connectionCount + _.size(e.app[key].connection);
97
}
98
return connectionCount;
99
};
100
101
102
/**
103
* Gets connection object for connection which has an authenticated client with a given easyrtcid
104
*
105
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
106
* @param {function(?Error, Object=)} callback Callback with error and connection object
107
*/
108
pub.getConnectionWithEasyrtcid = function(easyrtcid, callback) {
109
for (var key in e.app) {
110
if (e.app[key].connection[easyrtcid] && e.app[key].connection[easyrtcid].isAuthenticated) {
111
pub.app(key, function(err, appObj) {
112
if (err) {
113
callback(err);
114
return;
115
}
116
appObj.connection(easyrtcid, callback);
117
});
118
return;
119
}
120
}
121
pub.util.logWarning("Can not find connection [" + easyrtcid + "]");
122
callback(new pub.util.ConnectionWarning("Can not find connection [" + easyrtcid + "]"));
123
};
124
125
126
/**
127
* Gets individual option value. The option value returned is for the server level.
128
*
129
* Note that some options can be set at the application or room level. If an option has not been set at the room level, it will check to see if it has been set at the application level, if not it will revert to the server level.
130
*
131
* @param {String} optionName Option name
132
* @return {*} Option value (can be any JSON type)
133
*/
134
pub.getOption = function(optionName) {
135
if(typeof e.option[optionName] === "undefined"){
136
pub.util.logError("Unknown option requested. Unrecognised option name '" + optionName + "'.");
137
return null;
138
}
139
return e.option[optionName];
140
};
141
142
143
/**
144
* Gets EasyRTC Version. The format is in a major.minor.patch format with an optional letter following denoting alpha or beta status. The version is retrieved from the package.json file located in the EasyRTC project root folder.
145
*
146
* @return {string} EasyRTC Version
147
*/
148
pub.getVersion = function() {
149
return e.version;
150
};
151
152
153
/**
154
* Returns the EasyRTC private object containing the current state. This should only be used for debugging purposes.
155
*
156
* @private
157
* @return {Object} EasyRTC private object
158
*/
159
pub._getPrivateObj = function() {
160
return e;
161
};
162
163
164
/**
165
* Sets individual option. The option value set is for the server level.
166
*
167
* Note that some options can be set at the application or room level. If an option has not been set at the room level, it will check to see if it has been set at the application level, if not it will revert to the server level.
168
*
169
* @param {Object} optionName Option name
170
* @param {Object} optionValue Option value
171
* @return {Boolean} true on success, false on failure
172
*/
173
pub.setOption = function(optionName, optionValue) {
174
// Can only set options which currently exist
175
if (typeof e.option[optionName] == "undefined") {
176
pub.util.logError("Error setting option. Unrecognised option name '" + optionName + "'.");
177
return false;
178
}
179
180
e.option[optionName] = pub.util.deepCopy(optionValue);
181
return true;
182
};
183
184
185
/**
186
* EasyRTC Event handling object which contain most methods for interacting with EasyRTC events. For convenience, this class has also been attached to the application, connection, session, and room classes.
187
* @class
188
*/
189
pub.events = {};
190
191
192
/**
193
* EasyRTC EventEmitter.
194
*
195
* @private
196
*/
197
pub.events._eventListener = new events.EventEmitter();
198
199
200
/**
201
* Expose event listener's emit function.
202
*
203
* @param {string} eventName EasyRTC event name.
204
* @param {...*} eventParam The event parameters
205
*/
206
pub.events.emit = pub.events._eventListener.emit.bind(pub.events._eventListener);
207
208
209
/**
210
* Runs the default EasyRTC listener for a given event.
211
*
212
* @param {string} eventName EasyRTC event name.
213
* @param {...*} eventParam The event parameters
214
*/
215
pub.events.emitDefault = function() {
216
if (!pub.events.defaultListeners[arguments['0']]) {
217
console.error("Error emitting listener. No default for event '" + arguments['0'] + "' exists.");
218
return;
219
}
220
pub.events.defaultListeners[Array.prototype.shift.call(arguments)].apply(this, arguments);
221
};
222
223
224
/**
225
* Resets the listener for a given event to the default listener. Removes other listeners.
226
*
227
* @param {string} eventName EasyRTC event name.
228
*/
229
pub.events.setDefaultListener = function(eventName) {
230
if (!_.isFunction(pub.events.defaultListeners[eventName])) {
231
console.error("Error setting default listener. No default for event '" + eventName + "' exists.");
232
}
233
pub.events._eventListener.removeAllListeners(eventName);
234
pub.events._eventListener.on(eventName, pub.events.defaultListeners[eventName]);
235
};
236
237
238
/**
239
* Resets the listener for all EasyRTC events to the default listeners. Removes all other listeners.
240
*/
241
pub.events.setDefaultListeners = function() {
242
pub.events._eventListener.removeAllListeners();
243
for (var currentEventName in pub.events.defaultListeners) {
244
if (_.isFunction(pub.events.defaultListeners[currentEventName])) {
245
pub.events._eventListener.on(currentEventName, pub.events.defaultListeners[currentEventName]);
246
} else {
247
throw new pub.util.ServerError("Error setting default listener. No default for event '" + currentEventName + "' exists.");
248
}
249
}
250
};
251
252
253
/**
254
* Map of EasyRTC event listener names to their default functions. This map can be used to run a default function manually.
255
*/
256
pub.events.defaultListeners = {
257
"authenticate": eventListener.onAuthenticate,
258
"authenticated": eventListener.onAuthenticated,
259
"connection": eventListener.onConnection,
260
"disconnect": eventListener.onDisconnect,
261
"getIceConfig": eventListener.onGetIceConfig,
262
"roomCreate": eventListener.onRoomCreate,
263
"roomJoin": eventListener.onRoomJoin,
264
"roomLeave": eventListener.onRoomLeave,
265
"log": eventListener.onLog,
266
"shutdown": eventListener.onShutdown,
267
"startup": eventListener.onStartup,
268
"easyrtcAuth": eventListener.onEasyrtcAuth,
269
"easyrtcCmd": eventListener.onEasyrtcCmd,
270
"easyrtcMsg": eventListener.onEasyrtcMsg,
271
"emitEasyrtcCmd": eventListener.onEmitEasyrtcCmd,
272
"emitEasyrtcMsg": eventListener.onEmitEasyrtcMsg,
273
"emitError": eventListener.onEmitError,
274
"emitReturnAck": eventListener.onEmitReturnAck,
275
"emitReturnError": eventListener.onEmitReturnError,
276
"emitReturnToken": eventListener.onEmitReturnToken,
277
"msgTypeGetIceConfig": eventListener.onMsgTypeGetIceConfig,
278
"msgTypeGetRoomList": eventListener.onMsgTypeGetRoomList,
279
"msgTypeRoomJoin": eventListener.onMsgTypeRoomJoin,
280
"msgTypeRoomLeave": eventListener.onMsgTypeRoomLeave,
281
"msgTypeSetPresence": eventListener.onMsgTypeSetPresence,
282
"msgTypeSetRoomApiField": eventListener.onMsgTypeSetRoomApiField
283
};
284
285
286
/**
287
* Sets listener for a given EasyRTC event. Only one listener is allowed per event. Any other listeners for an event are removed before adding the new one. See the events documentation for expected listener parameters.
288
*
289
* @param {string} eventName Listener name.
290
* @param {function} listener Function to be called when listener is fired
291
*/
292
pub.events.on = function(eventName, listener) {
293
if (eventName && _.isFunction(listener)) {
294
pub.events._eventListener.removeAllListeners(eventName);
295
pub.events._eventListener.on(eventName, listener);
296
}
297
else {
298
pub.util.logError("Unable to add listener to event '" + eventName + "'");
299
}
300
};
301
302
303
/**
304
* Removes all listeners for an event. If there is a default EasyRTC listener, it will be added. If eventName is `null`, all events will be removed than the defaults will be restored.
305
*
306
* @param {?string} eventName Listener name. If `null`, then all events will be removed.
307
*/
308
pub.events.removeAllListeners = function(eventName) {
309
if (eventName) {
310
pub.events.setDefaultListener(eventName);
311
} else {
312
pub.events.setDefaultListeners();
313
}
314
};
315
316
317
/**
318
* General utility functions are grouped in this util object. For convenience, this class has also been attached to the application, connection, session, and room classes.
319
* @class
320
*/
321
pub.util = {};
322
323
324
/**
325
* Performs a deep copy of an object, returning the duplicate.
326
* Do not use on objects with circular references.
327
*
328
* @function
329
* @param {Object} input Input variable (or object) to be copied.
330
* @returns {Object} New copy of variable.
331
*/
332
pub.util.deepCopy = g.deepCopy;
333
334
335
/**
336
* An empty dummy function, which is designed to be used as a default callback in functions when none has been provided.
337
*
338
* @param {Error} err Error object
339
*/
340
pub.util.nextToNowhere = function(err) {
341
};
342
343
/**
344
* Determines if an Error object is an instance of ApplicationError, ConnectionError, or ServerError. If it is, it will return true.
345
*
346
* @function
347
* @param {*|Error} Will accept any value, but will only return true for appropriate error objects.
348
* @return {Boolean}
349
*/
350
pub.util.isError = eu.isError;
351
352
353
/**
354
* Determines if an Error object is an instance of ApplicationWarning, ConnectionWarning, or ServerWarning. If it is, it will return true.
355
*
356
* @function
357
* @param {*|Error} Will accept any value, but will only return true for appropriate error objects.
358
* @return {Boolean}
359
*/
360
pub.util.isWarning = eu.isWarning;
361
362
363
/**
364
* Custom Error Object for EasyRTC Application Errors.
365
*
366
* @extends Error
367
* @param {string} msg Text message describing the error.
368
* @returns {Error}
369
*/
370
pub.util.ApplicationError = eu.ApplicationError;
371
372
373
/**
374
* Custom Error Object for EasyRTC Application Warnings.
375
*
376
* @extends Error
377
* @param {string} msg Text message describing the error.
378
* @returns {Error}
379
*/
380
pub.util.ApplicationWarning = eu.ApplicationWarning;
381
382
383
/**
384
* Custom Error Object for EasyRTC C Errors.
385
*
386
* @function
387
* @extends Error
388
* @param {string} msg Text message describing the error.
389
* @returns {Error}
390
*/
391
pub.util.ConnectionError = eu.ConnectionError;
392
393
/**
394
* Custom Error Object for EasyRTC Connection Warnings.
395
*
396
* @function
397
* @extends Error
398
* @param {string} msg Text message describing the error.
399
* @returns {Error}
400
*/
401
pub.util.ConnectionWarning = eu.ConnectionWarning;
402
403
404
/**
405
* Custom Error Object for EasyRTC Server Errors.
406
*
407
* @function
408
* @extends Error
409
* @param {string} msg Text message describing the error.
410
* @returns {Error}
411
*/
412
pub.util.ServerError = eu.ServerError;
413
414
415
/**
416
* Custom Error Object for EasyRTC Server Warnings.
417
*
418
* @function
419
* @extends Error
420
* @param {string} msg Text message describing the error.
421
* @returns {Error}
422
*/
423
pub.util.ServerWarning = eu.ServerWarning;
424
425
426
/**
427
* Returns a random available easyrtcid.
428
*
429
* @function
430
* @return {String} Available easyrtcid. A unique identifier for an EasyRTC connection.
431
*/
432
pub.util.getAvailableEasyrtcid = eu.getAvailableEasyrtcid;
433
434
435
/**
436
* Returns an EasyRTC message error object for a specific error code. This is meant to be emitted or returned to a websocket client.
437
*
438
* @param {String} errorCode EasyRTC error code associated with an error.
439
* @return {Object} EasyRTC message error object for the specific error code.
440
*/
441
pub.util.getErrorMsg = function(errorCode) {
442
var msg = {
443
msgType: "error",
444
serverTime: Date.now(),
445
msgData: {
446
errorCode: errorCode,
447
errorText: pub.util.getErrorText(errorCode)
448
}
449
};
450
451
if (!msg.msgData.errorText) {
452
msg.msgData.errorText = "Error occurred with error code: " + errorCode;
453
pub.util.logWarning("Emitted unknown error with error code [" + errorCode + "]");
454
}
455
456
return msg;
457
};
458
459
460
/**
461
* Returns human readable text for a given error code. If an unknown error code is provided, a null value will be returned.
462
*
463
* @param {String} errorCode EasyRTC error code associated with an error.
464
* @return {string} Human readable error string
465
*/
466
pub.util.getErrorText = function(errorCode) {
467
switch (errorCode) {
468
case "BANNED_IP_ADDR":
469
return "Client IP address is banned. Socket will be disconnected.";
470
break;
471
case "LOGIN_APP_AUTH_FAIL":
472
return "Authentication for application failed. Socket will be disconnected.";
473
break;
474
case "LOGIN_BAD_APP_NAME":
475
return "Provided application name is improper. Socket will be disconnected.";
476
break;
477
case "LOGIN_BAD_AUTH":
478
return "Authentication for application failed. Socket will be disconnected.";
479
break;
480
case "LOGIN_BAD_ROOM":
481
return "Requested room is invalid or does not exist. Socket will be disconnected.";
482
break;
483
case "LOGIN_BAD_STRUCTURE":
484
return "Authentication for application failed. The provided structure is improper. Socket will be disconnected.";
485
break;
486
case "LOGIN_BAD_USER_CFG":
487
return "Provided configuration options improper or invalid. Socket will be disconnected.";
488
break;
489
case "LOGIN_GEN_FAIL":
490
return "Authentication failed. Socket will be disconnected.";
491
break;
492
case "LOGIN_NO_SOCKETS":
493
return "No sockets available for account. Socket will be disconnected.";
494
break;
495
case "LOGIN_TIMEOUT":
496
return "Login has timed out. Socket will be disconnected.";
497
break;
498
case "MSG_REJECT_BAD_DATA":
499
return "Message rejected. The provided msgData is improper.";
500
break;
501
case "MSG_REJECT_BAD_ROOM":
502
return "Message rejected. Requested room is invalid or does not exist.";
503
break;
504
case "MSG_REJECT_BAD_FIELD":
505
return "Message rejected. Problem with field structure or name.";
506
break;
507
case "MSG_REJECT_BAD_SIZE":
508
return "Message rejected. Packet size is too large.";
509
break;
510
case "MSG_REJECT_BAD_STRUCTURE":
511
return "Message rejected. The provided structure is improper.";
512
break;
513
case "MSG_REJECT_BAD_TYPE":
514
return "Message rejected. The provided msgType is unsupported.";
515
break;
516
case "MSG_REJECT_GEN_FAIL":
517
return "Message rejected. General failure occurred.";
518
break;
519
case "MSG_REJECT_NO_AUTH":
520
return "Message rejected. Not logged in or client not authorized.";
521
break;
522
case "MSG_REJECT_NO_ROOM_LIST":
523
return "Message rejected. Room list unavailable.";
524
break;
525
case "MSG_REJECT_PRESENCE":
526
return "Message rejected. Presence could could not be set.";
527
break;
528
case "MSG_REJECT_TARGET_EASYRTCID":
529
return "Message rejected. Target easyrtcid is invalid, not using same application, or no longer online.";
530
break;
531
case "MSG_REJECT_TARGET_GROUP":
532
return "Message rejected. Target group is invalid or not defined.";
533
break;
534
case "MSG_REJECT_TARGET_ROOM":
535
return "Message rejected. Target room is invalid or not created.";
536
break;
537
case "SERVER_SHUTDOWN":
538
return "Server is being shutdown. Socket will be disconnected.";
539
break;
540
default:
541
pub.util.logWarning("Unknown message errorCode requested [" + errorCode + "]");
542
return null;
543
}
544
};
545
546
547
/**
548
* General logging function which emits a log event so long as the log level has a severity equal or greater than e.option.logLevel
549
*
550
* @param {string} level Log severity level. Can be ("debug"|"info"|"warning"|"error")
551
* @param {string} logText Text for log.
552
* @param {?*} [logFields] Simple JSON object which contains extra fields to be logged.
553
*/
554
pub.util.log = function(level, logText, logFields) {
555
switch (e.option.logLevel) {
556
case "error":
557
if (level != "error") {
558
break;
559
}
560
561
case "warning":
562
if (level == "info") {
563
break;
564
}
565
566
case "info":
567
if (level == "debug") {
568
break;
569
}
570
571
case "debug":
572
pub.events.emit("log", level, logText, logFields);
573
}
574
};
575
576
577
/**
578
* Convenience function for logging "debug" level items.
579
*
580
* @param {string} logText Text for log.
581
* @param {?*} [logFields] Simple JSON object which contains extra fields to be logged.
582
*/
583
pub.util.logDebug = function(logText, logFields) {
584
pub.util.log("debug", logText, logFields);
585
};
586
587
588
/**
589
* Convenience function for logging "info" level items.
590
*
591
* @param {string} logText Text for log.
592
* @param {?*} [logFields] Simple JSON object which contains extra fields to be logged.
593
*/
594
pub.util.logInfo = function(logText, logFields) {
595
pub.util.log("info", logText, logFields);
596
};
597
598
599
/**
600
* Convenience function for logging "warning" level items.
601
*
602
* @param {string} logText Text for log.
603
* @param {?*} [logFields] Simple JSON object which contains extra fields to be logged.
604
*/
605
pub.util.logWarning = function(logText, logFields) {
606
pub.util.log("warning", logText, logFields);
607
};
608
609
610
/**
611
* Convenience function for logging "error" level items.
612
*
613
* @param {string} logText Text for log.
614
* @param {?*} [logFields] Simple JSON object which contains extra fields to be logged.
615
*/
616
pub.util.logError = function(logText, logFields) {
617
pub.util.log("error", logText, logFields);
618
};
619
620
621
/**
622
* Sends an 'ack' socket message to a given socketCallback. Provides additional checking and logging.
623
*
624
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
625
* @param {Function} socketCallback Socket.io callback function
626
* @param {?Object} appObj EasyRTC application object. Contains methods used for identifying and managing an application.
627
*/
628
pub.util.sendSocketCallbackAck = function(easyrtcid, socketCallback, appObj) {
629
return pub.util.sendSocketCallbackMsg(easyrtcid, socketCallback, {"msgType":"ack"}, appObj);
630
};
631
632
633
/**
634
* Sends a complete socket message to a given socketCallback. Provides additional checking and logging.
635
*
636
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
637
* @param {Function} socketCallback Socket.io callback function
638
* @param {Object} msg Message object which contains the full message for a client; this can include the standard msgType and msgData fields.
639
* @param {?Object} appObj EasyRTC application object. Contains methods used for identifying and managing an application.
640
*/
641
pub.util.sendSocketCallbackMsg = function(easyrtcid, socketCallback, msg, appObj) {
642
var appName;
643
644
if (appObj) {
645
appName = appObj.getAppName();
646
if (!appObj.isConnectedSync(easyrtcid)) {
647
pub.util.logDebug("["+appName+"]["+easyrtcid+"] Unable to return socket message. Peer no longer connected.");
648
return false;
649
}
650
}
651
652
if (!_.isFunction(socketCallback)) {
653
pub.util.logWarning("["+appName+"]["+easyrtcid+"] Unable to return socket message. Provided socketCallback was not a function.");
654
return false;
655
}
656
657
try {
658
socketCallback(msg);
659
} catch(err) {
660
pub.util.logWarning("["+appName+"]["+easyrtcid+"] Unable to return socket message. Call to socketCallback failed.");
661
}
662
663
if (e.option.logMessagesEnable) {
664
try {
665
pub.util.logDebug("["+appName+"]["+easyrtcid+"] Returning socket.io message: ["+JSON.stringify(msg)+"]");
666
}
667
catch(err) {
668
pub.util.logDebug("["+appName+"]["+easyrtcid+"] Returning socket.io message");
669
}
670
}
671
return true;
672
};
673
674
/**
675
* Checks with EasyRTC site for latest version. Writes to the log if a version can be found. If connection cannot be established than no error will be shown.
676
*/
677
pub.util.updateCheck = function() {
678
var easyrtcVersion = pub.getVersion();
679
680
require("http").get("http://easyrtc.com/version/?app=easyrtc&ver=" + easyrtcVersion + "&platform=" + process.platform + "&nodever=" + process.version, function(res) {
681
if (res.statusCode == 200)
682
res.on('data', function(latestVersion) {
683
latestVersion = (latestVersion + "").replace(/[^0-9a-z.]/g, "");
684
if (latestVersion != easyrtcVersion) {
685
var l = latestVersion.replace(/[^0-9.]/g, "").split(".", 3);
686
l[0] = parseInt(l[0]);
687
l[1] = parseInt(l[1]);
688
l[2] = parseInt(l[2]);
689
var v = easyrtcVersion.replace(/[^0-9.]/g, "").split(".", 3);
690
v[0] = parseInt(v[0]);
691
v[1] = parseInt(v[1]);
692
v[2] = parseInt(v[2]);
693
if (v[0] < l[0] || (v[0] == l[0] && v[1] < l[1]) || (v[0] == l[0] && v[1] == l[1] && v[2] < l[2]))
694
pub.util.logWarning("Update Check: New version of EasyRTC is available (" + latestVersion + "). Visit http://easyrtc.com/ for details or run 'npm update' to upgrade.");
695
else if (v[0] == l[0] && v[1] == l[1] && v[2] == l[2] && easyrtcVersion.replace(/[^a-z]/gi, "") != "")
696
pub.util.logWarning("Update Check: New non-beta version of EasyRTC is available (" + latestVersion + "). Visit http://easyrtc.com/ for details.");
697
}
698
});
699
}).on('error', function(e) {
700
});
701
};
702
703
704
/**
705
* Checks an incoming EasyRTC message to determine if it is syntactically valid.
706
*
707
* @param {string} type The Socket.IO message type. Expected values are (easyrtcAuth|easyrtcCmd|easyrtcMsg)
708
* @param {Object} msg Message object which contains the full message from a client; this can include the standard msgType and msgData fields.
709
* @param {?Object} appObj EasyRTC application object. Contains methods used for identifying and managing an application.
710
* @param {function(?Error, boolean, string)} callback Callback with error, a boolean of whether message if valid, and a string indicating the error code if the message is invalid.
711
*/
712
pub.util.isValidIncomingMessage = function(type, msg, appObj, callback) {
713
// A generic getOption variable which points to the getOption function at either the top or application level
714
var getOption = (_.isObject(appObj) ? appObj.getOption : pub.getOption);
715
716
// All messages follow the basic structure
717
if (!_.isString(type)) {
718
callback(null, false, "MSG_REJECT_BAD_TYPE");
719
return;
720
}
721
if (!_.isObject(msg)) {
722
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
723
return;
724
}
725
if (!_.isString(msg.msgType)) {
726
callback(null, false, "MSG_REJECT_BAD_TYPE");
727
return;
728
}
729
730
switch (type) {
731
case "easyrtcAuth":
732
if (msg.msgType != "authenticate") {
733
callback(null, false, "MSG_REJECT_BAD_TYPE");
734
return;
735
}
736
if (!_.isObject(msg.msgData)) {
737
callback(null, false, "MSG_REJECT_BAD_DATA");
738
return;
739
}
740
741
// msgData.apiVersion (required)
742
if (msg.msgData.apiVersion === undefined || !_.isString(msg.msgData.apiVersion) || !getOption("apiVersionRegExp").test(msg.msgData.apiVersion)) {
743
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
744
return;
745
}
746
747
// msgData.appName
748
if (msg.msgData.applicationName !== undefined && (!_.isString(msg.msgData.applicationName) || !getOption("appNameRegExp").test(msg.msgData.applicationName))) {
749
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
750
return;
751
}
752
753
// msgData.easyrtcsid
754
if (msg.msgData.easyrtcsid !== undefined && (!_.isString(msg.msgData.easyrtcsid) || !getOption("easyrtcsidRegExp").test(msg.msgData.easyrtcsid))) {
755
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
756
return;
757
}
758
759
var isCallbackRun = false;
760
async.waterfall([
761
function(asyncCallback) {
762
if (!appObj) {
763
pub.app((msg.msgData.applicationName !== undefined ? msg.msgData.applicationName : getOption("appDefaultName")), function(err, newAppObj) {
764
if (!err) {
765
appObj = newAppObj;
766
getOption = appObj.getOption;
767
}
768
asyncCallback(null);
769
});
770
}
771
else {
772
asyncCallback(null);
773
}
774
},
775
function(asyncCallback) {
776
// msgData.username
777
if (msg.msgData.username !== undefined && (!_.isString(msg.msgData.username) || !getOption("usernameRegExp").test(msg.msgData.username))) {
778
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
779
isCallbackRun = true;
780
return;
781
}
782
783
// msgData.credential
784
if (msg.msgData.credential !== undefined && (!_.isObject(msg.msgData.credential) || _.isEmpty(msg.msgData.credential))) {
785
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
786
isCallbackRun = true;
787
return;
788
}
789
790
// msgData.roomJoin
791
if (msg.msgData.roomJoin !== undefined) {
792
if (!_.isObject(msg.msgData.roomJoin)) {
793
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
794
isCallbackRun = true;
795
return;
796
}
797
798
for (var currentRoomName in msg.msgData.roomJoin) {
799
if (!getOption("roomNameRegExp").test(currentRoomName) || !_.isObject(msg.msgData.roomJoin[currentRoomName]) || !_.isString(msg.msgData.roomJoin[currentRoomName].roomName) || currentRoomName != msg.msgData.roomJoin[currentRoomName].roomName) {
800
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
801
isCallbackRun = true;
802
return;
803
}
804
// if roomParameter field is defined, it must be an object
805
if (msg.msgData.roomJoin[currentRoomName].roomParameter !== undefined && !_.isObject(msg.msgData.roomJoin[currentRoomName].roomParameter)) {
806
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
807
isCallbackRun = true;
808
return;
809
}
810
}
811
}
812
813
// msgData.setPresence
814
if (msg.msgData.setPresence !== undefined) {
815
if (!_.isObject(msg.msgData.setPresence) || _.isEmpty(msg.msgData.setPresence)) {
816
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
817
isCallbackRun = true;
818
return;
819
}
820
if (msg.msgData.setPresence.show !== undefined && (!_.isString(msg.msgData.setPresence.show) || !getOption("presenceShowRegExp").test(msg.msgData.setPresence.show))) {
821
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
822
isCallbackRun = true;
823
return;
824
}
825
if (msg.msgData.setPresence.status !== undefined && (!_.isString(msg.msgData.setPresence.status) || !getOption("presenceStatusRegExp").test(msg.msgData.setPresence.status))) {
826
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
827
isCallbackRun = true;
828
return;
829
}
830
}
831
832
// TODO: setUserCfg
833
if (msg.msgData.setUserCfg !== undefined) {
834
}
835
asyncCallback(null);
836
837
}
838
],
839
function(err) {
840
if (err) {
841
if (!isCallbackRun) {
842
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
843
isCallbackRun = true;
844
}
845
}
846
else {
847
// Incoming message syntactically valid
848
callback(null, true, null);
849
}
850
}
851
);
852
853
return;
854
break;
855
856
case "easyrtcCmd":
857
switch (msg.msgType) {
858
case "candidate" :
859
case "offer" :
860
case "answer" :
861
// candidate, offer, and answer each require a non-empty msgData object and a proper targetEasyrtcid
862
if (!_.isObject(msg.msgData) || _.isEmpty(msg.msgData)) {
863
callback(null, false, "MSG_REJECT_BAD_DATA");
864
return;
865
}
866
if (!_.isString(msg.targetEasyrtcid) || !getOption("easyrtcidRegExp").test(msg.targetEasyrtcid)) {
867
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
868
return;
869
}
870
break;
871
case "reject" :
872
case "hangup" :
873
// reject, and hangup each require a targetEasyrtcid but no msgData
874
if (msg.msgData !== undefined) {
875
callback(null, false, "MSG_REJECT_BAD_DATA");
876
return;
877
}
878
if (!_.isString(msg.targetEasyrtcid) || !getOption("easyrtcidRegExp").test(msg.targetEasyrtcid)) {
879
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
880
return;
881
}
882
break;
883
884
case "getIceConfig" :
885
if (msg.msgData !== undefined && !_.isEmpty(msg.msgData)) {
886
callback(null, false, "MSG_REJECT_BAD_DATA");
887
return;
888
}
889
break;
890
891
case "getRoomList" :
892
if (msg.msgData !== undefined) {
893
callback(null, false, "MSG_REJECT_BAD_DATA");
894
return;
895
}
896
break;
897
898
case "roomJoin" :
899
if (!_.isObject(msg.msgData)) {
900
callback(null, false, "MSG_REJECT_BAD_DATA");
901
return;
902
}
903
if (!_.isObject(msg.msgData.roomJoin)) {
904
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
905
return;
906
}
907
908
for (var currentRoomName in msg.msgData.roomJoin) {
909
if (!getOption("roomNameRegExp").test(currentRoomName) || !_.isObject(msg.msgData.roomJoin[currentRoomName]) || !_.isString(msg.msgData.roomJoin[currentRoomName].roomName) || currentRoomName != msg.msgData.roomJoin[currentRoomName].roomName) {
910
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
911
return;
912
}
913
}
914
break;
915
916
case "roomLeave" :
917
if (!_.isObject(msg.msgData)) {
918
callback(null, false, "MSG_REJECT_BAD_DATA");
919
return;
920
}
921
if (!_.isObject(msg.msgData.roomLeave)) {
922
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
923
return;
924
}
925
926
for (var currentRoomName in msg.msgData.roomLeave) {
927
if (!getOption("roomNameRegExp").test(currentRoomName) || !_.isObject(msg.msgData.roomLeave[currentRoomName]) || !_.isString(msg.msgData.roomLeave[currentRoomName].roomName) || currentRoomName != msg.msgData.roomLeave[currentRoomName].roomName) {
928
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
929
return;
930
}
931
}
932
break;
933
934
case "setPresence" :
935
if (!_.isObject(msg.msgData)) {
936
callback(null, false, "MSG_REJECT_BAD_DATA");
937
return;
938
}
939
if (!_.isObject(msg.msgData.setPresence) || _.isEmpty(msg.msgData.setPresence)) {
940
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
941
return;
942
}
943
if (msg.msgData.setPresence.show !== undefined && (!_.isString(msg.msgData.setPresence.show) || !getOption("presenceShowRegExp").test(msg.msgData.setPresence.show))) {
944
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
945
return;
946
}
947
if (msg.msgData.setPresence.status !== undefined && (!_.isString(msg.msgData.setPresence.status) || !getOption("presenceStatusRegExp").test(msg.msgData.setPresence.status))) {
948
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
949
return;
950
}
951
break;
952
953
case "setRoomApiField" :
954
if (!_.isObject(msg.msgData)) {
955
callback(null, false, "MSG_REJECT_BAD_DATA");
956
return;
957
}
958
if (!_.isObject(msg.msgData.setRoomApiField) || _.isEmpty(msg.msgData.setRoomApiField)) {
959
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
960
return;
961
}
962
if (!_.isString(msg.msgData.setRoomApiField.roomName) || !getOption("roomNameRegExp").test(msg.msgData.setRoomApiField.roomName)) {
963
callback(null, false, "MSG_REJECT_BAD_ROOM");
964
return;
965
}
966
if (msg.msgData.setRoomApiField.field !== undefined) {
967
if (!_.isObject(msg.msgData.setRoomApiField.field)) {
968
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
969
return;
970
}
971
try {
972
if (JSON.stringify(msg.msgData.setRoomApiField.field).length >= 4096) {
973
callback(null, false, "MSG_REJECT_BAD_SIZE");
974
return;
975
}
976
} catch (e) {
977
if (!_.isObject(msg.msgData.setRoomApiField.field)) {
978
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
979
return;
980
}
981
}
982
}
983
break;
984
985
case "setUserCfg" :
986
if (!_.isObject(msg.msgData)) {
987
callback(null, false, "MSG_REJECT_BAD_DATA");
988
return;
989
}
990
if (!_.isObject(msg.msgData.setUserCfg) || _.isEmpty(msg.msgData.setUserCfg)) {
991
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
992
return;
993
}
994
995
// setUserCfg.p2pList
996
if (msg.msgData.setUserCfg.p2pList !== undefined && (!_.isObject(msg.msgData.setUserCfg.p2pList) || _.isEmpty(msg.msgData.setUserCfg.p2pList))) {
997
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
998
return;
999
}
1000
// TODO: Go through p2pList to confirm each key is an easyrtcid
1001
1002
// setUserCfg.userSettings
1003
if (msg.msgData.setUserCfg.userSettings !== undefined && (!_.isObject(msg.msgData.setUserCfg.userSettings) || _.isEmpty(msg.msgData.setUserCfg.userSettings))) {
1004
callback(null, false, "MSG_REJECT_BAD_STRUCTURE");
1005
return;
1006
}
1007
1008
break;
1009
1010
default:
1011
// Reject all unknown msgType's
1012
callback(null, false, "MSG_REJECT_BAD_TYPE");
1013
return;
1014
}
1015
1016
break;
1017
1018
case "easyrtcMsg":
1019
// targetEasyrtcid
1020
if (msg.targetEasyrtcid !== undefined && (!_.isString(msg.targetEasyrtcid) || !getOption("easyrtcidRegExp").test(msg.targetEasyrtcid))) {
1021
callback(null, false, "MSG_REJECT_TARGET_EASYRTCID");
1022
return;
1023
}
1024
// targetGroup
1025
if (msg.targetGroup !== undefined && (!_.isString(msg.targetGroup) || !getOption("groupNameRegExp").test(msg.targetGroup))) {
1026
callback(null, false, "MSG_REJECT_TARGET_GROUP");
1027
return;
1028
}
1029
// targetRoom
1030
if (msg.targetRoom !== undefined && (!_.isString(msg.targetRoom) || !getOption("roomNameRegExp").test(msg.targetRoom))) {
1031
callback(null, false, "MSG_REJECT_TARGET_ROOM");
1032
return;
1033
}
1034
break;
1035
1036
default:
1037
callback(null, false, "MSG_REJECT_BAD_TYPE");
1038
return;
1039
}
1040
1041
// Incoming message syntactically valid
1042
callback(null, true, null);
1043
};
1044
1045
1046
/**
1047
* Will attempt to deliver an EasyRTC session id via a cookie. Requires that session management be enabled from within Express.
1048
*
1049
* @param {Object} req Http request object
1050
* @param {Object} res Http result object
1051
*/
1052
pub.util.sendSessionCookie = function(req, res) {
1053
// If sessions or session cookies are disabled, return without an error.
1054
if (!pub.getOption("sessionEnable") || !pub.getOption("sessionCookieEnable")) {
1055
return;
1056
}
1057
if (req.sessionID && (!req.cookies || !req.cookies["easyrtcsid"] || req.cookies["easyrtcsid"] != req.sessionID)) {
1058
try {
1059
pub.util.logDebug("Sending easyrtcsid cookie [" + req.sessionID + "] to [" + req.ip + "] for request [" + req.url + "]");
1060
res.cookie("easyrtcsid", req.sessionID, {maxAge: 2592000000, httpOnly: false});
1061
} catch (e) {
1062
pub.util.logWarning("Problem setting easyrtcsid cookie [" + req.sessionID + "] to [" + req.ip + "] for request [" + req.url + "]");
1063
}
1064
}
1065
};
1066
1067
1068
/**
1069
* Determine if a given application name has been defined.
1070
*
1071
* @param {string} appName Application name which uniquely identifies it on the server.
1072
* @param {function(?Error, boolean)} callback Callback with error and boolean of whether application is defined.
1073
*/
1074
pub.isApp = function(appName, callback) {
1075
callback(null, (e.app[appName] ? true : false));
1076
};
1077
1078
1079
/**
1080
* Creates a new EasyRTC application with default values. If a callback is provided, it will receive the new application object.
1081
*
1082
* The callback may receive an Error object if unsuccessful. Depending on the severity, known errors have an "instanceof" ApplicationWarning or ApplicationError.
1083
*
1084
* @param {string} appName Application name which uniquely identifies it on the server.
1085
* @param {?object} options Options object with options to apply to the application. May be null.
1086
* @param {appCallback} [callback] Callback with error and application object
1087
*/
1088
pub.createApp = function(appName, options, callback) {
1089
if (!_.isFunction(callback)) {
1090
callback = function(err, appObj) {
1091
};
1092
}
1093
if (!appName || !pub.getOption("appNameRegExp").test(appName)) {
1094
pub.util.logWarning("Can not create application with improper name: '" + appName + "'");
1095
callback(new pub.util.ApplicationWarning("Can not create application with improper name: '" + appName + "'"));
1096
return;
1097
}
1098
if (e.app[appName]) {
1099
pub.util.logWarning("Can not create application which already exists: '" + appName + "'");
1100
callback(new pub.util.ApplicationWarning("Can not create application which already exists: '" + appName + "'"));
1101
return;
1102
}
1103
if (!_.isObject(options)) {
1104
options = {};
1105
}
1106
1107
pub.util.logDebug("Creating application: '" + appName + "'");
1108
1109
e.app[appName] = {
1110
appName: appName,
1111
connection: {},
1112
field: {},
1113
group: {},
1114
option: {},
1115
room: {},
1116
session: {}
1117
};
1118
1119
// Get the new app object
1120
pub.app(appName, function(err, appObj) {
1121
if (err) {
1122
callback(err);
1123
return;
1124
}
1125
1126
// Set all options in options object. If any fail, an error will be sent to the callback.
1127
async.each(Object.keys(options), function(currentOptionName, asyncCallback) {
1128
appObj.setOption(currentOptionName, options[currentOptionName]);
1129
asyncCallback(null);
1130
},
1131
function(err) {
1132
if (err) {
1133
callback(new pub.util.ApplicationError("Could not set options when creating application: '" + appName + "'", err));
1134
return;
1135
}
1136
// Set default application fields
1137
var appDefaultFieldObj = appObj.getOption("appDefaultFieldObj");
1138
if (_.isObject(appDefaultFieldObj)) {
1139
for (var currentFieldName in appDefaultFieldObj) {
1140
appObj.setField(
1141
currentFieldName,
1142
appDefaultFieldObj[currentFieldName].fieldValue,
1143
appDefaultFieldObj[currentFieldName].fieldOption,
1144
null
1145
);
1146
}
1147
}
1148
1149
if (appObj.getOption("roomDefaultEnable")) {
1150
pub.events.emit("roomCreate", appObj, null, appObj.getOption("roomDefaultName"), null, function(err, roomObj){
1151
if (err) {
1152
callback(err);
1153
return;
1154
}
1155
// Return app object to callback
1156
callback(null, appObj);
1157
});
1158
}
1159
else {
1160
// Return app object to callback
1161
callback(null, appObj);
1162
}
1163
});
1164
});
1165
};
1166
1167
1168
/**
1169
* Contains the methods for interfacing with an EasyRTC application.
1170
*
1171
* The callback will receive an application object upon successful retrieval of application.
1172
*
1173
* The callback may receive an Error object if unsuccessful. Depending on the severity, known errors have an "instanceof" ApplicationWarning or ApplicationError.
1174
*
1175
* The function does return an application object which is useful for chaining, however the callback approach is safer and provides additional information in the event of an error.
1176
*
1177
* @param {?string} appName Application name which uniquely identifies it on the server. Uses default application if null.
1178
* @param {appCallback} [callback] Callback with error and application object
1179
*/
1180
pub.app = function(appName, callback) {
1181
1182
/**
1183
* The primary method for interfacing with an EasyRTC application.
1184
*
1185
* @class appObj
1186
* @memberof pub
1187
*/
1188
var appObj = {};
1189
if (!appName) {
1190
appName = pub.getOption("appDefaultName");
1191
}
1192
if (!_.isFunction(callback)) {
1193
callback = function(err, appObj) {
1194
};
1195
}
1196
if (!e.app[appName]) {
1197
pub.util.logDebug("Attempt to request non-existent application name: '" + appName + "'");
1198
callback(new pub.util.ApplicationWarning("Attempt to request non-existent application name: '" + appName + "'"));
1199
return;
1200
}
1201
1202
1203
/**
1204
* Expose all event functions
1205
*
1206
* @memberof pub.appObj
1207
*/
1208
appObj.events = pub.events;
1209
1210
1211
/**
1212
* Expose all utility functions
1213
*
1214
* @memberof pub.appObj
1215
*/
1216
appObj.util = pub.util;
1217
1218
1219
/**
1220
* Returns the application name for the application. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
1221
*
1222
* @memberof pub.appObj
1223
* @return {string} The application name.
1224
*/
1225
appObj.getAppName = function() {
1226
return appName;
1227
};
1228
1229
1230
/**
1231
* Sends the count of the number of connections in the app to a provided callback.
1232
*
1233
* @memberof pub.appObj
1234
* @param {function(?Error, Number)} callback Callback with error and array containing all easyrtcids.
1235
*/
1236
appObj.getConnectionCount = function(callback) {
1237
callback(null, appObj.getConnectionCountSync());
1238
};
1239
1240
1241
/**
1242
* Sends the count of the number of connections in the app to a provided callback.
1243
*
1244
* @memberof pub.appObj
1245
* @returns {Number} The current number of connections in a room.
1246
*/
1247
appObj.getConnectionCountSync = function() {
1248
return _.size(e.app[appName].connection);
1249
};
1250
1251
1252
/**
1253
* Returns an array of all easyrtcids connected to the application
1254
*
1255
* @memberof pub.appObj
1256
* @param {function(?Error, Array.<string>)} callback Callback with error and array of easyrtcids.
1257
*/
1258
appObj.getConnectionEasyrtcids = function(callback) {
1259
var easyrtcids = [];
1260
for (var key in e.app[appName].connection) {
1261
easyrtcids.push(key);
1262
}
1263
callback(null, easyrtcids);
1264
};
1265
1266
1267
/**
1268
* Returns application level field object for a given field name to a provided callback.
1269
*
1270
* @memberof pub.appObj
1271
* @param {string} fieldName Field name
1272
* @param {function(?Error, Object=)} callback Callback with error and field object (any type)
1273
*/
1274
appObj.getField = function(fieldName, callback) {
1275
if (!e.app[appName].field[fieldName]) {
1276
pub.util.logDebug("Can not find app field: '" + fieldName + "'");
1277
callback(new pub.util.ApplicationWarning("Can not find app field: '" + fieldName + "'"));
1278
return;
1279
}
1280
callback(null, pub.util.deepCopy(e.app[appName].field[fieldName]));
1281
};
1282
1283
1284
/**
1285
* Returns application level field object for a given field name. If the field is not set, it will return a field object will a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
1286
*
1287
* @memberof pub.appObj
1288
* @param {string} fieldName Field name
1289
* @returns {Object} Field object
1290
*/
1291
appObj.getFieldSync = function(fieldName) {
1292
if (!e.app[appName].field[fieldName]) {
1293
return {"fieldName": fieldName, "fieldOption": {}, "fieldValue": null};
1294
}
1295
return pub.util.deepCopy(e.app[appName].field[fieldName]);
1296
};
1297
1298
1299
/**
1300
* Returns application level field value for a given field name. If the field is not set, it will return a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
1301
*
1302
* @memberof pub.appObj
1303
* @param {string} fieldName Field name
1304
* @returns {?*} Field value. Can be any JSON object.
1305
*/
1306
appObj.getFieldValueSync = function(fieldName) {
1307
if (!e.app[appName].field[fieldName]) {
1308
return null;
1309
}
1310
return pub.util.deepCopy(e.app[appName].field[fieldName].fieldValue);
1311
};
1312
1313
1314
/**
1315
* Returns an object containing all field names and values within the application. Can be limited to fields with isShared option set to true.
1316
*
1317
* @memberof pub.appObj
1318
* @param {boolean} limitToIsShared Limits returned fields to those which have the isShared option set to true.
1319
* @param {function(?Error, Object=)} callback Callback with error and object containing field names and values.
1320
*/
1321
appObj.getFields = function(limitToIsShared, callback) {
1322
var fieldObj = {};
1323
for (var fieldName in e.app[appName].field) {
1324
if (!limitToIsShared || e.app[appName].field[fieldName].fieldOption.isShared) {
1325
fieldObj[fieldName] = {
1326
fieldName: fieldName,
1327
fieldValue: pub.util.deepCopy(e.app[appName].field[fieldName].fieldValue)
1328
};
1329
}
1330
}
1331
callback(null, fieldObj);
1332
};
1333
1334
1335
/**
1336
* Returns an array of all group names within the application
1337
*
1338
* @memberof pub.appObj
1339
* @param {function(?Error, Array.<string>)} callback Callback with error and array of group names.
1340
*/
1341
appObj.getGroupNames = function(callback) {
1342
var groupNames = [];
1343
for (var key in e.app[appName].group) {
1344
groupNames.push(key);
1345
}
1346
callback(null, groupNames);
1347
};
1348
1349
1350
/**
1351
* Gets individual option value. Will first check if option is defined for the application, else it will revert to the global level option.
1352
*
1353
* @memberof pub.appObj
1354
* @param {String} optionName Option name
1355
* @return {*} Option value (can be any JSON type)
1356
*/
1357
appObj.getOption = function(optionName) {
1358
return ((e.app[appName].option[optionName] === undefined) ? pub.getOption(optionName) : (e.app[appName].option[optionName]));
1359
};
1360
1361
1362
/**
1363
* Returns an array of all room names within the application.
1364
*
1365
* @memberof pub.appObj
1366
* @param {function(?Error, Array.<string>)} callback Callback with error and array of room names.
1367
*/
1368
appObj.getRoomNames = function(callback) {
1369
var roomNames = [];
1370
for (var key in e.app[appName].room) {
1371
roomNames.push(key);
1372
}
1373
callback(null, roomNames);
1374
};
1375
1376
1377
/**
1378
* Returns an array of all easyrtcsids within the application
1379
*
1380
* @memberof pub.appObj
1381
* @param {function(?Error, Array.<string>)} callback Callback with error and array containing easyrtcsids.
1382
*/
1383
appObj.getEasyrtcsids = function(callback) {
1384
var easyrtcsids = [];
1385
for (var key in e.app[appName].session) {
1386
easyrtcsids.push(key);
1387
}
1388
callback(null, easyrtcsids);
1389
};
1390
1391
/**
1392
* Returns an array of all easyrtcsids within the application. Old SessionKey name kept for transition purposes. Use getEasyrtcsid();
1393
*
1394
* @memberof pub.appObj
1395
* @ignore
1396
*/
1397
appObj.getSessionKeys = appObj.getEasyrtcsids;
1398
1399
1400
/**
1401
* Gets connection status for a connection. It is possible for a connection to be considered connected without being authenticated.
1402
*
1403
* @memberof pub.appObj
1404
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
1405
* @param {function(?Error, Boolean)} callback Callback with error and a boolean indicating if easyrtcid is connected.
1406
*/
1407
appObj.isConnected = function(easyrtcid, callback) {
1408
if (e.app[appName] && e.app[appName].connection && e.app[appName].connection[easyrtcid]) {
1409
callback(null, true);
1410
} else {
1411
callback(null, false);
1412
}
1413
};
1414
1415
1416
/**
1417
* Gets connection status for a connection. It is possible for a connection to be considered connected without being authenticated. Synchronous function.
1418
*
1419
* @memberof pub.appObj
1420
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
1421
* @returns {boolean}
1422
*/
1423
appObj.isConnectedSync = function(easyrtcid) {
1424
if (e.app[appName] && e.app[appName].connection && e.app[appName].connection[easyrtcid]) {
1425
return true;
1426
} else {
1427
return false;
1428
}
1429
};
1430
1431
1432
/**
1433
* Sets individual option. Set value to NULL to delete the option (thus reverting to global option).
1434
*
1435
* @memberof pub.appObj
1436
* @param {String} optionName Option name
1437
* @param {?*} optionValue Option value
1438
* @return {Boolean} true on success, false on failure
1439
*/
1440
appObj.setOption = function(optionName, optionValue) {
1441
// Can only set options which currently exist
1442
if (typeof e.option[optionName] == "undefined") {
1443
pub.util.logError("Error setting option. Unrecognised option name '" + optionName + "'.");
1444
return false;
1445
}
1446
1447
// If value is null, delete option from application (reverts to global option)
1448
if (optionValue == null) {
1449
if (!(e.app[appName].option[optionName] === 'undefined')) {
1450
delete e.app[appName].option[optionName];
1451
}
1452
} else {
1453
// Set the option value to be a full deep copy, thus preserving private nature of the private EasyRTC object.
1454
e.app[appName].option[optionName] = pub.util.deepCopy(optionValue);
1455
}
1456
return true;
1457
};
1458
1459
1460
/**
1461
* Sets application field value for a given field name.
1462
*
1463
* @memberof pub.appObj
1464
* @param {string} fieldName Must be formatted according to "fieldNameRegExp" option.
1465
* @param {Object} fieldValue
1466
* @param {?Object} fieldOption Field options (such as isShared which defaults to false)
1467
* @param {nextCallback} [next] A success callback of form next(err).
1468
*/
1469
appObj.setField = function(fieldName, fieldValue, fieldOption, next) {
1470
pub.util.logDebug("[" + appName + "] Setting field [" + fieldName + "]", fieldValue);
1471
if (!_.isFunction(next)) {
1472
next = pub.util.nextToNowhere;
1473
}
1474
1475
if (!pub.getOption("fieldNameRegExp").test(fieldName)) {
1476
pub.util.logWarning("Can not create application field with improper name: '" + fieldName + "'");
1477
next(new pub.util.ApplicationWarning("Can not create application field with improper name: '" + fieldName + "'"));
1478
return;
1479
}
1480
e.app[appName].field[fieldName] = {
1481
fieldName: fieldName,
1482
fieldValue: fieldValue,
1483
fieldOption: {isShared: ((_.isObject(fieldOption) && fieldOption.isShared) ? true : false)}
1484
};
1485
1486
next(null);
1487
};
1488
1489
1490
/**
1491
* Gets connection object for a given connection key. Returns null if connection not found.
1492
* The returned connection object includes functions for managing connection fields.
1493
*
1494
* @memberof pub.appObj
1495
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
1496
* @param {connectionCallback} callback Callback with error and object containing EasyRTC connection object.
1497
*/
1498
appObj.connection = function(easyrtcid, callback) {
1499
if (!e.app[appName].connection[easyrtcid]) {
1500
pub.util.logWarning("Attempt to request non-existent connection key: '" + easyrtcid + "'");
1501
callback(new pub.util.ConnectionWarning("Attempt to request non-existent connection key: '" + easyrtcid + "'"));
1502
return;
1503
}
1504
1505
if (!pub.socketServer) {
1506
pub.util.logError("Socket server undefined.");
1507
callback(new pub.util.ConnectionWarning("Attempt to request non-existent socket: '" + easyrtcid + "'"));
1508
return;
1509
}
1510
1511
var socketId = e.app[appName].connection[easyrtcid].socketId;
1512
1513
if (pub.socketServer.sockets.connected){
1514
if (!pub.socketServer.sockets.connected[socketId] || pub.socketServer.sockets.connected[socketId].disconnected) {
1515
pub.util.logWarning("["+easyrtcid+"] Attempt to request non-existent socket: '" + socketId + "'");
1516
callback(new pub.util.ConnectionWarning("Attempt to request non-existent socket: '" + socketId + "'"));
1517
return;
1518
}
1519
1520
if (pub.socketServer.sockets.connected[socketId].disconnected) {
1521
pub.util.logWarning("["+easyrtcid+"] Attempt to request disconnected socket: '" + socketId + "'");
1522
callback(new pub.util.ConnectionWarning("Attempt to request disconnected socket: '" + socketId + "'"));
1523
return;
1524
}
1525
}
1526
else {
1527
if (!pub.socketServer.sockets.sockets[socketId] || pub.socketServer.sockets.sockets[socketId].disconnected) {
1528
pub.util.logWarning("["+easyrtcid+"] Attempt to request non-existent socket: '" + socketId + "'");
1529
callback(new pub.util.ConnectionWarning("Attempt to request non-existent socket: '" + socketId + "'"));
1530
return;
1531
}
1532
1533
if (pub.socketServer.sockets.sockets[socketId].disconnected) {
1534
pub.util.logWarning("["+easyrtcid+"] Attempt to request disconnected socket: '" + socketId + "'");
1535
callback(new pub.util.ConnectionWarning("Attempt to request disconnected socket: '" + socketId + "'"));
1536
return;
1537
}
1538
}
1539
1540
1541
/**
1542
* @class connectionObj
1543
* @memberof pub.appObj
1544
*/
1545
var connectionObj = {};
1546
1547
// House the local session object
1548
var _sessionObj;
1549
1550
/**
1551
* Expose all event functions
1552
*
1553
* @memberof pub.appObj.connectionObj
1554
*/
1555
connectionObj.events = pub.events;
1556
1557
1558
/**
1559
* Expose all utility functions
1560
*
1561
* @memberof pub.appObj.connectionObj
1562
*/
1563
connectionObj.util = pub.util;
1564
1565
1566
/**
1567
* Reference to connection's socket.io object. See http://socket.io/ for more information.
1568
*
1569
* @memberof pub.appObj.connectionObj
1570
*/
1571
if (pub.socketServer.sockets.connected){
1572
connectionObj.socket = pub.socketServer.sockets.connected[socketId];
1573
}
1574
else {
1575
connectionObj.socket = pub.socketServer.sockets.sockets[socketId];
1576
}
1577
1578
1579
/**
1580
* Returns the application object to which the connection belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
1581
*
1582
* @memberof pub.appObj.connectionObj
1583
* @return {Object} The application object
1584
*/
1585
connectionObj.getApp = function() {
1586
return appObj;
1587
};
1588
1589
1590
/**
1591
* Returns the application name for the application to which the connection belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
1592
*
1593
* @memberof pub.appObj.connectionObj
1594
* @return {string} The application name
1595
*/
1596
connectionObj.getAppName = function() {
1597
return appName;
1598
};
1599
1600
1601
/**
1602
* Returns the easyrtcid for the connection. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
1603
*
1604
* @memberof pub.appObj.connectionObj
1605
* @return {string} Returns the connection's easyrtcid, which is the EasyRTC unique identifier for a socket connection.
1606
*/
1607
connectionObj.getEasyrtcid = function() {
1608
return easyrtcid;
1609
};
1610
1611
1612
/**
1613
* Returns connection level field object for a given field name to a provided callback.
1614
*
1615
* @memberof pub.appObj.connectionObj
1616
* @param {string} fieldName Field name
1617
* @param {function(?Error, Object=)} callback Callback with error and field object (any type)
1618
*/
1619
connectionObj.getField = function(fieldName, callback) {
1620
if (!e.app[appName].connection[easyrtcid].field[fieldName]) {
1621
pub.util.logDebug("Can not find connection field: '" + fieldName + "'");
1622
callback(new pub.util.ApplicationWarning("Can not find connection field: '" + fieldName + "'"));
1623
return;
1624
}
1625
callback(null, pub.util.deepCopy(e.app[appName].connection[easyrtcid].field[fieldName]));
1626
};
1627
1628
1629
/**
1630
* Returns connection level field object for a given field name. If the field is not set, it will return a field object will a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
1631
*
1632
* @memberof pub.appObj.connectionObj
1633
* @param {string} fieldName Field name
1634
* @returns {Object} Field object
1635
*/
1636
connectionObj.getFieldSync = function(fieldName) {
1637
if (!e.app[appName].connection[easyrtcid].field[fieldName]) {
1638
return {"fieldName": fieldName, "fieldOption": {}, "fieldValue": null};
1639
}
1640
return pub.util.deepCopy(e.app[appName].connection[easyrtcid].field[fieldName]);
1641
};
1642
1643
1644
/**
1645
* Returns connection level field value for a given field name. If the field is not set, it will return a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
1646
*
1647
* @memberof pub.appObj.connectionObj
1648
* @param {string} fieldName Field name
1649
* @returns {?*} Field value
1650
*/
1651
connectionObj.getFieldValueSync = function(fieldName) {
1652
if (!e.app[appName].connection[easyrtcid].field[fieldName]) {
1653
return null;
1654
}
1655
return pub.util.deepCopy(e.app[appName].connection[easyrtcid].field[fieldName].fieldValue);
1656
};
1657
1658
1659
/**
1660
* Returns an object containing all field names and values within the connection to a provided callback. Can be limited to fields with isShared option set to true.
1661
*
1662
* @memberof pub.appObj.connectionObj
1663
* @param {boolean} limitToIsShared Limits returned fields to those which have the isShared option set to true.
1664
* @param {function(?Error, Object=)} callback Callback with error and object containing field names and values.
1665
*/
1666
connectionObj.getFields = function(limitToIsShared, callback) {
1667
var fieldObj = {};
1668
for (var fieldName in e.app[appName].connection[easyrtcid].field) {
1669
if (!limitToIsShared || e.app[appName].connection[easyrtcid].field[fieldName].fieldOption.isShared) {
1670
fieldObj[fieldName] = {
1671
fieldName: fieldName,
1672
fieldValue: pub.util.deepCopy(e.app[appName].connection[easyrtcid].field[fieldName].fieldValue)
1673
};
1674
}
1675
}
1676
callback(null, fieldObj);
1677
};
1678
1679
1680
/**
1681
* Returns an array of all room names which connection has entered.
1682
*
1683
* @memberof pub.appObj.connectionObj
1684
* @param {function(?Error, Array.<string>)} callback Callback with error and array of room names.
1685
*/
1686
connectionObj.getRoomNames = function(callback) {
1687
var roomNames = [];
1688
for (var key in e.app[appName].connection[easyrtcid].room) {
1689
roomNames.push(key);
1690
}
1691
callback(null, roomNames);
1692
};
1693
1694
1695
/**
1696
* Returns the session object to which the connection belongs (if one exists). Returns a null if connection is not attached to a session (such as when sessions are disabled). Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
1697
*
1698
* @memberof pub.appObj.connectionObj
1699
* @return {Object} The session object. May be null if connection has not been joined to a session.
1700
*/
1701
connectionObj.getSession = function() {
1702
return _sessionObj;
1703
};
1704
1705
1706
/**
1707
* TO BE REMOVED - Use getSession() instead.
1708
* Returns the session object which the connection belongs to. Will return null if connection is not in a session (such as if session handling is disabled).
1709
*
1710
* @ignore
1711
* @memberof pub.appObj.connectionObj
1712
* @param {function(?Error, Object=)} callback Callback with error and Session object
1713
*/
1714
connectionObj.getSessionObj = function(callback) {
1715
if (e.app[appName].connection[easyrtcid] && e.app[appName].connection[easyrtcid].toSession && e.app[appName].connection[easyrtcid].toSession.easyrtcsid) {
1716
appObj.session(e.app[appName].connection[easyrtcid].toSession.easyrtcsid, callback);
1717
}
1718
else {
1719
callback(null, null);
1720
}
1721
};
1722
1723
1724
/**
1725
* Returns the username associated with the connection. Returns NULL if no username has been set.
1726
* Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
1727
*
1728
* @memberof pub.appObj.connectionObj
1729
* @return {String} The username associated with the connection.
1730
*/
1731
connectionObj.getUsername = function() {
1732
return e.app[appName].connection[easyrtcid].username;
1733
};
1734
1735
1736
/**
1737
* Joins the connection to a specified session. A connection can only be assigned to one session.
1738
*
1739
* @memberof pub.appObj.connectionObj
1740
* @param {string} easyrtcsid EasyRTC session identifier
1741
* @param {nextCallback} next A success callback of form next(err).
1742
*/
1743
connectionObj.joinSession = function(easyrtcsid, next) {
1744
if (!e.app[appName].session[easyrtcsid]) {
1745
next(new pub.util.ConnectionWarning("[" + appName + "][" + easyrtcid + "] Session [" + easyrtcsid + "] does not exist. Could not join session"));
1746
return;
1747
}
1748
1749
appObj.session(easyrtcsid, function(err, sessionObj) {
1750
if (err) {
1751
next(err);
1752
return;
1753
}
1754
1755
if(!e.app[appName] || !e.app[appName].connection[easyrtcid] || !e.app[appName].session[easyrtcsid]) {
1756
next(new pub.util.ConnectionWarning("[" + appName + "][" + easyrtcid + "] Session [" + easyrtcsid + "] does not exist. Could not join session"));
1757
return;
1758
}
1759
1760
e.app[appName].connection[easyrtcid].toSession = e.app[appName].session[easyrtcsid];
1761
e.app[appName].connection[easyrtcid].toSession.toConnection[easyrtcid] = e.app[appName].connection[easyrtcid];
1762
1763
// Set local session object
1764
_sessionObj = sessionObj;
1765
1766
next(null);
1767
});
1768
};
1769
1770
1771
/**
1772
* Sets connection authentication status for the connection.
1773
*
1774
* @memberof pub.appObj.connectionObj
1775
* @param {Boolean} isAuthenticated True/false as to if the connection should be considered authenticated.
1776
* @param {nextCallback} next A success callback of form next(err).
1777
*/
1778
connectionObj.setAuthenticated = function(isAuthenticated, next) {
1779
if (isAuthenticated) {
1780
e.app[appName].connection[easyrtcid].isAuthenticated = true;
1781
} else {
1782
e.app[appName].connection[easyrtcid].isAuthenticated = false;
1783
}
1784
next(null);
1785
};
1786
1787
1788
/**
1789
* Sets the credential for the connection.
1790
*
1791
* @memberof pub.appObj.connectionObj
1792
* @param {?*} credential Credential for the connection. Can be any JSON object.
1793
* @param {nextCallback} next A success callback of form next(err).
1794
*/
1795
connectionObj.setCredential = function(credential, next) {
1796
e.app[appName].connection[easyrtcid].credential = credential;
1797
next(null);
1798
};
1799
1800
1801
/**
1802
* Sets connection field value for a given field name.
1803
*
1804
* @memberof pub.appObj.connectionObj
1805
* @param {string} fieldName Must be formatted according to "fieldNameRegExp" option.
1806
* @param {Object} fieldValue
1807
* @param {?Object} fieldOption Field options (such as isShared which defaults to false)
1808
* @param {nextCallback} [next] A success callback of form next(err). Possible err will be instanceof (ApplicationWarning).
1809
*/
1810
connectionObj.setField = function(fieldName, fieldValue, fieldOption, next) {
1811
pub.util.logDebug("[" + appName + "][" + easyrtcid + "] - Setting field [" + fieldName + "]", fieldValue);
1812
if (!_.isFunction(next)) {
1813
next = pub.util.nextToNowhere;
1814
}
1815
1816
if (!pub.getOption("fieldNameRegExp").test(fieldName)) {
1817
pub.util.logWarning("Can not create connection field with improper name: '" + fieldName + "'");
1818
next(new pub.util.ApplicationWarning("Can not create connection field with improper name: '" + fieldName + "'"));
1819
return;
1820
}
1821
1822
e.app[appName].connection[easyrtcid].field[fieldName] = {
1823
fieldName: fieldName,
1824
fieldValue: fieldValue,
1825
fieldOption: {isShared: ((_.isObject(fieldOption) && fieldOption.isShared) ? true : false)}
1826
};
1827
1828
next(null);
1829
};
1830
1831
1832
/**
1833
* Sets the presence object for the connection.
1834
*
1835
* @memberof pub.appObj.connectionObj
1836
* @param {Object} presenceObj A presence object.
1837
* @param {nextCallback} next A success callback of form next(err).
1838
*/
1839
connectionObj.setPresence = function(presenceObj, next) {
1840
if (presenceObj.show !== undefined) {
1841
e.app[appName].connection[easyrtcid].presence.show = presenceObj.show;
1842
}
1843
if (presenceObj.status !== undefined) {
1844
e.app[appName].connection[easyrtcid].presence.status = presenceObj.status;
1845
}
1846
if (presenceObj.type !== undefined) {
1847
e.app[appName].connection[easyrtcid].presence.type = presenceObj.type;
1848
}
1849
next(null);
1850
};
1851
1852
1853
/**
1854
* Sets the username string for the connection.
1855
*
1856
* @memberof pub.appObj.connectionObj
1857
* @param {?string} username Username to assign to the connection.
1858
* @param {nextCallback} next A success callback of form next(err).
1859
*/
1860
connectionObj.setUsername = function(username, next) {
1861
e.app[appName].connection[easyrtcid].username = username;
1862
next(null);
1863
};
1864
1865
1866
/**
1867
* Emits the roomData message with a clientListDelta for the current connection to other connections in rooms this connection is in.
1868
* Note: To send listDeltas for individual rooms, use connectionRoomObj.emitRoomDataDelta
1869
*
1870
* @memberof pub.appObj.connectionObj
1871
* @param {Boolean} isLeavingAllRooms Indicator if connection is leaving all rooms. Meant to be used upon disconnection / logoff.
1872
* @param {function(?Error, Object=)} callback Callback of form (err, roomDataObj) which will contain the roomDataObj including all updated rooms of the connection and is designed to be returnable to the connection.
1873
*/
1874
connectionObj.emitRoomDataDelta = function(isLeavingAllRooms, callback) {
1875
pub.util.logDebug("[" + appName + "][" + easyrtcid + "] Running func 'connectionObj.emitRoomDataDelta'");
1876
if (!_.isFunction(callback)) {
1877
callback = function(err, roomDataObj) {
1878
};
1879
}
1880
1881
var fullRoomDataDelta = {};
1882
1883
var otherClients = {};
1884
1885
// Generate a complete roomDelta for the current client
1886
connectionObj.generateRoomDataDelta(isLeavingAllRooms, function(err, newFullRoomDataDelta) {
1887
fullRoomDataDelta = newFullRoomDataDelta;
1888
1889
// Running callback right away so client doesn't have to wait to continue
1890
callback(null, fullRoomDataDelta);
1891
1892
// Populate otherClients object with other clients who share room(s)
1893
for (var currentRoomName in fullRoomDataDelta) {
1894
for (var currentEasyrtcid in e.app[appName].room[currentRoomName].clientList) {
1895
if (otherClients[currentEasyrtcid] === undefined) {
1896
otherClients[currentEasyrtcid] = {};
1897
}
1898
otherClients[currentEasyrtcid][currentRoomName] = true;
1899
}
1900
}
1901
1902
// Emit custom roomData object to each client who shares a room with the current client
1903
for (var currentEasyrtcid in otherClients) {
1904
var msg = {
1905
"msgData": {
1906
"roomData": {}
1907
}
1908
};
1909
1910
for (var currentRoomName in otherClients[currentEasyrtcid]) {
1911
if (fullRoomDataDelta[currentRoomName]) {
1912
msg.msgData.roomData[currentRoomName] = fullRoomDataDelta[currentRoomName];
1913
}
1914
}
1915
1916
// Anonymous wrapper to deliver arguments
1917
(function(innerCurrentEasyrtcid, innerMsg){
1918
connectionObj.getApp().connection(innerCurrentEasyrtcid, function(err, emitToConnectionObj) {
1919
if (!err && innerCurrentEasyrtcid != easyrtcid && emitToConnectionObj) {
1920
pub.events.emit("emitEasyrtcCmd", emitToConnectionObj, "roomData", innerMsg, null, function() {});
1921
}
1922
});
1923
})(currentEasyrtcid, msg);
1924
}
1925
});
1926
};
1927
1928
1929
/**
1930
* Generates a full room clientList object for the given connection
1931
*
1932
* @memberof pub.appObj.connectionObj
1933
* @param {?string} [roomStatus="join"] Room status which allow for values of "join"|"update"|"leave".
1934
* @param {?Object} roomMap Map of rooms to generate connection clientList for. If null, then all rooms will be used.
1935
* @param {function(?Error, Object=)} callback Callback which includes a formed roomData object .
1936
*/
1937
connectionObj.generateRoomClientList = function(roomStatus, roomMap, callback) {
1938
if (!_.isString(roomStatus)) {
1939
roomStatus = "join";
1940
}
1941
1942
if (!_.isObject(roomMap)) {
1943
roomMap = e.app[appName].connection[easyrtcid].room;
1944
}
1945
1946
var roomData = {};
1947
1948
for (var currentRoomName in e.app[appName].connection[easyrtcid].room) {
1949
// If room is not in the provided roomMap, then skip it.
1950
if (!roomMap[currentRoomName]) {
1951
continue;
1952
}
1953
1954
var connectionRoom = e.app[appName].connection[easyrtcid].room[currentRoomName];
1955
roomData[currentRoomName] = {
1956
"roomName": currentRoomName,
1957
"roomStatus": roomStatus,
1958
"clientList": {}
1959
};
1960
1961
// Empty current clientList
1962
connectionRoom.clientList = {};
1963
1964
// Fill connection clientList, and roomData clientList for current room
1965
for (var currentEasyrtcid in connectionRoom.toRoom.clientList) {
1966
1967
var currentToConnection = connectionRoom.toRoom.clientList[currentEasyrtcid].toConnection;
1968
1969
connectionRoom.clientList[currentEasyrtcid] = {
1970
"toConnection": currentToConnection
1971
};
1972
1973
roomData[currentRoomName].clientList[currentEasyrtcid] = {
1974
"easyrtcid": currentEasyrtcid,
1975
"roomJoinTime": currentToConnection.room[currentRoomName].enteredOn,
1976
"presence": currentToConnection.presence
1977
};
1978
1979
if (currentToConnection.room[currentRoomName] && (!_.isEmpty(currentToConnection.room[currentRoomName].apiField))) {
1980
roomData[currentRoomName].clientList[currentEasyrtcid].apiField = currentToConnection.room[currentRoomName].apiField;
1981
}
1982
1983
if (currentToConnection.username) {
1984
roomData[currentRoomName].clientList[currentEasyrtcid].username = currentToConnection.username;
1985
}
1986
}
1987
1988
// Include room fields (with isShared set to true)
1989
for (var fieldName in connectionRoom.toRoom.field) {
1990
if (_.isObject(connectionRoom.toRoom.field[fieldName].fieldOption) && connectionRoom.toRoom.field[fieldName].fieldOption.isShared) {
1991
if (!_.isObject(roomData[currentRoomName].field)) {
1992
roomData[currentRoomName].field = {};
1993
}
1994
roomData[currentRoomName].field[fieldName] = {
1995
"fieldName": fieldName,
1996
"fieldValue": pub.util.deepCopy(connectionRoom.toRoom.field[fieldName].fieldValue)
1997
};
1998
}
1999
}
2000
2001
// Updating timestamp of when clientList was retrieved. Useful for sending delta's later on.
2002
connectionRoom.gotListOn = Date.now();
2003
}
2004
callback(null, roomData);
2005
};
2006
2007
2008
/**
2009
* Generates a delta roomData object for the current user including all rooms the user is in. The result can be selectively parsed to deliver delta roomData objects to other clients.
2010
*
2011
* @memberof pub.appObj.connectionObj
2012
* @param {Boolean} isLeavingRoom Indicates if connection is in the process of leaving the room.
2013
* @param {function(?Error, Object=)} callback Callback of form (err, roomDataDelta).
2014
*/
2015
connectionObj.generateRoomDataDelta = function(isLeavingRoom, callback) {
2016
pub.util.logDebug("[" + appName + "][" + easyrtcid + "] Running func 'connectionObj.generateRoomDataDelta'");
2017
2018
var roomDataDelta = {};
2019
2020
// set the roomData's clientListDelta for each room the client is in
2021
for (var currentRoomName in e.app[appName].connection[easyrtcid].room) {
2022
roomDataDelta[currentRoomName] = {
2023
"roomName": currentRoomName,
2024
"roomStatus": "update",
2025
"clientListDelta": {}
2026
};
2027
2028
if (isLeavingRoom) {
2029
roomDataDelta[currentRoomName].clientListDelta.removeClient = {};
2030
roomDataDelta[currentRoomName].clientListDelta.removeClient[easyrtcid] = {"easyrtcid": easyrtcid};
2031
} else {
2032
roomDataDelta[currentRoomName].clientListDelta.updateClient = {};
2033
roomDataDelta[currentRoomName].clientListDelta.updateClient[easyrtcid] = {
2034
"easyrtcid": easyrtcid,
2035
"roomJoinTime": e.app[appName].connection[easyrtcid].room[currentRoomName].enteredOn,
2036
"presence": e.app[appName].connection[easyrtcid].presence
2037
};
2038
2039
if (!_.isEmpty(e.app[appName].connection[easyrtcid].apiField)) {
2040
roomDataDelta[currentRoomName].clientListDelta.updateClient[easyrtcid].apiField = e.app[appName].connection[easyrtcid].apiField;
2041
}
2042
if (e.app[appName].connection[easyrtcid].username) {
2043
roomDataDelta[currentRoomName].clientListDelta.updateClient[easyrtcid].username = e.app[appName].connection[easyrtcid].username;
2044
}
2045
}
2046
}
2047
2048
callback(null, roomDataDelta);
2049
};
2050
2051
2052
/**
2053
* Generates the roomList message object
2054
*
2055
* @memberof pub.appObj.connectionObj
2056
* @param {function(?Error, Object=)} callback Callback with error and roomList object.
2057
*/
2058
connectionObj.generateRoomList = function(callback) {
2059
pub.util.logDebug("[" + appName + "][" + easyrtcid + "] Running func 'connectionObj.generateRoomList'");
2060
var roomList = {};
2061
2062
for (var currentRoomName in e.app[appName].room) {
2063
roomList[currentRoomName] = {
2064
"roomName": currentRoomName,
2065
"numberClients": _.size(e.app[appName].room[currentRoomName].clientList)
2066
};
2067
}
2068
callback(null, roomList);
2069
};
2070
2071
2072
/**
2073
* Gets connection authentication status for the connection. It is possible for a connection to become disconnected and keep the authenticated flag. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2074
*
2075
* @memberof pub.appObj.connectionObj
2076
* @returns {Boolean} Authentication status
2077
*/
2078
connectionObj.isAuthenticated = function() {
2079
if (e.app[appName].connection[easyrtcid] && e.app[appName].connection[easyrtcid].isAuthenticated) {
2080
return true;
2081
} else {
2082
return false;
2083
}
2084
};
2085
2086
2087
/**
2088
* Gets connection status for the connection. It is possible for a connection to be considered connected without being authenticated. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2089
*
2090
* @memberof pub.appObj.connectionObj
2091
* @returns {Boolean} Connection status
2092
*/
2093
connectionObj.isConnected = function() {
2094
if (connectionObj.socket && connectionObj.socket.socket) {
2095
return connectionObj.socket.socket.connected;
2096
}
2097
else {
2098
return false;
2099
}
2100
};
2101
2102
2103
/**
2104
* Returns a boolean to the callback indicating if connection is in a given group. NOT YET IMPLEMENTED
2105
* @ignore
2106
* @memberof pub.appObj.connectionObj
2107
* @param {string} groupName Group name to check.
2108
* @param {function(?Error, Boolean)} callback Callback with error and a boolean indicating if connection is in a room..
2109
*/
2110
connectionObj.isInGroup = function(groupName, callback) {
2111
if (_.isString(groupName) && e.app[appName].connection[easyrtcid].group[groupName] !== undefined) {
2112
callback(null, true);
2113
}
2114
else {
2115
callback(null, false);
2116
}
2117
};
2118
2119
2120
/**
2121
* Returns a boolean to the callback indicating if connection is in a given room
2122
*
2123
* @memberof pub.appObj.connectionObj
2124
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2125
* @param {function(?Error, Boolean)} callback Callback with error and a boolean indicating if connection is in a room..
2126
*/
2127
connectionObj.isInRoom = function(roomName, callback) {
2128
if (_.isString(roomName) && e.app[appName].connection[easyrtcid].room[roomName] !== undefined) {
2129
callback(null, true);
2130
}
2131
else {
2132
callback(null, false);
2133
}
2134
};
2135
2136
2137
/**
2138
* Joins an existing room, returning a connectionRoom object.
2139
*
2140
* @memberof pub.appObj.connectionObj
2141
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2142
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC connection room object (same as calling room(roomName))
2143
*/
2144
connectionObj.joinRoom = function(roomName, callback) {
2145
if (!roomName || !appObj.getOption("roomNameRegExp").test(roomName)) {
2146
pub.util.logWarning("[" + appName + "][" + easyrtcid + "] Can not enter room with improper name: '" + roomName + "'");
2147
callback(new pub.util.ConnectionWarning("Can not enter room with improper name: '" + roomName + "'"));
2148
return;
2149
}
2150
// Check if room doesn't exist
2151
if (!appObj.isRoomSync(roomName)) {
2152
pub.util.logWarning("[" + appName + "][" + easyrtcid + "] Can not enter room which doesn't exist: '" + roomName + "'");
2153
callback(new pub.util.ConnectionWarning("Can not enter room which doesn't exist: '" + roomName + "'"));
2154
return;
2155
}
2156
2157
// Check if client already in room
2158
if (e.app[appName].connection[easyrtcid].room[roomName]) {
2159
connectionObj.room(roomName, callback);
2160
return;
2161
}
2162
2163
// Local private function to create the default connection-room object in the private variable
2164
var createConnectionRoom = function(roomName, appRoomObj, callback) {
2165
// Join room. Creates a default connection room object
2166
e.app[appName].connection[easyrtcid].room[roomName] = {
2167
apiField: {},
2168
enteredOn: Date.now(),
2169
gotListOn: Date.now(),
2170
clientList: {},
2171
toRoom: e.app[appName].room[roomName]
2172
};
2173
2174
// Add easyrtcid to room clientList
2175
e.app[appName].room[roomName].clientList[easyrtcid] = {
2176
enteredOn: Date.now(),
2177
modifiedOn: Date.now(),
2178
toConnection: e.app[appName].connection[easyrtcid]
2179
};
2180
2181
// Returns connection room object to callback.
2182
connectionObj.room(roomName, callback);
2183
};
2184
2185
appObj.room(roomName, function(err, appRoomObj) {
2186
if (err) {
2187
callback(err);
2188
return;
2189
}
2190
createConnectionRoom(roomName, appRoomObj, callback);
2191
});
2192
};
2193
2194
2195
/**
2196
* Gets room object for a given room name. Returns null if room not found.
2197
* The returned room object includes functions for managing room fields.
2198
*
2199
* @memberof pub.appObj.connectionObj
2200
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2201
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC connection room object.
2202
*/
2203
connectionObj.room = function(roomName, callback) {
2204
if (_.isUndefined(e.app[appName].connection[easyrtcid].room[roomName])) {
2205
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
2206
callback(new pub.util.ConnectionWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2207
return;
2208
}
2209
2210
/**
2211
* This is a gateway object connecting connections to the rooms they are in.
2212
*
2213
* @class connectionRoomObj
2214
* @memberof pub.appObj.connectionObj
2215
*/
2216
var connectionRoomObj = {};
2217
2218
// House the local room object
2219
var _roomObj;
2220
2221
2222
/**
2223
* Expose all event functions
2224
*
2225
* @memberof pub.appObj.connectionObj.connectionRoomObj
2226
*/
2227
connectionRoomObj.events = pub.events;
2228
2229
2230
/**
2231
* Expose all utility functions
2232
*
2233
* @memberof pub.appObj.connectionObj.connectionRoomObj
2234
*/
2235
connectionRoomObj.util = pub.util;
2236
2237
2238
/**
2239
* Returns the application object to which the connection belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2240
*
2241
* @memberof pub.appObj.connectionObj.connectionRoomObj
2242
* @return {Object} The application object
2243
*/
2244
connectionRoomObj.getApp = function() {
2245
return appObj;
2246
};
2247
2248
2249
/**
2250
* Returns the application name for the application to which the connection belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2251
*
2252
* @memberof pub.appObj.connectionObj.connectionRoomObj
2253
* @return {string} The application name
2254
*/
2255
connectionRoomObj.getAppName = function() {
2256
return appName;
2257
};
2258
2259
2260
/**
2261
* Returns the connection object to which the connection belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2262
*
2263
* @memberof pub.appObj.connectionObj.connectionRoomObj
2264
* @return {Object} The application object
2265
*/
2266
connectionRoomObj.getConnection = function() {
2267
return connectionObj;
2268
};
2269
2270
2271
/**
2272
* Returns the room object to which the connection belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2273
*
2274
* @memberof pub.appObj.connectionObj.connectionRoomObj
2275
* @return {Object} The room object
2276
*/
2277
connectionRoomObj.getRoom = function() {
2278
return _roomObj;
2279
};
2280
2281
2282
/**
2283
* Returns the room name to which the connection belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2284
*
2285
* @memberof pub.appObj.connectionObj.connectionRoomObj
2286
* @return {string} The room name
2287
*/
2288
connectionRoomObj.getRoomName = function() {
2289
return roomName;
2290
};
2291
2292
2293
/**
2294
* Leaves the current room. Any room variables will be lost.
2295
*
2296
* @memberof pub.appObj.connectionObj.connectionRoomObj
2297
* @param {nextCallback} [next] A success callback of form next(err).
2298
*/
2299
connectionRoomObj.leaveRoom = function(next) {
2300
if (!_.isFunction(next)) {
2301
next = pub.util.nextToNowhere;
2302
}
2303
2304
if (appObj.isRoomSync(roomName)){
2305
e.app[appName].room[roomName].modifiedOn = Date.now();
2306
delete e.app[appName].room[roomName].clientList[easyrtcid];
2307
}
2308
2309
if (e.app[appName].connection[easyrtcid]){
2310
delete e.app[appName].connection[easyrtcid].room[roomName];
2311
}
2312
2313
connectionRoomObj.emitRoomDataDelta(true, function(err, roomDataObj) {
2314
next(err);
2315
});
2316
};
2317
2318
2319
/**
2320
* Emits the roomData message with a clientListDelta for the current connection to other connections in the same room.
2321
*
2322
* @memberof pub.appObj.connectionObj.connectionRoomObj
2323
* @param {boolean} isLeavingRoom Is connection leaving the room?
2324
* @param {function(?Error, Object=)} callback Callback with error and room data delta object.
2325
*/
2326
connectionRoomObj.emitRoomDataDelta = function(isLeavingRoom, callback) {
2327
pub.util.logDebug("[" + appName + "][" + easyrtcid + "] Room [" + roomName + "] Running func 'connectionRoomObj.emitRoomDataDelta'");
2328
if (!_.isFunction(callback)) {
2329
callback = function(err, roomDataObj) {
2330
};
2331
}
2332
2333
connectionRoomObj.generateRoomDataDelta(isLeavingRoom, function(err, roomDataDelta) {
2334
if (err) {
2335
callback(err);
2336
return;
2337
}
2338
if (!appObj.isRoomSync(roomName)) {
2339
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
2340
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2341
return;
2342
}
2343
2344
var msg = {"msgData": {"roomData": {}}};
2345
msg.msgData.roomData[roomName] = roomDataDelta;
2346
2347
for (var currentEasyrtcid in e.app[appName].room[roomName].clientList) {
2348
// Anonymous wrapper to deliver arguments
2349
(function(innerCurrentEasyrtcid, innerMsg){
2350
connectionObj.getApp().connection(innerCurrentEasyrtcid, function(err, emitToConnectionObj) {
2351
if (!err && innerCurrentEasyrtcid != easyrtcid && emitToConnectionObj) {
2352
pub.events.emit("emitEasyrtcCmd", emitToConnectionObj, "roomData", innerMsg, null, function() {
2353
});
2354
}
2355
});
2356
})(currentEasyrtcid, msg);
2357
2358
}
2359
callback(null, roomDataDelta);
2360
});
2361
};
2362
2363
2364
/**
2365
* Generated the roomData[room] message with a clientListDelta for the current connection to other connections in the same room.
2366
*
2367
* @memberof pub.appObj.connectionObj.connectionRoomObj
2368
* @param {boolean} isLeavingRoom Is connection leaving the room?
2369
* @param {function(?Error, Object=)} callback Callback with error and room data delta object.
2370
*/
2371
connectionRoomObj.generateRoomDataDelta = function(isLeavingRoom, callback) {
2372
pub.util.logDebug("[" + appName + "][" + easyrtcid + "] Room [" + roomName + "] Running func 'connectionRoomObj.generateRoomDataDelta'");
2373
if (!_.isFunction(callback)) {
2374
callback = pub.util.nextToNowhere;
2375
}
2376
if (!appObj.isRoomSync(roomName)) {
2377
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
2378
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2379
return;
2380
}
2381
var roomDataDelta = {"roomName": roomName, "roomStatus": "update", "clientListDelta": {}};
2382
2383
if (isLeavingRoom) {
2384
roomDataDelta.clientListDelta.removeClient = {};
2385
roomDataDelta.clientListDelta.removeClient[easyrtcid] = {"easyrtcid": easyrtcid};
2386
} else {
2387
var connectionRoom = e.app[appName].connection[easyrtcid].room[roomName];
2388
roomDataDelta.clientListDelta.updateClient = {};
2389
roomDataDelta.clientListDelta.updateClient[easyrtcid] = {
2390
"easyrtcid": easyrtcid,
2391
"roomJoinTime": e.app[appName].connection[easyrtcid].room[roomName].enteredOn,
2392
"presence": e.app[appName].connection[easyrtcid].presence
2393
};
2394
2395
if (!_.isEmpty(e.app[appName].connection[easyrtcid].room[roomName].apiField)) {
2396
roomDataDelta.clientListDelta.updateClient[easyrtcid].apiField = e.app[appName].connection[easyrtcid].room[roomName].apiField;
2397
}
2398
if (e.app[appName].connection[easyrtcid].username) {
2399
roomDataDelta.clientListDelta.updateClient[easyrtcid].username = e.app[appName].connection[easyrtcid].username;
2400
}
2401
}
2402
2403
callback(null, roomDataDelta);
2404
};
2405
2406
/**
2407
* Sets the API field for the current connection in a room.
2408
*
2409
* @memberof pub.appObj.connectionObj.connectionRoomObj
2410
* @param {object} apiFieldObj A API field object, including the field name and field value.
2411
* @param {nextCallback} next A success callback of form next(err).
2412
*/
2413
connectionRoomObj.setApiField = function(apiFieldObj, next) {
2414
if (!_.isFunction(next)) {
2415
next = pub.util.nextToNowhere;
2416
}
2417
2418
e.app[appName].connection[easyrtcid].room[roomName].apiField = pub.util.deepCopy(apiFieldObj);
2419
next(null);
2420
};
2421
2422
// Set the roomObj before returning the connectionRoomObj
2423
appObj.room(roomName,
2424
function(err, roomObj) {
2425
_roomObj = roomObj;
2426
callback(null, connectionRoomObj);
2427
}
2428
);
2429
};
2430
2431
2432
/**
2433
* Removes a connection object. Does not (currently) remove connection from rooms or groups.
2434
*
2435
* @memberof pub.appObj.connectionObj
2436
* @param {nextCallback} next A success callback of form next(err).
2437
*/
2438
connectionObj.removeConnection = function(next) {
2439
if (e.app[appName] && _.isObject(e.app[appName].connection) && e.app[appName].connection[easyrtcid]) {
2440
e.app[appName].connection[easyrtcid].isAuthenticated = false;
2441
// Remove link to connection from session in local storage
2442
if (e.app[appName].connection[easyrtcid].toSession) {
2443
delete e.app[appName].connection[easyrtcid].toSession.toConnection[easyrtcid];
2444
}
2445
2446
// Remove connection from local storage
2447
delete e.app[appName].connection[easyrtcid];
2448
}
2449
next(null);
2450
};
2451
2452
// Before returning connectionObj, join the connection to a session (if available).
2453
if (e.app[appName].connection[easyrtcid].toSession) {
2454
appObj.session(e.app[appName].connection[easyrtcid].toSession.easyrtcsid, function(err, sessionObj) {
2455
if (err) {
2456
callback(err);
2457
return;
2458
}
2459
_sessionObj = sessionObj;
2460
callback(null, connectionObj);
2461
});
2462
} else {
2463
callback(null, connectionObj);
2464
}
2465
};
2466
2467
2468
/**
2469
* Creates a new connection with a provided connection key
2470
*
2471
* @memberof pub.appObj
2472
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
2473
* @param {string} socketId Socket.io socket identifier for a socket connection.
2474
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC connection object (same as calling connection(easyrtcid))
2475
*/
2476
appObj.createConnection = function(easyrtcid, socketId, callback) {
2477
if (!easyrtcid || !appObj.getOption("easyrtcidRegExp").test(easyrtcid)) {
2478
pub.util.logWarning("Can not create connection with improper name: '" + easyrtcid + "'");
2479
callback(new pub.util.ConnectionWarning("Can not create connection with improper name: '" + easyrtcid + "'"));
2480
return;
2481
}
2482
2483
if (e.app[appName].connection[easyrtcid]) {
2484
pub.util.logWarning("Can not create connection which already exists: '" + easyrtcid + "'");
2485
callback(new pub.util.ConnectionWarning("Can not create connection which already exists: '" + easyrtcid + "'"));
2486
return;
2487
}
2488
2489
// Set the connection structure with some default values
2490
e.app[appName].connection[easyrtcid] = {
2491
easyrtcid: easyrtcid,
2492
socketId: socketId,
2493
connectOn: Date.now(),
2494
isAuthenticated: false,
2495
userName: null,
2496
credential: null,
2497
field: {},
2498
group: {},
2499
presence: {
2500
show: "chat",
2501
status: null
2502
},
2503
room: {},
2504
toApp: e.app[appName]
2505
};
2506
2507
// Initialize a new connection object
2508
appObj.connection(easyrtcid, function(err, connectionObj) {
2509
if (err) {
2510
callback(err);
2511
return;
2512
}
2513
2514
// Set default connection fields
2515
var connectionDefaultFieldObj = appObj.getOption("connectionDefaultFieldObj");
2516
if (_.isObject(connectionDefaultFieldObj)) {
2517
for (var currentFieldName in connectionDefaultFieldObj) {
2518
connectionObj.setField(
2519
currentFieldName,
2520
connectionDefaultFieldObj[currentFieldName].fieldValue,
2521
connectionDefaultFieldObj[currentFieldName].fieldOption,
2522
null
2523
);
2524
}
2525
}
2526
2527
callback(null, connectionObj);
2528
});
2529
};
2530
2531
2532
/**
2533
* Counts how many occupants are in a room.
2534
*
2535
* @memberof pub.appObj
2536
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2537
* @param {function(?Error, number=)} callback Callback with error and client count
2538
*/
2539
appObj.getRoomOccupantCount = function(roomName, callback) {
2540
if (!appObj.isRoomSync(roomName)) {
2541
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2542
return;
2543
}
2544
2545
callback(null, _.size(e.app[appName].room[roomName].clientList));
2546
};
2547
2548
/**
2549
* Delete an existing room, providing the room is empty.
2550
*
2551
* @memberof pub.appObj
2552
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2553
* @param {function(?Error, Object=)} callback Callback with error and true if a room was deleted.
2554
*/
2555
appObj.deleteRoom = function(roomName, callback) {
2556
if (!roomName) {
2557
var errorMsg = "Can't delete room with a null room name";
2558
pub.util.logWarning(errorMsg);
2559
callback(new pub.util.ApplicationWarning(errorMsg), false);
2560
return;
2561
}
2562
2563
// If room is already deleted or if it doesn't exist, report error
2564
if (!appObj.isRoomSync(roomName)) {
2565
var errorMsg = "Can't delete non-existing room: " + roomName;
2566
pub.util.logWarning(errorMsg);
2567
callback(new pub.util.ApplicationWarning(errorMsg), false);
2568
return;
2569
}
2570
2571
if (!_.isEmpty(e.app[appName].room[roomName].clientList)){
2572
var errorMsg = "Can't delete room " + roomName + " because it isn't empty";
2573
pub.util.logWarning(errorMsg);
2574
callback(new pub.util.ApplicationWarning(errorMsg), false);
2575
return;
2576
}
2577
2578
e.app[appName].room[roomName].deleted = true;
2579
2580
delete e.app[appName].room[roomName];
2581
callback(null, true);
2582
};
2583
2584
2585
/**
2586
* Creates a new room, sending the resulting room object to a provided callback.
2587
*
2588
* @memberof pub.appObj
2589
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2590
* @param {?object} options Options object with options to apply to the room. May be null.
2591
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC room object (same as calling appObj.room(roomName))
2592
*/
2593
appObj.createRoom = function(roomName, options, callback) {
2594
if (!roomName || !appObj.getOption("roomNameRegExp").test(roomName)) {
2595
pub.util.logWarning("Can not create room with improper name: '" + roomName + "'");
2596
callback(new pub.util.ApplicationWarning("Can not create room with improper name: '" + roomName + "'"));
2597
return;
2598
}
2599
if (appObj.isRoomSync(roomName)) {
2600
pub.util.logWarning("Can not create room which already exists: '" + roomName + "'");
2601
callback(new pub.util.ApplicationWarning("Can not create room which already exists: '" + roomName + "'"));
2602
return;
2603
}
2604
if (!_.isObject(options)) {
2605
options = {};
2606
}
2607
pub.util.logDebug("Creating room: '" + roomName + "' with options:", options);
2608
2609
e.app[appName].room[roomName] = {
2610
roomName: roomName,
2611
deleted: false,
2612
clientList: {},
2613
field: {},
2614
option: {},
2615
modifiedOn: Date.now()
2616
};
2617
2618
// Initialize a new room object
2619
appObj.room(roomName, function(err, roomObj) {
2620
if (err) {
2621
callback(err);
2622
return;
2623
}
2624
2625
// Set all options in options object. If any fail, an error will be sent to the callback.
2626
async.each(Object.keys(options), function(currentOptionName, asyncCallback) {
2627
roomObj.setOption(currentOptionName, options[currentOptionName]);
2628
asyncCallback(null);
2629
},
2630
function(err) {
2631
if (err) {
2632
callback(new pub.util.ApplicationError("Could not set options when creating room: '" + roomName + "'", err));
2633
return;
2634
}
2635
2636
// Set default room fields
2637
var roomDefaultFieldObj = roomObj.getOption("roomDefaultFieldObj");
2638
2639
if (_.isObject(roomDefaultFieldObj)) {
2640
for (var currentFieldName in roomDefaultFieldObj) {
2641
roomObj.setField(
2642
currentFieldName,
2643
roomDefaultFieldObj[currentFieldName].fieldValue,
2644
roomDefaultFieldObj[currentFieldName].fieldOption,
2645
null
2646
);
2647
}
2648
}
2649
2650
// Return room object to callback
2651
callback(null, roomObj);
2652
});
2653
});
2654
};
2655
2656
2657
/**
2658
* Creates a new session with a provided easyrtcsid
2659
*
2660
* @memberof pub.appObj
2661
* @param {string} easyrtcsid EasyRTC Session Identifier. Must be formatted according to "easyrtcsidRegExp" option.
2662
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC session object (same as calling session(easyrtcsid))
2663
*/
2664
appObj.createSession = function(easyrtcsid, callback) {
2665
pub.util.logDebug("[" + appObj.getAppName() + "] Creating session [" + easyrtcsid + "]");
2666
2667
if (!easyrtcsid || !appObj.getOption("easyrtcsidRegExp").test(easyrtcsid)) {
2668
pub.util.logWarning("Can not create session with improper name [" + easyrtcsid + "]");
2669
callback(new pub.util.ConnectionWarning("Can not create session with improper name [" + easyrtcsid + "]"));
2670
return;
2671
}
2672
2673
if (e.app[appName].session[easyrtcsid]) {
2674
pub.util.logWarning("Can not create session which already exists [" + easyrtcsid + "]");
2675
callback(new pub.util.ConnectionWarning("Can not create session which already exists [" + easyrtcsid + "]"));
2676
return;
2677
}
2678
2679
// Set the session structure with some default values
2680
e.app[appName].session[easyrtcsid] = {
2681
"easyrtcsid": easyrtcsid,
2682
"startOn": Date.now(),
2683
"toConnection":{},
2684
"field": {}
2685
};
2686
2687
appObj.session(easyrtcsid, callback);
2688
};
2689
2690
2691
/**
2692
* Checks if a provided room is defined. The callback returns a boolean if room is defined.
2693
*
2694
* @memberof pub.appObj
2695
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2696
* @param {function(?Error, boolean)} callback Callback with error and boolean of whether room is defined.
2697
*/
2698
appObj.isRoom = function(roomName, callback) {
2699
callback(null,((e.app[appName] && e.app[appName].room[roomName] && !e.app[appName].room[roomName].deleted) ? true : false));
2700
};
2701
2702
2703
/**
2704
* Checks if a provided room is defined. This is a synchronous function, thus may not be available in custom cases where room state is not kept in memory.
2705
*
2706
* @memberof pub.appObj
2707
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2708
* @return {Boolean} Returns boolean. True if room is defined.
2709
*/
2710
appObj.isRoomSync = function(roomName) {
2711
return ((e.app[appName] && e.app[appName].room[roomName] && !e.app[appName].room[roomName].deleted) ? true : false);
2712
};
2713
2714
2715
/**
2716
* Checks if a provided session is defined. The callback returns a boolean if session is defined
2717
*
2718
* @memberof pub.appObj
2719
* @param {string} easyrtcsid EasyRTC session identifier
2720
* @param {function(?Error, boolean)} callback Callback with error and boolean of whether session is defined.
2721
*/
2722
appObj.isSession = function(easyrtcsid, callback) {
2723
callback(null, (e.app[appName].session[easyrtcsid] ? true : false));
2724
};
2725
2726
2727
/**
2728
* NOT YET IMPLEMENTED - Gets group object for a given group name. Returns null if group not found.
2729
* The returned group object includes functions for managing group fields.
2730
*
2731
* @memberof pub.appObj
2732
* @param {string} groupName Group name
2733
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC group object.
2734
*/
2735
appObj.group = function(groupName, callback) {
2736
if (!e.app[appName].group[groupName]) {
2737
pub.util.logWarning("Attempt to request non-existent group name: '" + groupName + "'");
2738
callback(new pub.util.ApplicationWarning("Attempt to request non-existent group name: '" + groupName + "'"));
2739
return;
2740
}
2741
2742
var groupObj = {};
2743
2744
/**
2745
* Expose all event functions
2746
*/
2747
groupObj.events = pub.events;
2748
2749
/**
2750
* Expose all utility functions
2751
*/
2752
groupObj.util = pub.util;
2753
2754
/**
2755
* NOT YET IMPLEMENTED - Returns an array of all connected clients within the room.
2756
*
2757
* @ignore
2758
* @param {function(?Error, Array.<string>)} callback Callback with error and array containing all easyrtcids.
2759
*/
2760
groupObj.getConnections = function(callback) {
2761
var connectedEasyrtcidArray = [];
2762
for (var key in e.app[appName].group[groupName].clientList) {
2763
connectedEasyrtcidArray.push(key);
2764
}
2765
callback(null, connectedEasyrtcidArray);
2766
};
2767
2768
callback(null, groupObj);
2769
};
2770
2771
2772
/**
2773
* Gets room object for a given room name. Returns null if room not found.
2774
* The returned room object includes functions for managing room fields.
2775
*
2776
* @memberof pub.appObj
2777
* @param {string} roomName Room name which uniquely identifies a room within an EasyRTC application.
2778
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC room object.
2779
*/
2780
appObj.room = function(roomName, callback) {
2781
if (!appObj.isRoomSync(roomName)) {
2782
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
2783
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2784
return;
2785
}
2786
2787
/**
2788
* EasyRTC Room Object. Contains methods for handling a specific room including determining which connections have joined.
2789
*
2790
* @class roomObj
2791
* @memberof pub.appObj
2792
*/
2793
var roomObj = {};
2794
2795
2796
/**
2797
* Expose all event functions
2798
*
2799
* @memberof pub.appObj.roomObj
2800
*/
2801
roomObj.events = pub.events;
2802
2803
2804
/**
2805
* Expose all utility functions
2806
*
2807
* @memberof pub.appObj.roomObj
2808
*/
2809
roomObj.util = pub.util;
2810
2811
2812
/**
2813
* Returns the application object to which the room belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2814
*
2815
* @memberof pub.appObj.roomObj
2816
* @return {Object} The application object
2817
*/
2818
roomObj.getApp = function() {
2819
return appObj;
2820
};
2821
2822
2823
/**
2824
* Returns the application name for the application to which the room belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2825
*
2826
* @memberof pub.appObj.roomObj
2827
* @return {string} The application name
2828
*/
2829
roomObj.getAppName = function() {
2830
return appName;
2831
};
2832
2833
2834
/**
2835
* Returns the room name for the current room. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
2836
*
2837
* @memberof pub.appObj.roomObj
2838
* @return {string} The room name
2839
*/
2840
roomObj.getRoomName = function() {
2841
return roomName;
2842
};
2843
2844
2845
/**
2846
* INCOMPLETE: Emits a roomData message containing fields to all connections in the current room. This is meant to be called after a room field has been set or updated.
2847
* @ignore
2848
*/
2849
roomObj.emitRoomDataFieldUpdate = function(skipEasyrtcid, next) {
2850
roomObj.getFields(true, function(err, fieldObj) {
2851
if (err) {
2852
next(err);
2853
return;
2854
}
2855
if (!appObj.isRoomSync(roomName)) {
2856
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
2857
next(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2858
return;
2859
}
2860
2861
var outgoingMsg = {"msgData": {"roomData": {}}};
2862
outgoingMsg.msgData.roomData[roomName] = {
2863
"roomName": roomName,
2864
"roomStatus": "update"
2865
};
2866
outgoingMsg.msgData.roomData[roomName].field = fieldObj;
2867
2868
async.each(
2869
Object.keys(e.app[appName].room[roomName].clientList),
2870
function(currentEasyrtcid, asyncCallback) {
2871
2872
// Skip a given easyrtcid?
2873
if (skipEasyrtcid && (skipEasyrtcid == currentEasyrtcid)) {
2874
asyncCallback(null);
2875
return;
2876
}
2877
2878
// Retrieve a connection object, then send the roomData message.
2879
appObj.connection(currentEasyrtcid, function(err, targetConnectionObj) {
2880
if (err || !_.isObject(targetConnectionObj)) {
2881
pub.util.logDebug("[" + currentEasyrtcid + "] Could not get connection object to send room data field update. Client may have disconnected.");
2882
asyncCallback(null);
2883
return;
2884
}
2885
pub.events.emit("emitEasyrtcCmd", targetConnectionObj, "roomData", outgoingMsg, function(msg) {
2886
}, function(err) {
2887
// Ignore errors if unable to send to a socket.
2888
asyncCallback(null);
2889
});
2890
});
2891
},
2892
function(err) {
2893
next(null);
2894
}
2895
);
2896
});
2897
};
2898
2899
2900
/**
2901
* Returns room level field object for a given field name to a provided callback.
2902
*
2903
* @memberof pub.appObj.roomObj
2904
* @param {string} fieldName Field name
2905
* @param {function(?Error, Object=)} callback Callback with error and field object (any type)
2906
*/
2907
roomObj.getField = function(fieldName, callback) {
2908
if (!appObj.isRoomSync(roomName)) {
2909
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
2910
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2911
return;
2912
}
2913
if (!e.app[appName].room[roomName].field[fieldName]) {
2914
pub.util.logDebug("Can not find room field: '" + fieldName + "'");
2915
callback(new pub.util.ApplicationWarning("Can not find room field: '" + fieldName + "'"));
2916
return;
2917
}
2918
callback(null, pub.util.deepCopy(e.app[appName].room[roomName].field[fieldName]));
2919
};
2920
2921
2922
/**
2923
* Returns room level field object for a given field name. If the field is not set, it will return a field value will a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
2924
*
2925
* @memberof pub.appObj.roomObj
2926
* @param {string} fieldName Field name
2927
* @returns {Object} Field object
2928
*/
2929
roomObj.getFieldSync = function(fieldName) {
2930
if (!appObj.isRoomSync(roomName)) {
2931
return {"fieldName": fieldName, "fieldOption": {}, "fieldValue": null};
2932
}
2933
if (!e.app[appName].room[roomName].field[fieldName]) {
2934
return {"fieldName": fieldName, "fieldOption": {}, "fieldValue": null};
2935
}
2936
return pub.util.deepCopy(e.app[appName].room[roomName].field[fieldName]);
2937
};
2938
2939
2940
/**
2941
* Returns room level field value for a given field name. If the field is not set, it will return a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
2942
*
2943
* @memberof pub.appObj.roomObj
2944
* @param {string} fieldName Field name
2945
* @returns {?*} Field value
2946
*/
2947
roomObj.getFieldValueSync = function(fieldName) {
2948
if (!appObj.isRoomSync(roomName)) {
2949
return null;
2950
}
2951
if (!e.app[appName].room[roomName].field[fieldName]) {
2952
return null;
2953
}
2954
return pub.util.deepCopy(e.app[appName].room[roomName].field[fieldName].fieldValue);
2955
};
2956
2957
2958
/**
2959
* Returns an object containing all field names and values within the room. Can be limited to fields with isShared option set to true.
2960
*
2961
* @memberof pub.appObj.roomObj
2962
* @param {boolean} limitToIsShared Limits returned fields to those which have the isShared option set to true.
2963
* @param {function(?Error, Object=)} callback Callback with error and object containing field names and values.
2964
*/
2965
roomObj.getFields = function(limitToIsShared, callback) {
2966
if (!appObj.isRoomSync(roomName)) {
2967
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
2968
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
2969
return;
2970
}
2971
var fieldObj = {};
2972
for (var fieldName in e.app[appName].room[roomName].field) {
2973
if (!limitToIsShared || e.app[appName].room[roomName].field[fieldName].fieldOption.isShared) {
2974
fieldObj[fieldName] = {
2975
fieldName: fieldName,
2976
fieldValue: pub.util.deepCopy(e.app[appName].room[roomName].field[fieldName].fieldValue)
2977
};
2978
}
2979
}
2980
callback(null, fieldObj);
2981
};
2982
2983
2984
/**
2985
* Gets individual option value. Will first check if option is defined for the room, else it will revert to the application level option (which will in turn fall back to the global level).
2986
*
2987
* @memberof pub.appObj.roomObj
2988
* @param {String} optionName Option name
2989
* @return {*} Option value (can be any type)
2990
*/
2991
roomObj.getOption = function(optionName) {
2992
return ((!appObj.isRoomSync(roomName) || e.app[appName].room[roomName].option[optionName] === undefined) ? appObj.getOption(optionName) : (e.app[appName].room[roomName].option[optionName]));
2993
};
2994
2995
2996
/**
2997
* Sets individual option which applies only to this room. Set value to NULL to delete the option (thus reverting to global option)
2998
*
2999
* @memberof pub.appObj.roomObj
3000
* @param {Object} optionName Option name
3001
* @param {Object} optionValue Option value
3002
* @return {Boolean} true on success, false on failure
3003
*/
3004
roomObj.setOption = function(optionName, optionValue) {
3005
if (!appObj.isRoomSync(roomName)) {
3006
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3007
return false;
3008
}
3009
// Can only set options which currently exist
3010
if (typeof e.option[optionName] == "undefined") {
3011
pub.util.logError("Error setting option. Unrecognised option name '" + optionName + "'.");
3012
return false;
3013
}
3014
3015
// If value is null, delete option from application (reverts to global option)
3016
if (optionValue == null) {
3017
if (!(e.app[appName].option[optionName] === undefined)) {
3018
delete e.app[appName].room[roomName].option[optionName];
3019
}
3020
} else {
3021
// Set the option value to be a full deep copy, thus preserving private nature of the private EasyRTC object.
3022
e.app[appName].room[roomName].option[optionName] = pub.util.deepCopy(optionValue);
3023
}
3024
return true;
3025
};
3026
3027
3028
/**
3029
* Incomplete function for setting an easyrtcid as being a client in a room.
3030
*
3031
* @memberof pub.appObj.roomObj
3032
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
3033
* @param {nextCallback} next A success callback of form next(err).
3034
* @ignore
3035
*/
3036
roomObj.setConnection = function(easyrtcid, next) {
3037
if (!appObj.isRoomSync(roomName)) {
3038
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3039
next(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
3040
return;
3041
}
3042
pub.util.logWarning("Using deprecated roomObj.setConnection() function");
3043
e.app[appName].room[roomName].clientList[easyrtcid] = {enteredOn: Date.now()};
3044
next(null);
3045
};
3046
3047
3048
/**
3049
* Sets room field value for a given field name.
3050
*
3051
* @memberof pub.appObj.roomObj
3052
* @param {string} fieldName Must be formatted according to "fieldNameRegExp" option.
3053
* @param {Object} fieldValue
3054
* @param {?Object} fieldOption Field options (such as isShared which defaults to false)
3055
* @param {nextCallback} [next] A success callback of form next(err). Possible err will be instanceof (ApplicationWarning).
3056
*/
3057
roomObj.setField = function(fieldName, fieldValue, fieldOption, next) {
3058
if (!appObj.isRoomSync(roomName)) {
3059
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3060
next(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
3061
return;
3062
}
3063
pub.util.logDebug("[" + appName + "] Room [" + roomName + "] - Setting field [" + fieldName + "]", fieldValue);
3064
if (!_.isFunction(next)) {
3065
next = pub.util.nextToNowhere;
3066
}
3067
3068
if (!pub.getOption("fieldNameRegExp").test(fieldName)) {
3069
pub.util.logWarning("Can not create room field with improper name: '" + fieldName + "'");
3070
next(new pub.util.ApplicationWarning("Can not create room field with improper name: '" + fieldName + "'"));
3071
return;
3072
}
3073
3074
e.app[appName].room[roomName].field[fieldName] = {
3075
fieldName: fieldName,
3076
fieldValue: fieldValue,
3077
fieldOption: {isShared: ((_.isObject(fieldOption) && fieldOption.isShared) ? true : false)}
3078
};
3079
3080
next(null);
3081
};
3082
3083
3084
/**
3085
* Sends the count of the number of connections in a room to a provided callback.
3086
*
3087
* @memberof pub.appObj.roomObj
3088
* @param {function(?Error, Number)} callback Callback with error and array containing all easyrtcids.
3089
*/
3090
roomObj.getConnectionCount = function(callback) {
3091
if (!appObj.isRoomSync(roomName)) {
3092
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3093
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
3094
return;
3095
}
3096
callback(null, roomObj.getConnectionCountSync());
3097
};
3098
3099
3100
/**
3101
* Sends the count of the number of connections in a room to a provided callback. Returns 0 if room doesn't exist.
3102
*
3103
* @memberof pub.appObj.roomObj
3104
* @returns {Number} The current number of connections in a room.
3105
*/
3106
roomObj.getConnectionCountSync = function() {
3107
if (!appObj.isRoomSync(roomName)) {
3108
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3109
return 0;
3110
}
3111
return _.size(e.app[appName].room[roomName].clientList);
3112
};
3113
3114
3115
/**
3116
* Returns an array containing the easyrtcids of all connected clients within the room.
3117
*
3118
* @memberof pub.appObj.roomObj
3119
* @param {function(?Error, Array.<string>=)} callback Callback with error and array containing all easyrtcids.
3120
*/
3121
roomObj.getConnections = function(callback) {
3122
if (!appObj.isRoomSync(roomName)) {
3123
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3124
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
3125
return;
3126
}
3127
var connectedEasyrtcidArray = [];
3128
for (var key in e.app[appName].room[roomName].clientList) {
3129
connectedEasyrtcidArray.push(key);
3130
}
3131
callback(null, connectedEasyrtcidArray);
3132
};
3133
3134
3135
/**
3136
* Returns the connectionObj for a given easyrtcid, but only if it is currently a client in the room
3137
*
3138
* @memberof pub.appObj.roomObj
3139
* @param {string} easyrtcid EasyRTC unique identifier for a socket connection.
3140
* @param {function(?Error, Object=)} callback Callback with error and connectionObj.
3141
*/
3142
roomObj.getConnectionWithEasyrtcid = function(easyrtcid, callback) {
3143
if (!appObj.isRoomSync(roomName)) {
3144
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3145
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
3146
return;
3147
}
3148
if (e.app[appName].room[roomName].clientList[easyrtcid]){
3149
appObj.connection(easyrtcid, function(err, connectionObj) {
3150
if (err) {
3151
callback(new pub.util.ConnectionWarning("Can not find connection [" + easyrtcid + "] in room."));
3152
return;
3153
}
3154
// If there is no error, than run callback with the connection object.
3155
callback(null, connectionObj);
3156
});
3157
}
3158
else {
3159
callback(new pub.util.ConnectionWarning("Can not find connection [" + easyrtcid + "] in room."));
3160
}
3161
};
3162
3163
3164
/**
3165
* Returns an array containing the connectionObjs of all connected clients within the room.
3166
*
3167
* @memberof pub.appObj.roomObj
3168
* @param {function(?Error, Array.<Object>=)} callback Callback with error and array containing connectionObjs.
3169
*/
3170
roomObj.getConnectionObjects = function(callback) {
3171
if (!appObj.isRoomSync(roomName)) {
3172
pub.util.logWarning("Attempt to request non-existent room name: '" + roomName + "'");
3173
callback(new pub.util.ApplicationWarning("Attempt to request non-existent room name: '" + roomName + "'"));
3174
return;
3175
}
3176
var connectedObjArray = [];
3177
async.each(Object.keys(e.app[appName].room[roomName].clientList),
3178
function(currentEasyrtcid, asyncCallback) {
3179
appObj.connection(currentEasyrtcid, function(err, connectionObj) {
3180
if (err) {
3181
// We will silently ignore errors
3182
asyncCallback(null);
3183
return;
3184
}
3185
// If there is no error, than push the connection object.
3186
connectedObjArray.push(connectionObj);
3187
asyncCallback(null);
3188
});
3189
},
3190
function(err) {
3191
callback(null, connectedObjArray);
3192
}
3193
);
3194
};
3195
3196
callback(null, roomObj);
3197
};
3198
3199
3200
/**
3201
* NOT YET IMPLEMENTED - Gets session object for a given easyrtcsid. Returns null if session not found.
3202
* The returned session object includes functions for managing session fields.
3203
*
3204
* @memberof pub.appObj
3205
* @param {string} easyrtcsid EasyRTC session identifier
3206
* @param {function(?Error, Object=)} callback Callback with error and object containing EasyRTC session object.
3207
*/
3208
appObj.session = function(easyrtcsid, callback) {
3209
3210
if (!e.app[appName].session[easyrtcsid]) {
3211
pub.util.logWarning("Attempt to request non-existent easyrtcsid: '" + easyrtcsid + "'");
3212
callback(new pub.util.ApplicationWarning("Attempt to request non-existent easyrtcsid: '" + easyrtcsid + "'"));
3213
return;
3214
}
3215
3216
/**
3217
* The primary method for interfacing with an EasyRTC session.
3218
*
3219
* @class sessionObj
3220
* @memberof pub.appObj
3221
*/
3222
var sessionObj = {};
3223
3224
3225
/**
3226
* Expose all event functions
3227
*
3228
* @memberof pub.appObj.sessionObj
3229
*/
3230
sessionObj.events = pub.events;
3231
3232
3233
/**
3234
* Expose all utility functions
3235
*
3236
* @memberof pub.appObj.sessionObj
3237
*/
3238
sessionObj.util = pub.util;
3239
3240
3241
/**
3242
* Returns the application object to which the session belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
3243
*
3244
* @memberof pub.appObj.sessionObj
3245
* @return {Object} The application object
3246
*/
3247
sessionObj.getApp = function() {
3248
return appObj;
3249
};
3250
3251
3252
/**
3253
* Returns the application name for the application to which the session belongs. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
3254
*
3255
* @memberof pub.appObj.sessionObj
3256
* @return {string} The application name
3257
*/
3258
sessionObj.getAppName = function() {
3259
return appName;
3260
};
3261
3262
3263
/**
3264
* Returns the easyrtcsid for the session. Note that unlike most EasyRTC functions, this returns a value and does not use a callback.
3265
*
3266
* @memberof pub.appObj.sessionObj
3267
* @return {string} Returns the easyrtcsid, which is the EasyRTC unique identifier for a session.
3268
*/
3269
sessionObj.getEasyrtcsid = function() {
3270
return easyrtcsid;
3271
};
3272
3273
/**
3274
* Returns the easyrtcsid for the session. Old SessionKey name kept for transition purposes. Use getEasyrtcsid();
3275
*
3276
* @memberof pub.appObj.sessionObj
3277
* @ignore
3278
*/
3279
sessionObj.getSessionKey = sessionObj.getEasyrtcsid;
3280
3281
3282
/**
3283
* Returns session level field object for a given field name to a provided callback.
3284
*
3285
* @memberof pub.appObj.sessionObj
3286
* @param {string} fieldName Field name
3287
* @param {function(?Error, Object=)} callback Callback with error and field value (any type)
3288
*/
3289
sessionObj.getField = function(fieldName, callback) {
3290
if (!e.app[appName].session[easyrtcsid].field[fieldName]) {
3291
pub.util.logDebug("Can not find session field: '" + fieldName + "'");
3292
callback(new pub.util.ApplicationWarning("Can not find session field: '" + fieldName + "'"));
3293
return;
3294
}
3295
callback(null, pub.util.deepCopy(e.app[appName].session[easyrtcsid].field[fieldName]));
3296
};
3297
3298
3299
/**
3300
* Returns session level field object for a given field name. If the field is not set, it will return a field object will a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
3301
*
3302
* @memberof pub.appObj.sessionObj
3303
* @param {string} fieldName Field name
3304
* @returns {Object} Field object
3305
*/
3306
sessionObj.getFieldSync = function(fieldName) {
3307
if (!e.app[appName].session[easyrtcsid].field[fieldName]) {
3308
return {"fieldName": fieldName, "fieldOption": {}, "fieldValue": null};
3309
}
3310
return pub.util.deepCopy(e.app[appName].session[easyrtcsid].field[fieldName]);
3311
};
3312
3313
3314
/**
3315
* Returns session level field value for a given field name. If the field is not set, it will return a null field value. This is a synchronous function, thus may not be available in custom cases where state is not kept in memory.
3316
*
3317
* @memberof pub.appObj.sessionObj
3318
* @param {string} fieldName Field name
3319
* @returns {?*} Field value
3320
*/
3321
sessionObj.getFieldValueSync = function(fieldName) {
3322
if (!e.app[appName].session[easyrtcsid].field[fieldName]) {
3323
return null;
3324
}
3325
return pub.util.deepCopy(e.app[appName].session[easyrtcsid].field[fieldName].fieldValue);
3326
};
3327
3328
3329
/**
3330
* Returns an object containing all field names and values within the session to a provided callback. Can be limited to fields with isShared option set to true.
3331
*
3332
* @memberof pub.appObj.sessionObj
3333
* @param {boolean} limitToIsShared Limits returned fields to those which have the isShared option set to true.
3334
* @param {function(?Error, Object=)} callback Callback with error and object containing field names and values.
3335
*/
3336
sessionObj.getFields = function(limitToIsShared, callback) {
3337
var fieldObj = {};
3338
for (var fieldName in e.app[appName].session[easyrtcsid].field) {
3339
if (!limitToIsShared || e.app[appName].session[easyrtcsid].field[fieldName].fieldOption.isShared) {
3340
fieldObj[fieldName] = {
3341
fieldName: fieldName,
3342
fieldValue: pub.util.deepCopy(e.app[appName].session[easyrtcsid].field[fieldName].fieldValue)
3343
};
3344
}
3345
}
3346
callback(null, fieldObj);
3347
};
3348
3349
3350
/**
3351
* Sets session field value for a given field name.
3352
*
3353
* @memberof pub.appObj.sessionObj
3354
* @param {string} fieldName Must be formatted according to "fieldNameRegExp" option.
3355
* @param {Object} fieldValue
3356
* @param {?Object} fieldOption Field options (such as isShared which defaults to false)
3357
* @param {nextCallback} [next] A success callback of form next(err). Possible err will be instanceof (ApplicationWarning).
3358
*/
3359
sessionObj.setField = function(fieldName, fieldValue, fieldOption, next) {
3360
pub.util.logDebug("[" + appName + "] Session [" + easyrtcsid + "] - Setting field [" + fieldName + "]", fieldValue);
3361
if (!_.isFunction(next)) {
3362
next = pub.util.nextToNowhere;
3363
}
3364
3365
if (!pub.getOption("fieldNameRegExp").test(fieldName)) {
3366
pub.util.logWarning("Can not create session field with improper name: '" + fieldName + "'");
3367
next(new pub.util.ApplicationWarning("Can not create session field with improper name: '" + fieldName + "'"));
3368
return;
3369
}
3370
3371
e.app[appName].session[easyrtcsid].field[fieldName] = {
3372
fieldName: fieldName,
3373
fieldValue: fieldValue,
3374
fieldOption: {isShared: ((_.isObject(fieldOption) && fieldOption.isShared) ? true : false)}
3375
};
3376
3377
next(null);
3378
};
3379
3380
sessionObj.emitSessionDataFieldUpdate = function(next) {
3381
sessionObj.getFields(true, function(err, fieldObj) {
3382
if (err) {
3383
next(err);
3384
return;
3385
}
3386
var outgoingMsg = {"msgData": {"sessionData": {}}};
3387
outgoingMsg.msgData.sessionData = {
3388
"easyrtcsid": easyrtcsid,
3389
"sessionStatus": "update"
3390
};
3391
outgoingMsg.msgData.sessionData.field = fieldObj;
3392
// Loop through all active connection objects belonging to session
3393
async.each(
3394
Object.keys(e.app[appName].session[easyrtcsid].toConnection),
3395
function(currentEasyrtcid, asyncCallback) {
3396
3397
// Retrieve a connection object, then send the sessionData message.
3398
appObj.connection(currentEasyrtcid, function(err, targetConnectionObj) {
3399
if (err || !_.isObject(targetConnectionObj)) {
3400
pub.util.logDebug("[" + currentEasyrtcid + "] Could not get connection object to send session data field update. Client may have disconnected.");
3401
asyncCallback(null);
3402
return;
3403
}
3404
3405
// Emit sessionData easyrtcCmd to each connection
3406
pub.events.emit("emitEasyrtcCmd", targetConnectionObj, "sessionData", outgoingMsg, function(msg) {
3407
}, function(err) {
3408
// Ignore errors if unable to send to a socket.
3409
asyncCallback(null);
3410
});
3411
});
3412
},
3413
function(err) {
3414
next(null);
3415
}
3416
);
3417
});
3418
};
3419
3420
callback(null, sessionObj);
3421
};
3422
3423
callback(null, appObj);
3424
};
3425
3426
3427
// Documenting global callbacks
3428
/**
3429
* The next callback is called upon completion of a method. If the `err` parameter is null, than the method completed successfully.
3430
*
3431
* @callback nextCallback
3432
* @param {?Error} err Optional Error object. If it is null, than assume no error has occurred.
3433
*/
3434
3435
3436
/**
3437
* The application callback is called upon completion of a method which is meant to deliver an application object. If the `err` parameter is null, than the method completed successfully.
3438
*
3439
* @callback appCallback
3440
* @param {?Error} err Error object. If it is null, than assume no error has occurred.
3441
* @param {?Object} appObj Application object. Will be null if an error has occurred.
3442
*/
3443
3444
3445
/**
3446
* The connection callback is called upon completion of a method which is meant to deliver a connection object. If the `err` parameter is null, than the method completed successfully.
3447
*
3448
* @callback connectionCallback
3449
* @param {?Error} err Error object. If it is null, than assume no error has occurred.
3450
* @param {?Object} connectionObj Connection object. Will be null if an error has occurred.
3451
*/
3452
3453
3454
/**
3455
* The room callback is called upon completion of a method which is meant to deliver a room object. If the `err` parameter is null, than the method completed successfully.
3456
*
3457
* @callback roomCallback
3458
* @param {?Error} err Error object. If it is null, than assume no error has occurred.
3459
* @param {?Object} roomObj Room object. Will be null if an error has occurred.
3460
*/
3461
3462
// Documenting Custom Type-Definitions
3463
/**
3464
* An error object
3465
*
3466
* @typedef {Object} Error
3467
*/
3468
3469
// Running the default listeners to initialize the events
3470
pub.events.setDefaultListeners();