Browse Source

handle external auth; xmpp auth tbd

j8
isymchych 9 years ago
parent
commit
58d1c76ab0
7 changed files with 429 additions and 172 deletions
  1. 79
    73
      app.js
  2. 119
    42
      lib-jitsi-meet.js
  3. 100
    0
      modules/AuthHandler.js
  4. 4
    0
      modules/UI/UI.js
  5. 39
    56
      modules/UI/authentication/LoginDialog.js
  6. 5
    1
      modules/UI/util/UIUtil.js
  7. 83
    0
      modules/connection.js

+ 79
- 73
app.js View File

17
 import RoomnameGenerator from './modules/util/RoomnameGenerator';
17
 import RoomnameGenerator from './modules/util/RoomnameGenerator';
18
 import CQEvents from './service/connectionquality/CQEvents';
18
 import CQEvents from './service/connectionquality/CQEvents';
19
 import UIEvents from './service/UI/UIEvents';
19
 import UIEvents from './service/UI/UIEvents';
20
+import LoginDialog from './modules/UI/authentication/LoginDialog';
21
+import UIUtil from './modules/UI/util/UIUtil';
22
+
23
+import {openConnection} from './modules/connection';
24
+import AuthHandler from './modules/AuthHandler';
20
 
25
 
21
 import createRoomLocker from './modules/RoomLocker';
26
 import createRoomLocker from './modules/RoomLocker';
22
 
27
 
42
          location ~ ^/([a-zA-Z0-9]+)$ {
47
          location ~ ^/([a-zA-Z0-9]+)$ {
43
          rewrite ^/(.*)$ / break;
48
          rewrite ^/(.*)$ / break;
44
          }
49
          }
