Socket
Socket
Sign inDemoInstall

jingle

Package Overview
Dependencies
26
Maintainers
2
Versions
68
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.4.0 to 1.5.0

38

lib/fileSession.js

@@ -1,4 +0,3 @@

var _ = require('underscore');
var util = require('util');
var bows = require('bows');
var extend = require('extend-object');
var JingleSession = require('./genericSession');

@@ -9,5 +8,2 @@ var RTCPeerConnection = require('rtcpeerconnection');

var log = bows('JingleFile');
function FileSession(opts) {

@@ -39,6 +35,6 @@ JingleSession.call(this, opts);

this.sender.on('progress', function (sent, size) {
log(self.sid + ': Send progress ' + sent + '/' + size);
self._log('debug', 'Send progress ' + sent + '/' + size);
});
this.sender.on('sentFile', function (metadata) {
log(self.sid + ': Sent file ' + metadata.name);
self._log('debug', 'Sent file ' + metadata.name);

@@ -67,3 +63,3 @@ // send hash via description update

this.receiver.on('progress', function (received, size) {
log(self.sid + ': Receive progress ' + received + '/' + size);
self._log('debug', 'Receive progress ' + received + '/' + size);
});

@@ -78,3 +74,3 @@ }

FileSession.prototype = _.extend(FileSession.prototype, {
FileSession.prototype = extend(FileSession.prototype, {
start: function (file) {

@@ -119,3 +115,3 @@ var self = this;

log(this.sid + ': Accepted incoming session');
this._log('debug', 'Accepted incoming session');

@@ -125,3 +121,3 @@ this.state = 'active';

if (err) {
log(self.sid + ': Could not create WebRTC answer', err);
self._log('error', 'Could not create WebRTC answer', err);
return self.end('failed-application');

@@ -140,3 +136,3 @@ }

log(self.sid + ': Initiating incoming session');
this._log('debug', 'Initiating incoming session');

@@ -161,3 +157,3 @@ this.state = 'pending';

if (err) {
log(self.sid + ': Could not create WebRTC answer', err);
self._log('error', 'Could not create WebRTC answer', err);
return cb({condition: 'general-error'});

@@ -171,3 +167,3 @@ }

log(this.sid + ': Activating accepted outbound session');
this._log('debug', 'Activating accepted outbound session');

@@ -180,3 +176,3 @@ this.state = 'active';

if (err) {
log(self.sid + ': Could not process WebRTC answer', err);
self._log('error', 'Could not process WebRTC answer', err);
return cb({condition: 'general-error'});

@@ -190,3 +186,3 @@ }

onSessionTerminate: function (changes, cb) {
log(this.sid + ': Terminating session');
this._log('debug', 'Terminating session');
this.pc.close();

@@ -199,7 +195,7 @@ JingleSession.prototype.end.call(this, changes.reason, true);

log(this.sid + ': Adding ICE candidate');
this._log('debug', 'Adding ICE candidate');
this.pc.processIce(changes, function (err) {
if (err) {
log(self.sid + ': Could not process ICE candidate', err);
self._log('error', 'Could not process ICE candidate', err);
}

@@ -221,3 +217,3 @@ cb();

_onIceCandidate: function (candidateInfo) {
log(this.sid + ': Discovered new ICE candidate', candidateInfo.jingle);
this._log('debug', 'Discovered new ICE candidate', candidateInfo.jingle);
candidateInfo.jingle.contents[0].name = 'data';

@@ -230,7 +226,7 @@ this.send('transport-info', candidateInfo.jingle);

} else if (this.receiver.metadata.hash.value === this.receiver.metadata.actualhash) {
log(this.sid + ': Hash matches');
this._log('debug', 'Hash matches');
this.parent.emit('receivedFile', this, this.receivedFile, this.receiver.metadata);
this.end('success');
} else {
log(this.sid + ': Hash mismatch, terminating');
this._log('error', 'Hash mismatch, terminating');
this.end('media-error');

@@ -237,0 +233,0 @@ }

@@ -1,52 +0,67 @@

var bows = require('bows');
var util = require('util');
var async = require('async');
var extend = require('extend-object');
var WildEmitter = require('wildemitter');
var log = bows('JingleSession');
var ACTIONS = {
'content-accept': 'onContentAccept',
'content-add': 'onContentAdd',
'content-modify': 'onConentModify',
'content-reject': 'onContentReject',
'content-remove': 'onContentRemove',
'description-info': 'onDescriptionInfo',
'security-info': 'onSecurityInfo',
'session-accept': 'onSessionAccept',
'session-info': 'onSessionInfo',
'session-initiate': 'onSessionInitiate',
'session-terminate': 'onSessionTerminate',
'transport-accept': 'onTransportAccept',
'transport-info': 'onTransportInfo',
'transport-reject': 'onTransportReject',
'transport-replace': 'onTransportReplace',
// Unstandardized actions: might go away anytime without notice
'source-add': 'onSourceAdd',
'source-remove': 'onSourceRemove'
};
function actionToMethod(action) {
var words = action.split('-');
return 'on' + words[0][0].toUpperCase() + words[0].substr(1) + words[1][0].toUpperCase() + words[1].substr(1);
}
// actions defined in http://xmpp.org/extensions/xep-0166.html#def-action
var actions = [
'content-accept', 'content-add', 'content-modify',
'content-reject', 'content-remove', 'description-info',
'session-accept', 'session-info', 'session-initiate',
'session-terminate',
'source-add', 'source-remove', // unspecified actions, might go away anytime without notice
'transport-accept', 'transport-info',
'transport-reject', 'transport-replace'
];
function GenericSession(opts) {
WildEmitter.call(this);
var self = this;
function JingleSession(opts) {
var self = this;
this.sid = opts.sid || Date.now().toString();
this.peer = opts.peer;
this.peerID = opts.peerID || this.peer.full || this.peer;
this.isInitiator = opts.initiator || false;
this.parent = opts.parent;
this.state = 'starting';
this.parent = opts.parent;
this.connectionState = 'starting';
// We track the intial pending description types in case
// of the need for a tie-breaker.
this.pendingDescriptionTypes = opts.descriptionTypes || [];
this.pendingAction = false;
// Here is where we'll ensure that all actions are processed
// in order, even if a particular action requires async handling.
this.processingQueue = async.queue(function (task, next) {
var action = task.action;
var action = task.action;
var changes = task.changes;
var cb = task.cb;
log(self.sid + ': ' + action);
self._log('debug', action);
if (actions.indexOf(action) === -1) {
log(this.sid + ': Invalid action ' + action);
if (!ACTIONS[action]) {
self._log('error', 'Invalid action: ' + action);
cb({condition: 'bad-request'});
next();
return;
return next();
}
var method = actionToMethod(action);
self[method](changes, function (err) {
cb(err);
next();
self[ACTIONS[action]](changes, function (err, result) {
cb(err, result);
return next();
});

@@ -56,98 +71,262 @@ });

JingleSession.prototype = Object.create(WildEmitter.prototype, {
constructor: {
value: JingleSession
util.inherits(GenericSession, WildEmitter);
// We don't know how to handle any particular content types,
// so no actions are supported.
Object.keys(ACTIONS).forEach(function (action) {
var method = ACTIONS[action];
GenericSession.prototype[method] = function (changes, cb) {
this._log('error', 'Unsupported action: ' + action);
cb();
};
});
// Provide some convenience properties for checking
// the session's state.
Object.defineProperties(GenericSession.prototype, {
state: {
get: function () {
return this._sessionState;
},
set: function (value) {
if (value !== this._sessionState) {
var prev = this._sessionState;
this._log('info', 'Changing session state to: ' + value);
this._sessionState = value;
this.emit('change:sessionState', this, value);
this.emit('change:' + value, this, true);
if (prev) {
this.emit('change:' + prev, this, false);
}
}
}
},
connectionState: {
get: function () {
return this._connectionState;
},
set: function (value) {
if (value !== this._connectionState) {
var prev = this._connectionState;
this._log('info', 'Changing connection state to: ' + value);
this._connectionState = value;
this.emit('change:connectionState', this, value);
this.emit('change:' + value, this, true);
if (prev) {
this.emit('change:' + prev, this, false);
}
}
}
},
starting: {
get: function () {
return this._sessionState === 'starting';
}
},
pending: {
get: function () {
return this._sessionState === 'pending';
}
},
active: {
get: function () {
return this._sessionState === 'active';
}
},
ended: {
get: function () {
return this._sessionState === 'ended';
}
},
connected: {
get: function () {
return this._connectionState === 'connected';
}
},
connecting: {
get: function () {
return this._connectionState === 'connecting';
}
},
disconnected: {
get: function () {
return this._connectionState === 'disconnected';
}
},
interrupted: {
get: function () {
return this._connectionState === 'interrupted';
}
}
});
GenericSession.prototype = extend(GenericSession.prototype, {
_log: function (level, message) {
message = this.sid + ': ' + message;
this.emit('log:' + level, message);
},
send: function (action, data) {
data = data || {};
data.sid = this.sid;
data.action = action;
JingleSession.prototype.process = function (action, changes, cb) {
this.processingQueue.push({
action: action,
changes: changes,
cb: cb
});
};
var requirePending = {
'session-inititate': true,
'session-accept': true,
'content-add': true,
'content-remove': true,
'content-reject': true,
'content-accept': true,
'content-modify': true,
'transport-replace': true,
'transport-reject': true,
'transport-accept': true,
'source-add': true,
'source-remove': true
};
JingleSession.prototype.send = function (type, data) {
data = data || {};
data.sid = this.sid;
data.action = type;
this.parent.emit('send', {
to: this.peer,
type: 'set',
jingle: data
});
};
if (requirePending[action]) {
this.pendingAction = action;
} else {
this.pendingAction = false;
}
Object.defineProperty(JingleSession.prototype, 'state', {
get: function () {
return this._state;
this.emit('send', {
to: this.peer,
type: 'set',
jingle: data
});
},
set: function (value) {
var validStates = {
starting: true,
pending: true,
active: true,
ended: true
};
process: function (action, changes, cb) {
this.processingQueue.push({
action: action,
changes: changes,
cb: cb
});
},
start: function () {
this._log('error', 'Can not start base sessions');
this.end('unsupported-applications', true);
},
accept: function () {
this._log('error', 'Can not accept base sessions');
this.end('unsupported-applications');
},
cancel: function () {
this.end('cancel');
},
decline: function () {
this.end('decline');
},
end: function (reason, silent) {
this.state = 'ended';
if (!validStates[value]) {
throw new Error('Invalid Jingle Session State: ' + value);
if (!reason) {
reason = 'success';
}
if (this._state !== value) {
this._state = value;
log(this.sid + ': State changed to ' + value);
if (typeof reason === 'string') {
reason = {
condition: reason
};
}
}
});
Object.defineProperty(JingleSession.prototype, 'starting', {
get: function () {
return this._state === 'starting';
}
});
Object.defineProperty(JingleSession.prototype, 'pending', {
get: function () {
return this._state === 'pending';
}
});
Object.defineProperty(JingleSession.prototype, 'active', {
get: function () {
return this._state === 'active';
}
});
Object.defineProperty(JingleSession.prototype, 'ended', {
get: function () {
return this._state === 'ended';
}
});
if (!silent) {
this.send('session-terminate', {
reason: reason
});
}
this.emit('terminated', this, reason);
},
JingleSession.prototype.start = function () {
this.state = 'pending';
log(this.sid + ': Can not start generic session');
};
JingleSession.prototype.end = function (reason, silence) {
this.parent.peers[this.peer].splice(this.parent.peers[this.peer].indexOf(this), 1);
delete this.parent.sessions[this.sid];
// It is mandatory to reply to a session-info action with
// an unsupported-info error if the info isn't recognized.
//
// However, a session-info action with no associated payload
// is acceptable (works like a ping).
onSessionInfo: function (changes, cb) {
var okKeys = {
sid: true,
action: true,
initiator: true,
responder: true
};
this.state = 'ended';
var unknownPayload = false;
Object.keys(changes).forEach(function (key) {
if (!okKeys[key]) {
unknownPayload = true;
}
});
reason = reason || {};
if (unknownPayload) {
cb({
type: 'modify',
condition: 'feature-not-implemented',
jingleCondition: 'unsupported-info'
});
} else {
cb();
}
},
if (!silence) {
this.send('session-terminate', {reason: reason});
}
// It is mandatory to reply to a description-info action with
// an unsupported-info error if the info isn't recognized.
onDescriptionInfo: function (changes, cb) {
cb({
type: 'modify',
condition: 'feature-not-implemented',
jingleCondition: 'unsupported-info'
});
},
this.parent.emit('terminated', this, reason);
};
// It is mandatory to reply to a transport-info action with
// an unsupported-info error if the info isn't recognized.
onTransportInfo: function (changes, cb) {
cb({
type: 'modify',
condition: 'feature-not-implemented',
jingleCondition: 'unsupported-info'
});
},
actions.forEach(function (action) {
var method = actionToMethod(action);
JingleSession.prototype[method] = function (changes, cb) {
log(this.sid + ': Unsupported action ' + action);
// It is mandatory to reply to a content-add action with either
// a content-accept or content-reject.
onContentAdd: function (changes, cb) {
// Allow ack for the content-add to be sent.
cb();
};
this.send('content-reject', {
reason: {
condition: 'failed-application',
text: 'content-add is not supported'
}
});
},
// It is mandatory to reply to a transport-add action with either
// a transport-accept or transport-reject.
onTransportReplace: function (changes, cb) {
// Allow ack for the transport-replace be sent.
cb();
this.send('transport-reject', {
reason: {
condition: 'failed-application',
text: 'transport-replace is not supported'
}
});
}
});
module.exports = JingleSession;
module.exports = GenericSession;

@@ -1,10 +0,7 @@

var _ = require('underscore');
var util = require('util');
var bows = require('bows');
var extend = require('extend-object');
var JingleSession = require('./genericSession');
var RTCPeerConnection = require('rtcpeerconnection');
var log = bows('JingleMedia');
function MediaSession(opts) {

@@ -46,3 +43,3 @@ JingleSession.call(this, opts);

MediaSession.prototype = _.extend(MediaSession.prototype, {
MediaSession.prototype = extend(MediaSession.prototype, {
start: function (constraints) {

@@ -76,3 +73,3 @@ var self = this;

});
});
});
});

@@ -85,3 +82,3 @@ self.send('session-initiate', sessDesc.jingle);

this.pc.close();
_.each(this.streams, function (stream) {
this.streams.forEach(function (stream) {
self._onStreamRemoved({stream: stream});

@@ -94,3 +91,3 @@ });

log(this.sid + ': Accepted incoming session');
this._log('debug', 'Accepted incoming session');

@@ -100,3 +97,3 @@ this.state = 'active';

if (err) {
log(self.sid + ': Could not create WebRTC answer', err);
self._log('error', 'Could not create WebRTC answer', err);
return self.end('failed-application');

@@ -111,3 +108,3 @@ }

});
});
});
});

@@ -118,19 +115,19 @@ self.send('session-accept', answer.jingle);

ring: function () {
log(this.sid + ': Ringing on incoming session');
this._log('debug', 'Ringing on incoming session');
this.send('session-info', {ringing: true});
},
mute: function (creator, name) {
log(this.sid + ': Muting');
this._log('debug', 'Muting');
this.send('session-info', {mute: {creator: creator, name: name}});
},
unmute: function (creator, name) {
log(this.sid + ': Unmuting');
this._log('debug', 'Unmuting');
this.send('session-info', {unmute: {creator: creator, name: name}});
},
hold: function () {
log(this.sid + ': Placing on hold');
this._log('debug', 'Placing on hold');
this.send('session-info', {hold: true});
},
resume: function () {
log(this.sid + ': Resuing from hold');
this._log('debug', 'Resuing from hold');
this.send('session-info', {active: true});

@@ -207,3 +204,3 @@ },

log(self.sid + ': Initiating incoming session');
this._log('debug', 'Initiating incoming session');

@@ -215,3 +212,3 @@ this.state = 'pending';

if (err) {
log(self.sid + ': Could not create WebRTC answer', err);
self._log('error', 'Could not create WebRTC answer', err);
return cb({condition: 'general-error'});

@@ -225,3 +222,3 @@ }

log(this.sid + ': Activating accepted outbound session');
this._log('debug', 'Activating accepted outbound session');

@@ -231,3 +228,3 @@ this.state = 'active';

if (err) {
log(self.sid + ': Could not process WebRTC answer', err);
self._log('error', 'Could not process WebRTC answer', err);
return cb({condition: 'general-error'});

@@ -242,5 +239,5 @@ }

var self = this;
log(this.sid + ': Terminating session');
this._log('debug', 'Terminating session');
this.pc.close();
_.each(this.streams, function (stream) {
this.streams.forEach(function (stream) {
self._onStreamRemoved({stream: stream});

@@ -254,7 +251,7 @@ });

log(this.sid + ': Adding ICE candidate');
this._log('debug', 'Adding ICE candidate');
this.pc.processIce(changes, function (err) {
if (err) {
log(self.sid + ': Could not process ICE candidate', err);
self._log('error', 'Could not process ICE candidate', err);
}

@@ -265,5 +262,5 @@ cb();

onSessionInfo: function (info, cb) {
log(info);
this._log('debug', 'Session info', info);
if (info.ringing) {
log(this.sid + ': Ringing on remote stream');
this._log('debug', 'Ringing on remote stream');
this.parent.emit('ringing', this);

@@ -273,3 +270,3 @@ }

if (info.hold) {
log(this.sid + ': On hold');
this._log('debug', 'On hold');
this.parent.emit('hold', this);

@@ -279,3 +276,3 @@ }

if (info.active) {
log(this.sid + ': Resumed from hold');
this._log('debug', 'Resumed from hold');
this.parent.emit('resumed', this);

@@ -285,3 +282,3 @@ }

if (info.mute) {
log(this.sid + ': Muted', info.mute);
this._log('debug', 'Muted', info.mute);
this.parent.emit('mute', this, info.mute);

@@ -291,3 +288,3 @@ }

if (info.unmute) {
log(this.sid + ': Unmuted', info.unmute);
this._log('debug', 'Unmuted', info.unmute);
this.parent.emit('unmute', this, info.unmute);

@@ -299,3 +296,3 @@ }

onSourceAdd: function (changes, cb) {
// note that this method is highly experimental and may
// note that this method is highly experimental and may
// go away without notice

@@ -323,5 +320,5 @@ var self = this;

}
log(this.sid + ': source-add');
this._log('debug', 'source-add');
var newDesc = this.pc.remoteDescription;

@@ -350,3 +347,3 @@ this.pc.remoteDescription.contents.forEach(function (content, idx) {

// FIXME: this block is pretty reusable, even though sometimes the
// FIXME: this block is pretty reusable, even though sometimes the
// order of setRemoteDescription/setLocalDescription should change

@@ -356,3 +353,3 @@ this.pc.handleOffer({type: 'offer', jingle: newDesc}, function (err) {

// handle error
log(this.sid + ': source-add offer error');
self._log('error', 'source-add offer error');
return cb({condition: 'general-error'});

@@ -363,3 +360,3 @@ }

if (err) {
log(this.sid + ': source-add answer error');
self._log('error', 'source-add answer error');
return cb({condition: 'general-error'});

@@ -372,3 +369,3 @@ }

onSourceRemove: function (changes, cb) {
// note that this method is highly experimental and may
// note that this method is highly experimental and may
// go away without notice

@@ -397,3 +394,3 @@ var self = this;

log(this.sid + ': source-remove');
this._log('debug', 'source-remove');

@@ -461,3 +458,3 @@ var newDesc = this.pc.remoteDescription;

});
// FIXME: this block is pretty reusable, even though sometimes the
// FIXME: this block is pretty reusable, even though sometimes the
// order of setRemoteDescription/setLocalDescription should change

@@ -467,3 +464,3 @@ this.pc.handleOffer({type: 'offer', jingle: newDesc}, function (err) {

// handle error
log(this.sid + ': source-remove offer error');
self._log('error', 'source-remove offer error');
return cb({condition: 'general-error'});

@@ -474,3 +471,3 @@ }

if (err) {
log(this.sid + ': source-remove answer error');
self._log('error', 'source-remove answer error');
return cb({condition: 'general-error'});

@@ -522,7 +519,7 @@ }

_onIceCandidate: function (candidateInfo) {
log(this.sid + ': Discovered new ICE candidate', candidateInfo.jingle);
this._log('debug', 'Discovered new ICE candidate', candidateInfo.jingle);
this.send('transport-info', candidateInfo.jingle);
},
_onStreamAdded: function (event) {
log(this.sid + ': Remote media stream added');
this._log('debug', 'Remote media stream added');

@@ -539,3 +536,3 @@ // unfortunately, firefox does not support this yet

_onStreamRemoved: function (event) {
log(this.sid + ': Remote media stream removed');
this._log('debug', 'Remote media stream removed');
this.parent.emit('peerStreamRemoved', this, event.stream);

@@ -542,0 +539,0 @@ }

@@ -1,6 +0,5 @@

var _ = require('underscore');
var bows = require('bows');
var hark = require('hark');
var util = require('util');
var intersect = require('intersect');
var WildEmitter = require('wildemitter');
var webrtc = require('webrtcsupport');
var WildEmitter = require('wildemitter');

@@ -12,36 +11,24 @@ var GenericSession = require('./genericSession');

var log = bows('Jingle');
function SessionManager(conf) {
WildEmitter.call(this);
conf = conf || {};
function Jingle(opts) {
opts = opts || {};
var config = this.config = {
debug: false,
peerConnectionConfig: {
iceServers: [{'url': 'stun:stun.l.google.com:19302'}]
},
peerConnectionConstraints: {
optional: [
{DtlsSrtpKeyAgreement: true},
{RtpDataChannels: false}
]
},
autoAdjustMic: false,
media: {
audio: true,
video: true
}
};
this.jid = conf.jid;
this.selfID = conf.selfID || (this.jid && this.jid.full) || this.jid || '';
this.MediaSession = MediaSession;
this.jid = opts.jid;
this.sessions = {};
this.peers = {};
this.prepareSession = conf.prepareSession || function (opts) {
if (opts.descriptionTypes.indexOf('rtp') >= 0) {
return new MediaSession(opts);
}
if (opts.descriptionTypes.indexOf('filetransfer') >= 0) {
return new FileSession(opts);
}
};
this.screenSharingSupport = webrtc.screenSharing;
for (var item in opts) {
config[item] = opts[item];
}
this.capabilities = [

@@ -68,63 +55,163 @@ 'urn:xmpp:jingle:1'

];
} else {
log('WebRTC not supported');
}
WildEmitter.call(this);
this.config = {
debug: false,
peerConnectionConfig: {
iceServers: conf.iceServers || [{'url': 'stun:stun.l.google.com:19302'}]
},
peerConnectionConstraints: {
optional: [
{DtlsSrtpKeyAgreement: true},
{RtpDataChannesl: false}
]
},
media: {
audio: true,
video: true
}
};
if (this.config.debug) {
this.on('*', function (event, val1, val2) {
log(event, val1, val2);
});
for (var item in conf) {
this.config[item] = conf[item];
}
this.iceServers = this.config.peerConnectionConfig.iceServers;
}
Jingle.prototype = Object.create(WildEmitter.prototype, {
constructor: {
value: Jingle
util.inherits(SessionManager, WildEmitter);
SessionManager.prototype.addICEServer = function (server) {
// server == {
// url: '',
// [username: '',]
// [credential: '']
// }
if (typeof server === 'string') {
server = {url: server};
}
});
Jingle.prototype.addICEServer = function (server) {
this.config.peerConnectionConfig.iceServers.push(server);
this.iceServers.push(server);
};
Jingle.prototype.setupAudioMonitor = function (stream) {
log('Setup audio');
var audio = hark(stream);
SessionManager.prototype.addSession = function (session) {
var self = this;
var timeout;
audio.on('speaking', function () {
if (self.hardMuted) {
return;
var sid = session.sid;
var peer = session.peerID;
this.sessions[sid] = session;
if (!this.peers[peer]) {
this.peers[peer] = [];
}
this.peers[peer].push(session);
// Automatically clean up tracked sessions
session.on('terminated', function () {
var peers = self.peers[peer] || [];
if (peers.length) {
peers.splice(peers.indexOf(session), 1);
}
self.setMicIfEnabled(1);
self.emit('speaking');
delete self.sessions[sid];
});
audio.on('stopped_speaking', function () {
if (self.hardMuted) {
return;
// Proxy session events
session.on('*', function (name, data, extraData, extraData2) {
// Listen for when we actually try to start a session to
// trigger the outgoing event.
if (name === 'send') {
var action = data.jingle && data.jingle.action;
if (session.isInitiator && action === 'session-initiate') {
self.emit('outgoing', session);
}
}
if (timeout) {
clearTimeout(timeout);
if (self.config.debug && (name === 'log:debug' || name === 'log:error')) {
console.log('Jingle:', data, extraData, extraData2);
}
timeout = setTimeout(function () {
self.setMicIfEnabled(0.5);
self.emit('stoppedSpeaking');
}, 1000);
// Don't proxy change:* events, since those don't apply to
// the session manager itself.
if (name.indexOf('change') === 0) {
return;
}
self.emit(name, data, extraData, extraData2);
});
this.emit('createdSession', session);
return session;
};
Jingle.prototype.setMicIfEnabled = function (volume) {
if (!this.config.autoAdjustMic) {
return;
SessionManager.prototype.createMediaSession = function (peer, sid, stream) {
var session = new MediaSession({
sid: sid,
peer: peer,
initiator: true,
stream: stream,
parent: this
});
this.addSession(session);
return session;
};
SessionManager.prototype.createFileTransferSession = function (peer, sid) {
var session = new FileSession({
sid: sid,
peer: peer,
initiator: true,
parent: this
});
this.addSession(session);
return session;
};
SessionManager.prototype.endPeerSessions = function (peer, reason, silent) {
peer = peer.full || peer;
var sessions = this.peers[peer] || [];
delete this.peers[peer];
sessions.forEach(function (session) {
session.end(reason || 'gone', silent);
});
};
SessionManager.prototype.endAllSessions = function (reason, silent) {
var self = this;
Object.keys(this.peers).forEach(function (peer) {
self.endPeerSessions(peer, reason, silent);
});
};
SessionManager.prototype._createIncomingSession = function (meta, req) {
var session;
if (this.prepareSession) {
session = this.prepareSession(meta, req);
}
this.gainController.setGain(volume);
// Fallback to a generic session type, which can
// only be used to end the session.
if (!session) {
session = new GenericSession(meta);
}
this.addSession(session);
return session;
};
Jingle.prototype.sendError = function (to, id, data) {
data.type = 'cancel';
SessionManager.prototype._sendError = function (to, id, data) {
if (!data.type) {
data.type = 'cancel';
}
this.emit('send', {

@@ -138,25 +225,52 @@ to: to,

Jingle.prototype.process = function (req) {
SessionManager.prototype._log = function (level, message) {
this.emit('log:' + level, message);
};
SessionManager.prototype.process = function (req) {
var self = this;
// Extract the request metadata that we need to verify
var sid = !!req.jingle ? req.jingle.sid : null;
var session = this.sessions[sid] || null;
var rid = req.id;
var sender = req.from.full || req.from;
if (req.type === 'error') {
return this.emit('error', req);
var isTieBreak = req.error && req.error.jingleCondition === 'tie-break';
if (session && session.pending && isTieBreak) {
return session.end('alternative-session', true);
} else {
if (session) {
session.pendingAction = false;
}
return this.emit('error', req);
}
}
if (req.type === 'result') {
if (session) {
session.pendingAction = false;
}
return;
}
var sids, currsid, sess;
var sid = req.jingle.sid;
var action = req.jingle.action;
var contents = req.jingle.contents || [];
var contentTypes = _.map(contents, function (content) {
return (content.description || {}).descType;
var descriptionTypes = contents.map(function (content) {
if (content.description) {
return content.description.descType;
}
});
var transportTypes = contents.map(function (content) {
if (content.transport) {
return content.transport.transType;
}
});
var session = this.sessions[sid] || null;
var sender = req.from.full || req.from;
var reqid = req.id;
// Now verify that we are allowed to actually process the
// requested action

@@ -166,4 +280,4 @@ if (action !== 'session-initiate') {

if (!session) {
log('Unknown session', sid);
return this.sendError(sender, reqid, {
this._log('error', 'Unknown session', sid);
return this._sendError(sender, rid, {
condition: 'item-not-found',

@@ -175,5 +289,5 @@ jingleCondition: 'unknown-session'

// Check if someone is trying to hijack a session.
if (session.peer !== sender || session.ended) {
log('Session has ended, or action has wrong sender');
return this.sendError(sender, reqid, {
if (session.peerID !== sender || session.ended) {
this._log('error', 'Session has ended, or action has wrong sender');
return this._sendError(sender, rid, {
condition: 'item-not-found',

@@ -186,4 +300,4 @@ jingleCondition: 'unknown-session'

if (action === 'session-accept' && !session.pending) {
log('Tried to accept session twice', sid);
return this.sendError(sender, reqid, {
this._log('error', 'Tried to accept session twice', sid);
return this._sendError(sender, rid, {
condition: 'unexpected-request',

@@ -195,6 +309,6 @@ jingleCondition: 'out-of-order'

// Can't process two requests at once, need to tie break
if (action !== 'session-terminate' && session.pendingAction) {
log('Tie break during pending request');
if (action !== 'session-terminate' && action === session.pendingAction) {
this._log('error', 'Tie break during pending request');
if (session.isInitiator) {
return this.sendError(sender, reqid, {
return this._sendError(sender, rid, {
condition: 'conflict',

@@ -207,5 +321,5 @@ jingleCondition: 'tie-break'

// Don't accept a new session if we already have one.
if (session.peer !== sender) {
log('Duplicate sid from new sender');
return this.sendError(sender, reqid, {
if (session.peerID !== sender) {
this._log('error', 'Duplicate sid from new sender');
return this._sendError(sender, rid, {
condition: 'service-unavailable'

@@ -218,5 +332,5 @@ });

if (session.pending) {
if (this.jid > session.peer) {
log('Tie break new session because of duplicate sids');
return this.sendError(sender, reqid, {
if (this.selfID > session.peerID) {
this._log('error', 'Tie break new session because of duplicate sids');
return this._sendError(sender, rid, {
condition: 'conflict',

@@ -226,24 +340,23 @@ jingleCondition: 'tie-break'

}
} else {
// The other side is just doing it wrong.
this._log('error', 'Someone is doing this wrong');
return this._sendError(sender, rid, {
condition: 'unexpected-request',
jingleCondition: 'out-of-order'
});
}
// The other side is just doing it wrong.
log('Someone is doing this wrong');
return this.sendError(sender, reqid, {
condition: 'unexpected-request',
jingleCondition: 'out-of-order'
});
} else if (Object.keys(this.peers[sender] || {}).length) {
} else if (this.peers[sender] && this.peers[sender].length) {
// Check if we need to have a tie breaker because we already have
// a different session that is using the requested content types.
sids = Object.keys(this.peers[sender]);
for (var i = 0; i < sids.length; i++) {
currsid = sids[i];
sess = this.sessions[currsid];
// a different session with this peer that is using the requested
// content description types.
for (var i = 0, len = this.peers[sender].length; i < len; i++) {
var sess = this.peers[sender][i];
if (sess && sess.pending) {
if (_.intersection(contentTypes, sess.contentTypes).length) {
if (intersect(descriptionTypes, sess.pendingDescriptionTypes).length) {
// We already have a pending session request for this content type.
if (currsid > sid) {
if (sess.sid > sid) {
// We won the tie breaker
log('Tie break');
return this.sendError(sender, reqid, {
this._log('info', 'Tie break');
return this._sendError(sender, rid, {
condition: 'conflict',

@@ -258,23 +371,20 @@ jingleCondition: 'tie-break'

// We've now weeded out invalid requests, so we can process the action now.
if (action === 'session-initiate') {
var opts = {
if (!contents.length) {
return self._sendError(sender, rid, {
condition: 'bad-request'
});
}
session = this._createIncomingSession({
sid: sid,
peer: sender,
peer: req.from,
peerID: sender,
initiator: false,
parent: this
};
if (contentTypes.indexOf('rtp') >= 0) {
session = new MediaSession(opts);
} else if (contentTypes.indexOf('filetransfer') >= 0) {
session = new FileSession(opts);
} else {
session = new GenericSession(opts);
}
this.sessions[sid] = session;
if (!this.peers[sender]) {
this.peers[sender] = [];
}
this.peers[sender].push(session);
this.emit('createdSession', session);
parent: this,
descriptionTypes: descriptionTypes,
transportTypes: transportTypes
}, req);
}

@@ -284,11 +394,14 @@

if (err) {
log('Could not process request', req, err);
self.sendError(sender, reqid, err);
self._log('error', 'Could not process request', req, err);
self._sendError(sender, rid, err);
} else {
self.emit(
'send',
{ to: sender, id: reqid, type: 'result', action: action }
);
self.emit('send', {
to: sender,
id: rid,
type: 'result',
});
// Wait for the initial action to be processed before emitting
// the session for the user to accept/reject.
if (action === 'session-initiate') {
log('Incoming session request from ', sender, session);
self.emit('incoming', session);

@@ -300,65 +413,3 @@ }

Jingle.prototype.createMediaSession = function (peer, sid, stream) {
var session = new MediaSession({
sid: sid,
peer: peer,
initiator: true,
stream: stream,
parent: this
});
sid = session.sid;
this.sessions[sid] = session;
if (!this.peers[peer]) {
this.peers[peer] = [];
}
this.peers[peer].push(session);
this.emit('createdSession', session);
log('Outgoing session', session.sid, session);
this.emit('outgoing', session);
return session;
};
Jingle.prototype.createFileTransferSession = function (peer, sid) {
var session = new FileSession({
sid: sid,
peer: peer,
initiator: true,
parent: this
});
sid = session.sid;
this.sessions[sid] = session;
if (!this.peers[peer]) {
this.peers[peer] = [];
}
this.peers[peer].push(session);
this.emit('createdSession', session);
log('Outgoing session', session.sid, session);
this.emit('outgoing', session);
return session;
};
Jingle.prototype.endPeerSessions = function (peer, silence) {
log('Ending all sessions with', peer);
var sessions = this.peers[peer] || [];
sessions.forEach(function (session) {
session.end('gone', silence);
});
delete this.peers[peer];
};
Jingle.prototype.endAllPeerSessions = function () {
log('Ending all peer sessions');
var self = this;
Object.keys(this.peers).forEach(function (peer) {
self.endPeerSessions(peer);
});
};
module.exports = Jingle;
module.exports = SessionManager;
{
"name": "jingle",
"description": "Generic Jingle via WebRTC session manager.",
"version": "1.4.0",
"version": "1.5.0",
"author": "Lance Stout <lance@andyet.net>",

@@ -13,9 +13,6 @@ "bugs": "https://github.com/otalk/jingle.js/issues",

"async": "^0.9.0",
"bows": "^0.3.0",
"extend-object": "^1.0.0",
"filetransfer": "^1.0.0",
"hark": "^1.1.1",
"mockconsole": "0.0.1",
"rtcpeerconnection": "^2.6.6",
"sdp-jingle-json": "^1.3.0",
"underscore": "^1.6.0",
"webrtcsupport": "^1.1.0",

@@ -33,3 +30,2 @@ "wildemitter": "^1.0.1"

"precommit-hook": "^1.0.0",
"tape": "2.x",
"uglify-js": "2.x"

@@ -36,0 +32,0 @@ },

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc