diff --git a/static/js/directives/chat.js b/static/js/directives/chat.js index b9a595f9..a89e1d33 100644 --- a/static/js/directives/chat.js +++ b/static/js/directives/chat.js @@ -291,21 +291,10 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro }); } _.delay(function() { - mediaStream.api.apply("sendChat", { - send: function(type, data, origType, origData) { - // We also send to self, to display our own stuff. - if (!noloop) { - var encrypted = (type !== origType); - mediaStream.api.received({ - Type: origData.Type, - Data: origData, - From: mediaStream.api.id, - To: peercall.id - }, encrypted); - } - return peercall.peerconnection.send(data); - } - })(to, message, status, mid); + mediaStream.api.sendChat(function(type, data) { + data.Type = type; + return peercall.peerconnection.send(data); + }, to, message, status, mid, true); }, 100); return mid; }; @@ -316,17 +305,8 @@ define(['jquery', 'underscore', 'text!partials/chat.html', 'text!partials/chatro }); } _.delay(function() { - mediaStream.api.send2("sendChat", function(type, data, encrypted) { - if (!noloop) { - //console.log("looped to self", type, data); - mediaStream.api.received({ - Type: data.Type, - Data: data, - From: mediaStream.api.id, - To: to - }, encrypted); - } - })(to, message, status, mid); + // Tell API to loop back message internally. + mediaStream.api.sendChat(to, message, status, mid, true); }, 100); return mid; }; diff --git a/static/js/directives/presentation.js b/static/js/directives/presentation.js index bc01604d..44253af4 100644 --- a/static/js/directives/presentation.js +++ b/static/js/directives/presentation.js @@ -415,17 +415,16 @@ define(['jquery', 'underscore', 'text!partials/presentation.html', 'bigscreen'], var tokenHandler = null; var mediaStreamSendPresentation = function(peercall, token, params) { - mediaStream.api.apply("sendPresentation", { - send: function(type, data) { - if (!peercall.peerconnection.datachannelReady) { - return peercall.e.one("dataReady", function() { - peercall.peerconnection.send(data); - }); - } else { - return peercall.peerconnection.send(data); - } + mediaStream.api.sendPresentation(function(type, data) { + data.Type = type; + if (!peercall.peerconnection.datachannelReady) { + return peercall.e.one("dataReady", function() { + peercall.peerconnection.send(data); + }); + } else { + return peercall.peerconnection.send(data); } - })(peercall.id, token, params); + }, peercall.id, token, params); }; var connector = function(token, peercall) { diff --git a/static/js/directives/screenshare.js b/static/js/directives/screenshare.js index 58e73dd7..3e3ee79f 100644 --- a/static/js/directives/screenshare.js +++ b/static/js/directives/screenshare.js @@ -205,12 +205,11 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials delete peers[currentcall.id]; console.log("Removed closed call from screen sharing.", currentcall.id); }); - mediaStream.api.apply("sendScreenshare", { - send: function(type, data) { - //console.log("sent screenshare", data, peercall); - return peercall.peerconnection.send(data); - } - })(peercall.from, token); + mediaStream.api.sendScreenshare(function(type, data) { + data.Type = type; + //console.log("sent screenshare", data, peercall); + return peercall.peerconnection.send(data); + }, peercall.from, token); }; usermedia.e.one("mediasuccess", function(event, usermedia) { diff --git a/static/js/directives/youtubevideo.js b/static/js/directives/youtubevideo.js index 90d79f03..2c5f4471 100644 --- a/static/js/directives/youtubevideo.js +++ b/static/js/directives/youtubevideo.js @@ -482,17 +482,16 @@ define(['require', 'jquery', 'underscore', 'moment', 'text!partials/youtubevideo var tokenHandler = null; var mediaStreamSendYouTubeVideo = function(peercall, token, params) { - mediaStream.api.apply("sendYouTubeVideo", { - send: function(type, data) { - if (!peercall.peerconnection.datachannelReady) { - return peercall.e.one("dataReady", function() { - peercall.peerconnection.send(data); - }); - } else { - return peercall.peerconnection.send(data); - } + mediaStream.api.sendYouTubeVideo(function(type, data) { + data.Type = type; + if (!peercall.peerconnection.datachannelReady) { + return peercall.e.one("dataReady", function() { + peercall.peerconnection.send(data); + }); + } else { + return peercall.peerconnection.send(data); } - })(peercall.id, token, params); + }, peercall.id, token, params); }; var connector = function(token, peercall) { diff --git a/static/js/mediastream/api.js b/static/js/mediastream/api.js index b8c095bb..a3dcc367 100644 --- a/static/js/mediastream/api.js +++ b/static/js/mediastream/api.js @@ -22,6 +22,21 @@ "use strict"; define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { + // "Decorate" the passed method to make sure the first argument is a + // function to use for sending data. Use the default send function if + // no function is passed. + var addDefaultSendFunc = function(f) { + var func = function(sendFunc) { + if (!_.isFunction(sendFunc)) { + var args = Array.prototype.slice.call(arguments); + args.unshift(_.bind(this.defaultSendFunc, this)); + return func.apply(this, args); + } + return f.apply(this, arguments); + }; + return func; + }; + var Api = function(version, connector, endToEndEncryption) { this.e = $({}); this.version = version; @@ -81,48 +96,64 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { }; - Api.prototype.send = function(type, data, noqueue) { - + Api.prototype.defaultSendFunc = function(type, data, noqueue) { var payload = { Type: type }; payload[type] = data; //console.log("<<<<<<<<<<<<", JSON.stringify(payload, null, 2)); this.connector.send(payload, noqueue); - }; - Api.prototype.send2 = function(name, cb) { - var obj = { - send: _.bind(function(type, data) { - if (cb) { - cb(type, data); - } - this.send(type, data); - }, this), - sendEncrypted: _.bind(function(type, data) { - if (cb) { - var to = data.To; - var encrypted = (to && this.endToEndEncryption); - cb(type, data, encrypted); - } - this.sendEncrypted(type, data); - }, this) + Api.prototype.supportsEncryption = function(peer, type) { + // Broadcast or server messages are never encrypted. + if (!peer) { + return false; + } + + // Need encryption support in the current browser environment. + if (!this.endToEndEncryption) { + return false; + } + + // Messages to setup encryption are never encrypted. + if (type === "EncryptionRegister" || + type === "EncryptionKeyBundle" || + type === "EncryptionRequestKeyBundle") { + return false; } - return this.apply(name, obj); + + // TODO(fancycode): Check if remote peer supports encryption. + return true; }; - Api.prototype.sendEncrypted = function(type, data, noqueue) { + Api.prototype.loopSelf = function(type, data, encrypted) { + this.received({ + Type: data.Type, + Data: data, + From: this.id, + To: data.To + }, encrypted); + }; + + Api.prototype.send = addDefaultSendFunc(function(sendFunc, type, data, noqueue, loopSelf) { var to = data.To; - if (!to || !this.endToEndEncryption) { - return this.send(type, data, noqueue); + if (this.supportsEncryption(to, type)) { + this.endToEndEncryption.encrypt(to, type, data, _.bind(function(encryptedType, encrypted) { + if (loopSelf) { + this.loopSelf(type, data, true); + } + encrypted.To = to; + sendFunc(encryptedType, encrypted, noqueue); + }, this)); + return; } - this.endToEndEncryption.encrypt(to, type, data, _.bind(function(type, encrypted) { - encrypted.To = to - this.send(type, encrypted, noqueue); - }, this)); - }; + if (loopSelf) { + this.loopSelf(type, data, false); + } + sendFunc(type, data, noqueue); + }); Api.prototype.request = function(type, data, cb, noqueue) { @@ -139,29 +170,6 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { } - // Helper hack function to send API requests to other destinations. - // Simply provide an alternative send function on the obj Object. The - // alternative send function will get the type and data to send, together - // with the original type and data (which are potentially unencrypted). - Api.prototype.apply = function(name, obj) { - var f = this[name]; - if (!obj.hasOwnProperty("sendEncrypted")) { - obj.sendEncrypted = _.bind(function(type, data) { - var to = data.To; - if (!to || !this.endToEndEncryption) { - return obj.send(type, data, type, data); - } - - this.endToEndEncryption.encrypt(to, type, data, _.bind(function(encryptedType, encrypted) { - encrypted.To = to - encrypted.Type = encryptedType - obj.send(encryptedType, encrypted, type, data); - }, this)); - }, this); - } - return _.bind(f, obj); - }; - Api.prototype.received = function(d, encrypted) { // Store received timestamp. @@ -331,7 +339,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { Offer: payload } - return this.sendEncrypted("Offer", data); + return this.send("Offer", data); }; @@ -343,7 +351,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { Candidate: payload } - return this.sendEncrypted("Candidate", data); + return this.send("Candidate", data); } @@ -355,7 +363,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { Answer: payload } - return this.sendEncrypted("Answer", data); + return this.send("Answer", data); } @@ -420,11 +428,15 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { } } - return this.sendEncrypted("Bye", data); + return this.send("Bye", data); }; - Api.prototype.sendChat = function(to, message, status, mid) { + Api.prototype.sendChat = addDefaultSendFunc(function(sendFunc, to, message, status, mid, loopSelf) { + if (!loopSelf && this.supportsEncryption(to, "Chat")) { + // We can't let the server loop back the encrypted message. + loopSelf = true; + } var data = { To: to, @@ -433,13 +445,12 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { Mid: mid, Message: message, Status: status, - NoEcho: true // This client shows own messages internally. + NoEcho: !!loopSelf } } - return this.sendEncrypted("Chat", data); - - }; + return this.send(sendFunc, "Chat", data, false, loopSelf); + }); Api.prototype.sendConference = function(id, ids) { @@ -453,8 +464,7 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { }; - Api.prototype.sendScreenshare = function(id, screen_id) { - + Api.prototype.sendScreenshare = addDefaultSendFunc(function(sendFunc, id, screen_id) { var data = { Id: id, Type: "Screenshare", @@ -463,12 +473,11 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { } } - return this.send("Screenshare", data); - - }; + return this.send(sendFunc, "Screenshare", data); - Api.prototype.sendPresentation = function(id, viewer_id, viewer_data) { + }); + Api.prototype.sendPresentation = addDefaultSendFunc(function(sendFunc, id, viewer_id, viewer_data) { var data = { Id: id, Type: "Presentation", @@ -480,12 +489,11 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { data.Presentation = _.extend(data.Presentation, viewer_data); } - return this.send("Presentation", data); - - }; + return this.send(sendFunc, "Presentation", data); - Api.prototype.sendYouTubeVideo = function(id, video_id, video_data) { + }); + Api.prototype.sendYouTubeVideo = addDefaultSendFunc(function(sendFunc, id, video_id, video_data) { var data = { Id: id, Type: "YouTubeVideo", @@ -497,9 +505,9 @@ define(['jquery', 'underscore', 'ua-parser'], function($, _, uaparser) { data.YouTubeVideo = _.extend(data.YouTubeVideo, video_data); } - return this.send("YouTubeVideo", data); + return this.send(sendFunc, "YouTubeVideo", data); - }; + }); Api.prototype.sendAlive = function(timestamp) {