45
-         */
50
+        */
46
         if (path.length > 1) {
51
         if (path.length > 1) {
47
             roomName = path.substr(1).toLowerCase();
52
             roomName = path.substr(1).toLowerCase();
48
         } else {
53
         } else {
104
 
109
 
105
 const ConnectionEvents = JitsiMeetJS.events.connection;
110
 const ConnectionEvents = JitsiMeetJS.events.connection;
106
 const ConnectionErrors = JitsiMeetJS.errors.connection;
111
 const ConnectionErrors = JitsiMeetJS.errors.connection;
107
-function connect() {
108
-    let connection = new JitsiMeetJS.JitsiConnection(null, null, {
109
-        hosts: config.hosts,
110
-        bosh: config.bosh,
111
-        clientNode: config.clientNode
112
-    });
113
 
112
 
114
-    return new Promise(function (resolve, reject) {
115
-        let handlers = {};
116
-
117
-        function unsubscribe () {
118
-            Object.keys(handlers).forEach(function (event) {
119
-                connection.removeEventListener(event, handlers[event]);
120
-            });
121
-        }
122
-
123
-        handlers[ConnectionEvents.CONNECTION_ESTABLISHED] = function () {
124
-            console.log('CONNECTED');
125
-            unsubscribe();
126
-            resolve(connection);
127
-        };
128
-
129
-        function listenForFailure (event) {
130
-            handlers[event] = function (...args) {
131
-                console.error(`CONNECTION FAILED: ${event}`, ...args);
132
-
133
-                unsubscribe();
134
-                reject([event, ...args]);
135
-            };
136
-        }
137
-
138
-        listenForFailure(ConnectionEvents.CONNECTION_FAILED);
139
-        listenForFailure(ConnectionErrors.PASSWORD_REQUIRED);
140
-        listenForFailure(ConnectionErrors.CONNECTION_ERROR);
141
-        listenForFailure(ConnectionErrors.OTHER_ERRORS);
142
-
143
-        // install event listeners
144
-        Object.keys(handlers).forEach(function (event) {
145
-            connection.addEventListener(event, handlers[event]);
146
-        });
147
-
148
-        connection.connect();
149
-    }).catch(function (err) {
150
-        if (err[0] === ConnectionErrors.PASSWORD_REQUIRED) {
151
-            // FIXME ask for password and try again
152
-            return connect();
153
-        }
154
-        console.error('FAILED TO CONNECT', err);
155
-        APP.UI.notifyConnectionFailed(err[1]);
156
-
157
-        throw new Error(err[0]);
158
-    });
159
-}
160
-
161
-var ConferenceEvents = JitsiMeetJS.events.conference;
162
-var ConferenceErrors = JitsiMeetJS.errors.conference;
113
+const ConferenceEvents = JitsiMeetJS.events.conference;
114
+const ConferenceErrors = JitsiMeetJS.errors.conference;
163
 function initConference(localTracks, connection) {
115
 function initConference(localTracks, connection) {
164
     let room = connection.initJitsiConference(APP.conference.roomName, {
116
     let room = connection.initJitsiConference(APP.conference.roomName, {
165
         openSctp: config.openSctp,
117
         openSctp: config.openSctp,
378
         APP.UI.changeDisplayName(APP.conference.localId, nickname);
330
         APP.UI.changeDisplayName(APP.conference.localId, nickname);
379
     });
331
     });
380
 
332
 
381
-    room.on(ConferenceErrors.CONNECTION_ERROR, function () {
382
-        // FIXME handle
383
-    });
384
-
385
     APP.UI.addListener(
333
     APP.UI.addListener(
386
         UIEvents.START_MUTED_CHANGED,
334
         UIEvents.START_MUTED_CHANGED,
387
         function (startAudioMuted, startVideoMuted) {
335
         function (startAudioMuted, startVideoMuted) {
461
     });
409
     });
462
 
410
 
463
     APP.UI.addListener(UIEvents.AUTH_CLICKED, function () {
411
     APP.UI.addListener(UIEvents.AUTH_CLICKED, function () {
464
-        // FIXME handle
412
+        AuthHandler.authenticate(room);
465
     });
413
     });
466
 
414
 
467
     APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, function (id) {
415
     APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, function (id) {
477
     });
425
     });
478
 
426
 
479
     return new Promise(function (resolve, reject) {
427
     return new Promise(function (resolve, reject) {
480
-        room.on(ConferenceEvents.CONFERENCE_JOINED, resolve);
428
+        room.on(ConferenceEvents.CONFERENCE_JOINED, handleConferenceJoined);
429
+        room.on(ConferenceEvents.CONFERENCE_FAILED, onConferenceFailed);
481
 
430
 
482
-        room.on(ConferenceErrors.PASSWORD_REQUIRED, function () {
483
-            APP.UI.markRoomLocked(true);
484
-            roomLocker.requirePassword().then(function () {
485
-                room.join(roomLocker.password);
486
-            });
487
-        });
431
+        let password;
432
+        let reconnectTimeout;
488
 
433
 
489
-        // FIXME handle errors here
434
+        function unsubscribe() {
435
+            room.off(
436
+                ConferenceEvents.CONFERENCE_JOINED, handleConferenceJoined
437
+            );
438
+            room.off(
439
+                ConferenceEvents.CONFERENCE_FAILED, onConferenceFailed
440
+            );
441
+            if (reconnectTimeout) {
442
+                clearTimeout(reconnectTimeout);
443
+            }
444
+            AuthHandler.closeAuth();
445
+        }
490
 
446
 
491
-        room.join();
492
-    }).catch(function (err) {
493
-        // FIXME notify that we cannot conenct to the room
447
+        function handleConferenceJoined() {
448
+            unsubscribe();
449
+            resolve();
450
+        }
494
 
451
 
495
-        throw new Error(err[0]);
452
+        function handleConferenceFailed(err) {
453
+            unsubscribe();
454
+            reject(err);
455
+        }
456
+
457
+        function onConferenceFailed(err, msg = '') {
458
+            console.error('CONFERENCE FAILED:', err, msg);
459
+            switch (err) {
460
+                // room is locked by the password
461
+            case ConferenceErrors.PASSWORD_REQUIRED:
462
+                APP.UI.markRoomLocked(true);
463
+                roomLocker.requirePassword().then(function () {
464
+                    room.join(roomLocker.password);
465
+                });
466
+                break;
467
+
468
+            case ConferenceErrors.CONNECTION_ERROR:
469
+                APP.UI.notifyConnectionFailed(msg);
470
+                break;
471
+
472
+                // not enough rights to create conference
473
+            case ConferenceErrors.AUTHENTICATION_REQUIRED:
474
+                // schedule reconnect to check if someone else created the room
475
+                reconnectTimeout = setTimeout(function () {
476
+                    room.join(password);
477
+                }, 5000);
478
+
479
+                // notify user that auth is required
480
+                AuthHandler.requireAuth(APP.conference.roomName);
481
+                break;
482
+
483
+            default:
484
+                handleConferenceFailed(err);
485
+            }
486
+        }
487
+
488
+        room.join(password);
496
     });
489
     });
497
 }
490
 }
498
 
491
 
505
     });
498
     });
506
 }
499
 }
507
 
500
 
501
+function connect() {
502
+    return openConnection({retry: true}).catch(function (err) {
503
+        if (err === ConnectionErrors.PASSWORD_REQUIRED) {
504
+            APP.UI.notifyTokenAuthFailed();
505
+        } else {
506
+            APP.UI.notifyConnectionFailed(err);
507
+        }
508
+        throw err;
509
+    });
510
+}
511
+
508
 function init() {
512
 function init() {
509
     APP.UI.start();
513
     APP.UI.start();
514
+
510
     JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
515
     JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
516
+
511
     JitsiMeetJS.init().then(function () {
517
     JitsiMeetJS.init().then(function () {
512
         return Promise.all([createLocalTracks(), connect()]);
518
         return Promise.all([createLocalTracks(), connect()]);
513
     }).then(function ([tracks, connection]) {
519
     }).then(function ([tracks, connection]) {

+ 119
- 42
lib-jitsi-meet.js View File

1
 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JitsiMeetJS = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
1
 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JitsiMeetJS = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
 (function (__filename){
2
 (function (__filename){
3
-/* global Strophe, $ */
3
+/* global Strophe, $, Promise */
4
 /* jshint -W101 */
4
 /* jshint -W101 */
5
 var logger = require("jitsi-meet-logger").getLogger(__filename);
5
 var logger = require("jitsi-meet-logger").getLogger(__filename);
6
 var RTC = require("./modules/RTC/RTC");
6
 var RTC = require("./modules/RTC/RTC");
54
         this.room.join(password, this.connection.tokenPassword);
54
         this.room.join(password, this.connection.tokenPassword);
55
 };
55
 };
56
 
56
 
57
+/**
58
+ * Check if joined to the conference.
59
+ */
60
+JitsiConference.prototype.isJoined = function () {
61
+    return this.room && this.room.joined;
62
+};
63
+
57
 /**
64
 /**
58
  * Leaves the conference.
65
  * Leaves the conference.
59
  */
66
  */
63
     this.room = null;
70
     this.room = null;
64
 };
71
 };
65
 
72
 
73
+/**
74
+ * Returns name of this conference.
75
+ */
76
+JitsiConference.prototype.getName = function () {
77
+    return this.options.name;
78
+};
79
+
80
+/**
81
+ * Check if external authentication is enabled for this conference.
82
+ */
83
+JitsiConference.prototype.isExternalAuthEnabled = function () {
84
+    return this.room && this.room.moderator.isExternalAuthEnabled();
85
+};
86
+
87
+/**
88
+ * Get url for external authentication.
89
+ * @param {boolean} [urlForPopup] if true then return url for login popup,
90
+ *                                else url of login page.
91
+ * @returns {Promise}
92
+ */
93
+JitsiConference.prototype.getExternalAuthUrl = function (urlForPopup) {
94
+    return new Promise(function (resolve, reject) {
95
+        if (!this.isExternalAuthEnabled()) {
96
+            reject();
97
+            return;
98
+        }
99
+        if (urlForPopup) {
100
+            this.room.moderator.getPopupLoginUrl(resolve, reject);
101
+        } else {
102
+            this.room.moderator.getLoginUrl(resolve, reject);
103
+        }
104
+    }.bind(this));
105
+};
106
+
66
 /**
107
 /**
67
  * Returns the local tracks.
108
  * Returns the local tracks.
68
  */
109
  */
225
  * @param track the JitsiLocalTrack object.
266
  * @param track the JitsiLocalTrack object.
226
  */
267
  */
227
 JitsiConference.prototype.removeTrack = function (track) {
268
 JitsiConference.prototype.removeTrack = function (track) {
269
+    if(!this.room){
270
+        if(this.rtc)
271
+            this.rtc.removeLocalStream(track);
272
+        return;
273
+    }
228
     this.room.removeStream(track.getOriginalStream(), function(){
274
     this.room.removeStream(track.getOriginalStream(), function(){
229
         this.rtc.removeLocalStream(track);
275
         this.rtc.removeLocalStream(track);
230
         this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);
276
         this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);
264
     }, function (err) {
310
     }, function (err) {
265
       reject(err);
311
       reject(err);
266
     }, function () {
312
     }, function () {
267
-      reject(JitsiConferenceErrors.PASSWORD_REQUIRED);
313
+      reject(JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED);
268
     });
314
     });
269
   });
315
   });
270
 };
316
 };
475
     conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
521
     conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
476
         conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_JOINED);
522
         conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_JOINED);
477
     });
523
     });
524
+    conference.room.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) {
525
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONNECTION_ERROR, pres);
526
+    });
527
+    conference.room.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) {
528
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONNECTION_ERROR, pres);
529
+    });
530
+    conference.room.addListener(XMPPEvents.PASSWORD_REQUIRED, function (pres) {
531
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.PASSWORD_REQUIRED, pres);
532
+    });
533
+    conference.room.addListener(XMPPEvents.AUTHENTICATION_REQUIRED, function () {
534
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.AUTHENTICATION_REQUIRED);
535
+    });
478
 //    FIXME
536
 //    FIXME
479
 //    conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
537
 //    conference.room.addListener(XMPPEvents.MUC_JOINED, function () {
480
 //        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT);
538
 //        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_LEFT);
498
         conference.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED);
556
         conference.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED);
499
     });
557
     });
500
     conference.room.addListener(XMPPEvents.CONFERENCE_SETUP_FAILED, function () {
558
     conference.room.addListener(XMPPEvents.CONFERENCE_SETUP_FAILED, function () {
501
-        conference.eventEmitter.emit(JitsiConferenceEvents.SETUP_FAILED);
559
+        conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.SETUP_FAILED);
502
     });
560
     });
503
 
561
 
504
     conference.room.addListener(XMPPEvents.MESSAGE_RECEIVED, function (jid, displayName, txt, myJid, ts) {
562
     conference.room.addListener(XMPPEvents.MESSAGE_RECEIVED, function (jid, displayName, txt, myJid, ts) {
561
      * Indicates that a password is required in order to join the conference.
619
      * Indicates that a password is required in order to join the conference.
562
      */
620
      */
563
     PASSWORD_REQUIRED: "conference.passwordRequired",
621
     PASSWORD_REQUIRED: "conference.passwordRequired",
622
+    /**
623
+     * Indicates that client must be authenticated to create the conference.
624
+     */
625
+    AUTHENTICATION_REQUIRED: "conference.authenticationRequired",
564
     /**
626
     /**
565
      * Indicates that password cannot be set for this conference.
627
      * Indicates that password cannot be set for this conference.
566
      */
628
      */
570
      * conference.
632
      * conference.
571
      */
633
      */
572
     CONNECTION_ERROR: "conference.connectionError",
634
     CONNECTION_ERROR: "conference.connectionError",
635
+    /**
636
+     * Indicates that the conference setup failed.
637
+     */
638
+    SETUP_FAILED: "conference.setup_failed",
573
     /**
639
     /**
574
      * Indicates that there is no available videobridge.
640
      * Indicates that there is no available videobridge.
575
      */
641
      */
653
      */
719
      */
654
     CONNECTION_RESTORED: "conference.connectionRestored",
720
     CONNECTION_RESTORED: "conference.connectionRestored",
655
     /**
721
     /**
656
-     * Indicates that the conference setup failed.
722
+     * Indicates that conference failed.
657
      */
723
      */
658
-    SETUP_FAILED: "conference.setup_failed",
724
+    CONFERENCE_FAILED: "conference.failed",
659
     /**
725
     /**
660
      * Indicates that conference has been joined.
726
      * Indicates that conference has been joined.
661
      */
727
      */
1346
     this.dontFireRemoveEvent = false;
1412
     this.dontFireRemoveEvent = false;
1347
     this.resolution = resolution;
1413
     this.resolution = resolution;
1348
     this.startMuted = false;
1414
     this.startMuted = false;
1349
-    this.isLocal = true;
1350
     var self = this;
1415
     var self = this;
1351
     JitsiTrack.call(this, null, stream,
1416
     JitsiTrack.call(this, null, stream,
1352
         function () {
1417
         function () {
1517
     this.videoType = data.videoType;
1582
     this.videoType = data.videoType;
1518
     this.ssrc = ssrc;
1583
     this.ssrc = ssrc;
1519
     this.muted = false;
1584
     this.muted = false;
1520
-    this.isLocal = false;
1521
     if((this.type === JitsiTrack.AUDIO && data.audiomuted)
1585
     if((this.type === JitsiTrack.AUDIO && data.audiomuted)
1522
       || (this.type === JitsiTrack.VIDEO && data.videomuted)) {
1586
       || (this.type === JitsiTrack.VIDEO && data.videomuted)) {
1523
         this.muted = true;
1587
         this.muted = true;
3391
 }).call(this,"/modules/RTC/ScreenObtainer.js")
3455
 }).call(this,"/modules/RTC/ScreenObtainer.js")
3392
 },{"../../service/desktopsharing/DesktopSharingEventTypes":82,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":47}],20:[function(require,module,exports){
3456
 },{"../../service/desktopsharing/DesktopSharingEventTypes":82,"./RTCBrowserType":17,"./adapter.screenshare":20,"jitsi-meet-logger":47}],20:[function(require,module,exports){
3393
 (function (__filename){
3457
 (function (__filename){
3394
-/*! adapterjs - v0.12.0 - 2015-09-04 */
3458
+/*! adapterjs - v0.12.3 - 2015-11-16 */
3395
 var console = require("jitsi-meet-logger").getLogger(__filename);
3459
 var console = require("jitsi-meet-logger").getLogger(__filename);
3396
-
3397
 // Adapter's interface.
3460
 // Adapter's interface.
3398
 var AdapterJS = AdapterJS || {};
3461
 var AdapterJS = AdapterJS || {};
3399
 
3462
 
3411
 // AdapterJS.options.hidePluginInstallPrompt = true;
3474
 // AdapterJS.options.hidePluginInstallPrompt = true;
3412
 
3475
 
3413
 // AdapterJS version
3476
 // AdapterJS version
3414
-AdapterJS.VERSION = '0.12.0';
3477
+AdapterJS.VERSION = '0.12.3';
3415
 
3478
 
3416
 // This function will be called when the WebRTC API is ready to be used
3479
 // This function will be called when the WebRTC API is ready to be used
3417
 // Whether it is the native implementation (Chrome, Firefox, Opera) or
3480
 // Whether it is the native implementation (Chrome, Firefox, Opera) or
4001
 
4064
 
4002
   createIceServers = function (urls, username, password) {
4065
   createIceServers = function (urls, username, password) {
4003
     var iceServers = [];
4066
     var iceServers = [];
4004
-    for (i = 0; i < urls.length; i++) {
4067
+    for (var i = 0; i < urls.length; i++) {
4005
       var iceServer = createIceServer(urls[i], username, password);
4068
       var iceServer = createIceServer(urls[i], username, password);
4006
       if (iceServer !== null) {
4069
       if (iceServer !== null) {
4007
         iceServers.push(iceServer);
4070
         iceServers.push(iceServer);
4091
         'username' : username
4154
         'username' : username
4092
       };
4155
       };
4093
     } else {
4156
     } else {
4094
-      for (i = 0; i < urls.length; i++) {
4157
+      for (var i = 0; i < urls.length; i++) {
4095
         var iceServer = createIceServer(urls[i], username, password);
4158
         var iceServer = createIceServer(urls[i], username, password);
4096
         if (iceServer !== null) {
4159
         if (iceServer !== null) {
4097
           iceServers.push(iceServer);
4160
           iceServers.push(iceServer);
4393
         return;
4456
         return;
4394
       }
4457
       }
4395
 
4458
 
4396
-      var streamId
4459
+      var streamId;
4397
       if (stream === null) {
4460
       if (stream === null) {
4398
         streamId = '';
4461
         streamId = '';
4399
-      }
4400
-      else {
4401
-        stream.enableSoundTracks(true); // TODO: remove on 0.12.0
4462
+      } else {
4463
+        if (typeof stream.enableSoundTracks !== 'undefined') {
4464
+          stream.enableSoundTracks(true);
4465
+        }
4402
         streamId = stream.id;
4466
         streamId = stream.id;
4403
       }
4467
       }
4404
 
4468
 
4440
 
4504
 
4441
         var height = '';
4505
         var height = '';
4442
         var width = '';
4506
         var width = '';
4443
-        if (element.getBoundingClientRect) {
4444
-          var rectObject = element.getBoundingClientRect();
4445
-          width = rectObject.width + 'px';
4446
-          height = rectObject.height + 'px';
4507
+        if (element.clientWidth || element.clientHeight) {
4508
+          width = element.clientWidth;
4509
+          height = element.clientHeight;
4447
         }
4510
         }
4448
-        else if (element.width) {
4511
+        else if (element.width || element.height) {
4449
           width = element.width;
4512
           width = element.width;
4450
           height = element.height;
4513
           height = element.height;
4451
-        } else {
4452
-          // TODO: What scenario could bring us here?
4453
         }
4514
         }
4454
 
4515
 
4455
         element.parentNode.insertBefore(frag, element);
4516
         element.parentNode.insertBefore(frag, element);
4468
         element.setStreamId(streamId);
4529
         element.setStreamId(streamId);
4469
       }
4530
       }
4470
       var newElement = document.getElementById(elementId);
4531
       var newElement = document.getElementById(elementId);
4471
-      newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
4472
-      newElement.onplay    = (element.onplay)    ? element.onplay    : function (arg) {};
4473
-      newElement.onclick   = (element.onclick)   ? element.onclick   : function (arg) {};
4474
-      if (isIE) { // on IE the event needs to be plugged manually
4475
-        newElement.attachEvent('onplaying', newElement.onplaying);
4476
-        newElement.attachEvent('onplay', newElement.onplay);
4477
-        newElement._TemOnClick = function (id) {
4478
-          var arg = {
4479
-            srcElement : document.getElementById(id)
4480
-          };
4481
-          newElement.onclick(arg);
4482
-        };
4483
-      }
4532
+      AdapterJS.forwardEventHandlers(newElement, element, Object.getPrototypeOf(element));
4484
 
4533
 
4485
       return newElement;
4534
       return newElement;
4486
     };
4535
     };
4503
       }
4552
       }
4504
     };
4553
     };
4505
 
4554
 
4555
+    AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
4556
+
4557
+      properties = Object.getOwnPropertyNames( prototype );
4558
+
4559
+      for(prop in properties) {
4560
+        propName = properties[prop];
4561
+
4562
+        if (typeof(propName.slice) === 'function') {
4563
+          if (propName.slice(0,2) == 'on' && srcElem[propName] != null) {
4564
+            if (isIE) {
4565
+              destElem.attachEvent(propName,srcElem[propName]);
4566
+            } else {
4567
+              destElem.addEventListener(propName.slice(2), srcElem[propName], false)
4568
+            }
4569
+          } else {
4570
+            //TODO (http://jira.temasys.com.sg/browse/TWP-328) Forward non-event properties ?
4571
+          }
4572
+        }
4573
+      }
4574
+
4575
+      var subPrototype = Object.getPrototypeOf(prototype)
4576
+      if(subPrototype != null) {
4577
+        AdapterJS.forwardEventHandlers(destElem, srcElem, subPrototype);
4578
+      }
4579
+    }
4580
+
4506
     RTCIceCandidate = function (candidate) {
4581
     RTCIceCandidate = function (candidate) {
4507
       if (!candidate.sdpMid) {
4582
       if (!candidate.sdpMid) {
4508
         candidate.sdpMid = '';
4583
         candidate.sdpMid = '';
9659
     var Interop = require('sdp-interop').Interop;
9734
     var Interop = require('sdp-interop').Interop;
9660
     this.interop = new Interop();
9735
     this.interop = new Interop();
9661
     var Simulcast = require('sdp-simulcast');
9736
     var Simulcast = require('sdp-simulcast');
9662
-    this.simulcast = new Simulcast({numOfLayers: 2, explodeRemoteSimulcast: false});
9737
+    this.simulcast = new Simulcast({numOfLayers: 3, explodeRemoteSimulcast: false});
9663
 
9738
 
9664
     // override as desired
9739
     // override as desired
9665
     this.trace = function (what, info) {
9740
     this.trace = function (what, info) {
10441
     );
10516
     );
10442
 };
10517
 };
10443
 
10518
 
10444
-Moderator.prototype.getLoginUrl =  function (urlCallback) {
10519
+Moderator.prototype.getLoginUrl =  function (urlCallback, failureCallback) {
10445
     var iq = $iq({to: this.getFocusComponent(), type: 'get'});
10520
     var iq = $iq({to: this.getFocusComponent(), type: 'get'});
10446
     iq.c('login-url', {
10521
     iq.c('login-url', {
10447
         xmlns: 'http://jitsi.org/protocol/focus',
10522
         xmlns: 'http://jitsi.org/protocol/focus',
10459
             } else {
10534
             } else {
10460
                 logger.error(
10535
                 logger.error(
10461
                     "Failed to get auth url from the focus", result);
10536
                     "Failed to get auth url from the focus", result);
10537
+                failureCallback(result);
10462
             }
10538
             }
10463
         },
10539
         },
10464
         function (error) {
10540
         function (error) {
10465
             logger.error("Get auth url error", error);
10541
             logger.error("Get auth url error", error);
10542
+            failureCallback(error);
10466
         }
10543
         }
10467
     );
10544
     );
10468
 };
10545
 };
10469
-Moderator.prototype.getPopupLoginUrl =  function (urlCallback) {
10546
+
10547
+Moderator.prototype.getPopupLoginUrl = function (urlCallback, failureCallback) {
10470
     var iq = $iq({to: this.getFocusComponent(), type: 'get'});
10548
     var iq = $iq({to: this.getFocusComponent(), type: 'get'});
10471
     iq.c('login-url', {
10549
     iq.c('login-url', {
10472
         xmlns: 'http://jitsi.org/protocol/focus',
10550
         xmlns: 'http://jitsi.org/protocol/focus',
10485
             } else {
10563
             } else {
10486
                 logger.error(
10564
                 logger.error(
10487
                     "Failed to get POPUP auth url from the focus", result);
10565
                     "Failed to get POPUP auth url from the focus", result);
10566
+               failureCallback(result);
10488
             }
10567
             }
10489
         },
10568
         },
10490
         function (error) {
10569
         function (error) {
10491
             logger.error('Get POPUP auth url error', error);
10570
             logger.error('Get POPUP auth url error', error);
10571
+            failureCallback(error);
10492
         }
10572
         }
10493
     );
10573
     );
10494
 };
10574
 };
10523
 
10603
 
10524
 module.exports = Moderator;
10604
 module.exports = Moderator;
10525
 
10605
 
10526
-
10527
-
10528
-
10529
 }).call(this,"/modules/xmpp/moderator.js")
10606
 }).call(this,"/modules/xmpp/moderator.js")
10530
 },{"../../service/authentication/AuthenticationEvents":81,"../../service/xmpp/XMPPEvents":85,"../settings/Settings":21,"jitsi-meet-logger":47}],35:[function(require,module,exports){
10607
 },{"../../service/authentication/AuthenticationEvents":81,"../../service/xmpp/XMPPEvents":85,"../settings/Settings":21,"jitsi-meet-logger":47}],35:[function(require,module,exports){
10531
 (function (__filename){
10608
 (function (__filename){

+ 100
- 0
modules/AuthHandler.js View File

1
+/* global JitsiMeetJS */
2
+
3
+import LoginDialog from './UI/authentication/LoginDialog';
4
+import UIEvents from '../service/UI/UIEvents';
5
+import UIUtil from './UI/util/UIUtil';
6
+import {openConnection} from './connection';
7
+
8
+const ConferenceEvents = JitsiMeetJS.events.conference;
9
+
10
+let externalAuthWindow;
11
+let authRequiredDialog;
12
+
13
+function doExternalAuth (room, lockPassword) {
14
+    if (externalAuthWindow) {
15
+        externalAuthWindow.focus();
16
+        return;
17
+    }
18
+    if (room.isJoined()) {
19
+        room.getExternalAuthUrl(true).then(function (url) {
20
+            externalAuthWindow = LoginDialog.showExternalAuthDialog(
21
+                url,
22
+                function () {
23
+                    externalAuthWindow = null;
24
+                    room.join(lockPassword);
25
+                }
26
+            );
27
+        });
28
+    } else {
29
+        // If conference has not been started yet
30
+        // then  redirect to login page
31
+        room.getExternalAuthUrl().then(UIUtil.redirect);
32
+    }
33
+}
34
+
35
+function doXmppAuth (room, lockPassword) {
36
+    let loginDialog = LoginDialog.showAuthDialog(function (id, password) {
37
+        // auth "on the fly":
38
+        // 1. open new connection with proper id and password
39
+        // 2. connect to the room
40
+        // (this will store sessionId in the localStorage)
41
+        // 3. close new connection
42
+        // 4. reallocate focus in current room
43
+        openConnection({id, password}).then(function (connection) {
44
+            // open room
45
+            let newRoom = connection.initJitsiConference(room.getName());
46
+
47
+            newRoom.on(ConferenceEvents.CONFERENCE_FAILED, function (err) {
48
+                connection.disconnect();
49
+                loginDialog.displayError(err);
50
+            });
51
+            // FIXME finish "on the fly" auth
52
+            room.room.moderator.allocateConferenceFocus(function () {
53
+                connection.disconnect();
54
+                loginDialog.close();
55
+            });
56
+
57
+        }, function (err) {
58
+            loginDialog.displayError(err);
59
+        });
60
+    }, function () { // user canceled
61
+        loginDialog.close();
62
+    });
63
+}
64
+
65
+function authenticate (room, lockPassword) {
66
+    if (room.isExternalAuthEnabled()) {
67
+        doExternalAuth(room, lockPassword);
68
+    } else {
69
+        doXmppAuth();
70
+    }
71
+}
72
+
73
+function requireAuth(roomName) {
74
+    if (authRequiredDialog) {
75
+        return;
76
+    }
77
+
78
+    authRequiredDialog = LoginDialog.showAuthRequiredDialog(
79
+        roomName, authenticate
80
+    );
81
+}
82
+
83
+function closeAuth() {
84
+    if (externalAuthWindow) {
85
+        externalAuthWindow.close();
86
+        externalAuthWindow = null;
87
+    }
88
+
89
+    if (authRequiredDialog) {
90
+        authRequiredDialog.close();
91
+        authRequiredDialog = null;
92
+    }
93
+}
94
+
95
+
96
+export default {
97
+    authenticate,
98
+    requireAuth,
99
+    closeAuth
100
+};

+ 4
- 0
modules/UI/UI.js View File

736
     Toolbar.updateRecordingState(state);
736
     Toolbar.updateRecordingState(state);
737
 };
737
 };
738
 
738
 
739
+UI.notifyTokenAuthFailed = function () {
740
+    messageHandler.showError("dialog.error", "dialog.tokenAuthFailed");
741
+};
742
+
739
 module.exports = UI;
743
 module.exports = UI;

+ 39
- 56
modules/UI/authentication/LoginDialog.js View File

2
 
2
 
3
 var messageHandler = require('../util/MessageHandler');
3
 var messageHandler = require('../util/MessageHandler');
4
 
4
 
5
-//FIXME: use LoginDialog to add retries to XMPP.connect method used when
6
-// anonymous domain is not enabled
5
+function getPasswordInputHtml() {
6
+    let placeholder = config.hosts.authdomain
7
+        ? "user identity"
8
+        : "user@domain.net";
9
+    let passRequiredMsg = APP.translation.translateString(
10
+        "dialog.passwordRequired"
11
+    );
12
+    return `
13
+        <h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2>
14
+        <input name="username" type="text" placeholder=${placeholder} autofocus>
15
+        <input name="password" type="password"
16
+               data-i18n="[placeholder]dialog.userPassword"
17
+               placeholder="user password">
18
+    `;
19
+}
7
 
20
 
8
 function Dialog(successCallback, cancelCallback) {
21
 function Dialog(successCallback, cancelCallback) {
9
-    var message = '<h2 data-i18n="dialog.passwordRequired">';
10
-    message += APP.translation.translateString("dialog.passwordRequired");
11
-    message += '</h2>' +
12
-        '<input name="username" type="text" ';
13
-    if (config.hosts.authdomain) {
14
-        message += 'placeholder="user identity" autofocus>';
15
-    } else {
16
-        message += 'placeholder="user@domain.net" autofocus>';
17
-    }
18
-    message += '<input name="password" ' +
19
-        'type="password" data-i18n="[placeholder]dialog.userPassword"' +
20
-        ' placeholder="user password">';
21
-
22
-    var okButton = APP.translation.generateTranslationHTML("dialog.Ok");
23
-
24
-    var cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel");
25
-
26
-    var states = {
22
+    const states = {
27
         login: {
23
         login: {
28
-            html: message,
29
-            buttons: [
30
-                { title: okButton, value: true},
31
-                { title: cancelButton, value: false}
32
-            ],
24
+            html: getPasswordInputHtml(),
25
+            buttons: [{
26
+                title: APP.translation.generateTranslationHTML("dialog.Ok"),
27
+                value: true
28
+            }, {
29
+                title: APP.translation.generateTranslationHTML("dialog.Cancel"),
30
+                value: false
31
+            }],
33
             focus: ':input:first',
32
             focus: ':input:first',
34
             submit: function (e, v, m, f) {
33
             submit: function (e, v, m, f) {
35
                 e.preventDefault();
34
                 e.preventDefault();
36
                 if (v) {
35
                 if (v) {
37
-                    var jid = f.username;
38
-                    var password = f.password;
36
+                    let jid = f.username;
37
+                    let password = f.password;
39
                     if (jid && password) {
38
                     if (jid && password) {
40
                         connDialog.goToState('connecting');
39
                         connDialog.goToState('connecting');
41
                         successCallback(jid, password);
40
                         successCallback(jid, password);
55
         finished: {
54
         finished: {
56
             title: APP.translation.translateString('dialog.error'),
55
             title: APP.translation.translateString('dialog.error'),
57
             html:   '<div id="errorMessage"></div>',
56
             html:   '<div id="errorMessage"></div>',
58
-            buttons: [
59
-                {
60
-                    title: APP.translation.translateString('dialog.retry'),
61
-                    value: 'retry'
62
-                },
63
-                {
64
-                    title: APP.translation.translateString('dialog.Cancel'),
65
-                    value: 'cancel'
66
-                },
67
-            ],
57
+            buttons: [{
58
+                title: APP.translation.translateString('dialog.retry'),
59
+                value: 'retry'
60
+            }, {
61
+                title: APP.translation.generateTranslationHTML("dialog.Cancel"),
62
+                value: false
63
+            }],
68
             defaultButton: 0,
64
             defaultButton: 0,
69
             submit: function (e, v, m, f) {
65
             submit: function (e, v, m, f) {
70
                 e.preventDefault();
66
                 e.preventDefault();
71
                 if (v === 'retry') {
67
                 if (v === 'retry') {
72
                     connDialog.goToState('login');
68
                     connDialog.goToState('login');
73
                 } else {
69
                 } else {
70
+                    // User cancelled
74
                     cancelCallback();
71
                     cancelCallback();
75
                 }
72
                 }
76
             }
73
             }
104
     };
101
     };
105
 }
102
 }
106
 
103
 
107
-var LoginDialog = {
104
+const LoginDialog = {
108
 
105
 
109
-    /**
110
-     * Displays login prompt used to establish new XMPP connection. Given
111
-     * <tt>callback(Strophe.Connection, Strophe.Status)</tt> function will be
112
-     * called when we connect successfully(status === CONNECTED) or when we fail
113
-     * to do so. On connection failure program can call Dialog.close() method in
114
-     * order to cancel or do nothing to let the user retry.
115
-     * @param callback <tt>function(Strophe.Connection, Strophe.Status)</tt>
116
-     *        called when we either fail to connect or succeed(check
117
-     *        Strophe.Status).
118
-     * @param obtainSession <tt>true</tt> if we want to send ConferenceIQ to
119
-     *        Jicofo in order to create session-id after the connection is
120
-     *        established.
121
-     * @returns {Dialog}
122
-     */
123
-    show: function (successCallback, cancelCallback) {
106
+    showAuthDialog: function (successCallback, cancelCallback) {
124
         return new Dialog(successCallback, cancelCallback);
107
         return new Dialog(successCallback, cancelCallback);
125
     },
108
     },
126
 
109
 
156
             msg,
139
             msg,
157
             true,
140
             true,
158
             buttons,
141
             buttons,
159
-            function (onSubmitEvent, submitValue) {
142
+            function (e, submitValue) {
160
 
143
 
161
                 // Do not close the dialog yet
144
                 // Do not close the dialog yet
162
-                onSubmitEvent.preventDefault();
145
+                e.preventDefault();
163
 
146
 
164
                 // Open login popup
147
                 // Open login popup
165
                 if (submitValue === 'authNow') {
148
                 if (submitValue === 'authNow') {
170
     }
153
     }
171
 };
154
 };
172
 
155
 
173
-module.exports = LoginDialog;
156
+export default LoginDialog;

+ 5
- 1
modules/UI/util/UIUtil.js View File

114
           .filter(function (item) { return item; })
114
           .filter(function (item) { return item; })
115
           .join(',');
115
           .join(',');
116
         $(selector).hide();
116
         $(selector).hide();
117
-    }
117
+    },
118
+
119
+     redirect (url) {
120
+         window.location.href = url;
121
+     }
118
 };
122
 };
119
 
123
 
120
 export default UIUtil;
124
 export default UIUtil;

+ 83
- 0
modules/connection.js View File

1
+/* global APP, JitsiMeetJS, config */
2
+
3
+import LoginDialog from './UI/authentication/LoginDialog';
4
+
5
+const ConnectionEvents = JitsiMeetJS.events.connection;
6
+const ConnectionErrors = JitsiMeetJS.errors.connection;
7
+
8
+export function openConnection({retry, id, password}) {
9
+    let connection = new JitsiMeetJS.JitsiConnection(null, null, {
10
+        hosts: config.hosts,
11
+        bosh: config.bosh,
12
+        clientNode: config.clientNode
13
+    });
14
+
15
+    return new Promise(function (resolve, reject) {
16
+        connection.addEventListener(
17
+            ConnectionEvents.CONNECTION_ESTABLISHED, handleConnectionEstablished
18
+        );
19
+        connection.addEventListener(
20
+            ConnectionEvents.CONNECTION_FAILED, onConnectionFailed
21
+        );
22
+        let authDialog;
23
+
24
+        function unsubscribe() {
25
+            connection.removeEventListener(
26
+                ConnectionEvents.CONNECTION_ESTABLISHED,
27
+                handleConnectionEstablished
28
+            );
29
+            connection.removeEventListener(
30
+                ConnectionEvents.CONNECTION_FAILED, onConnectionFailed
31
+            );
32
+            if (authDialog) {
33
+                authDialog.close();
34
+            }
35
+        }
36
+
37
+        function handleConnectionEstablished() {
38
+            unsubscribe();
39
+            resolve(connection);
40
+        }
41
+
42
+        function handleConnectionFailed(err) {
43
+            unsubscribe();
44
+            reject(err);
45
+        }
46
+
47
+        function onConnectionFailed (err) {
48
+            console.error("CONNECTION FAILED:", err);
49
+
50
+            if (!retry) {
51
+                handleConnectionFailed(err);
52
+                return;
53
+            }
54
+
55
+            // retry only if auth failed
56
+            if (err !== ConnectionErrors.PASSWORD_REQUIRED) {
57
+                handleConnectionFailed(err);
58
+                return;
59
+            }
60
+
61
+            // do not retry if token is not valid
62
+            if (config.token) {
63
+                handleConnectionFailed(err);
64
+                return;
65
+            }
66
+
67
+            // ask for password and try again
68
+
69
+            if (authDialog) {
70
+                authDialog.displayError(err);
71
+                return;
72
+            }
73
+
74
+            authDialog = LoginDialog.showAuthDialog(
75
+                function (id, password) {
76
+                    connection.connect({id, password});
77
+                }
78
+            );
79
+        }
80
+
81
+        connection.connect(id, password);
82
+    });
83
+}

Loading…
Cancel
Save