diff --git a/changelog.d/12909.removal b/changelog.d/12909.removal
new file mode 100644
index 0000000000..0baff46ea9
--- /dev/null
+++ b/changelog.d/12909.removal
@@ -0,0 +1 @@
+Remove `contrib/jitsimeetbridge`. This was an unused experiment that hasn't been meaningfully changed since 2014.
diff --git a/contrib/jitsimeetbridge/jitsimeetbridge.py b/contrib/jitsimeetbridge/jitsimeetbridge.py
deleted file mode 100644
index b3de468687..0000000000
--- a/contrib/jitsimeetbridge/jitsimeetbridge.py
+++ /dev/null
@@ -1,298 +0,0 @@
-#!/usr/bin/env python
-
-"""
-This is an attempt at bridging matrix clients into a Jitis meet room via Matrix
-video call. It uses hard-coded xml strings overg XMPP BOSH. It can display one
-of the streams from the Jitsi bridge until the second lot of SDP comes down and
-we set the remote SDP at which point the stream ends. Our video never gets to
-the bridge.
-
-Requires:
-npm install jquery jsdom
-"""
-import json
-import subprocess
-import time
-
-import gevent
-import grequests
-from BeautifulSoup import BeautifulSoup
-
-ACCESS_TOKEN = ""
-
-MATRIXBASE = "https://matrix.org/_matrix/client/api/v1/"
-MYUSERNAME = "@davetest:matrix.org"
-
-HTTPBIND = "https://meet.jit.si/http-bind"
-# HTTPBIND = 'https://jitsi.vuc.me/http-bind'
-# ROOMNAME = "matrix"
-ROOMNAME = "pibble"
-
-HOST = "guest.jit.si"
-# HOST="jitsi.vuc.me"
-
-TURNSERVER = "turn.guest.jit.si"
-# TURNSERVER="turn.jitsi.vuc.me"
-
-ROOMDOMAIN = "meet.jit.si"
-# ROOMDOMAIN="conference.jitsi.vuc.me"
-
-
-class TrivialMatrixClient:
- def __init__(self, access_token):
- self.token = None
- self.access_token = access_token
-
- def getEvent(self):
- while True:
- url = (
- MATRIXBASE
- + "events?access_token="
- + self.access_token
- + "&timeout=60000"
- )
- if self.token:
- url += "&from=" + self.token
- req = grequests.get(url)
- resps = grequests.map([req])
- obj = json.loads(resps[0].content)
- print("incoming from matrix", obj)
- if "end" not in obj:
- continue
- self.token = obj["end"]
- if len(obj["chunk"]):
- return obj["chunk"][0]
-
- def joinRoom(self, roomId):
- url = MATRIXBASE + "rooms/" + roomId + "/join?access_token=" + self.access_token
- print(url)
- headers = {"Content-Type": "application/json"}
- req = grequests.post(url, headers=headers, data="{}")
- resps = grequests.map([req])
- obj = json.loads(resps[0].content)
- print("response: ", obj)
-
- def sendEvent(self, roomId, evType, event):
- url = (
- MATRIXBASE
- + "rooms/"
- + roomId
- + "/send/"
- + evType
- + "?access_token="
- + self.access_token
- )
- print(url)
- print(json.dumps(event))
- headers = {"Content-Type": "application/json"}
- req = grequests.post(url, headers=headers, data=json.dumps(event))
- resps = grequests.map([req])
- obj = json.loads(resps[0].content)
- print("response: ", obj)
-
-
-xmppClients = {}
-
-
-def matrixLoop():
- while True:
- ev = matrixCli.getEvent()
- print(ev)
- if ev["type"] == "m.room.member":
- print("membership event")
- if ev["membership"] == "invite" and ev["state_key"] == MYUSERNAME:
- roomId = ev["room_id"]
- print("joining room %s" % (roomId))
- matrixCli.joinRoom(roomId)
- elif ev["type"] == "m.room.message":
- if ev["room_id"] in xmppClients:
- print("already have a bridge for that user, ignoring")
- continue
- print("got message, connecting")
- xmppClients[ev["room_id"]] = TrivialXmppClient(ev["room_id"], ev["user_id"])
- gevent.spawn(xmppClients[ev["room_id"]].xmppLoop)
- elif ev["type"] == "m.call.invite":
- print("Incoming call")
- # sdp = ev['content']['offer']['sdp']
- # print "sdp: %s" % (sdp)
- # xmppClients[ev['room_id']] = TrivialXmppClient(ev['room_id'], ev['user_id'])
- # gevent.spawn(xmppClients[ev['room_id']].xmppLoop)
- elif ev["type"] == "m.call.answer":
- print("Call answered")
- sdp = ev["content"]["answer"]["sdp"]
- if ev["room_id"] not in xmppClients:
- print("We didn't have a call for that room")
- continue
- # should probably check call ID too
- xmppCli = xmppClients[ev["room_id"]]
- xmppCli.sendAnswer(sdp)
- elif ev["type"] == "m.call.hangup":
- if ev["room_id"] in xmppClients:
- xmppClients[ev["room_id"]].stop()
- del xmppClients[ev["room_id"]]
-
-
-class TrivialXmppClient:
- def __init__(self, matrixRoom, userId):
- self.rid = 0
- self.matrixRoom = matrixRoom
- self.userId = userId
- self.running = True
-
- def stop(self):
- self.running = False
-
- def nextRid(self):
- self.rid += 1
- return "%d" % (self.rid)
-
- def sendIq(self, xml):
- fullXml = (
- "<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' sid='%s'>%s</body>"
- % (self.nextRid(), self.sid, xml)
- )
- # print "\t>>>%s" % (fullXml)
- return self.xmppPoke(fullXml)
-
- def xmppPoke(self, xml):
- headers = {"Content-Type": "application/xml"}
- req = grequests.post(HTTPBIND, verify=False, headers=headers, data=xml)
- resps = grequests.map([req])
- obj = BeautifulSoup(resps[0].content)
- return obj
-
- def sendAnswer(self, answer):
- print("sdp from matrix client", answer)
- p = subprocess.Popen(
- ["node", "unjingle/unjingle.js", "--sdp"],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- )
- jingle, out_err = p.communicate(answer)
- jingle = jingle % {
- "tojid": self.callfrom,
- "action": "session-accept",
- "initiator": self.callfrom,
- "responder": self.jid,
- "sid": self.callsid,
- }
- print("answer jingle from sdp", jingle)
- res = self.sendIq(jingle)
- print("reply from answer: ", res)
-
- self.ssrcs = {}
- jingleSoup = BeautifulSoup(jingle)
- for cont in jingleSoup.iq.jingle.findAll("content"):
- if cont.description:
- self.ssrcs[cont["name"]] = cont.description["ssrc"]
- print("my ssrcs:", self.ssrcs)
-
- gevent.joinall([gevent.spawn(self.advertiseSsrcs)])
-
- def advertiseSsrcs(self):
- time.sleep(7)
- print("SSRC spammer started")
- while self.running:
- ssrcMsg = (
- "<presence to='%(tojid)s' xmlns='jabber:client'><x xmlns='http://jabber.org/protocol/muc'/><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://jitsi.org/jitsimeet' ver='0WkSdhFnAUxrz4ImQQLdB80GFlE='/><nick xmlns='http://jabber.org/protocol/nick'>%(nick)s</nick><stats xmlns='http://jitsi.org/jitmeet/stats'><stat name='bitrate_download' value='175'/><stat name='bitrate_upload' value='176'/><stat name='packetLoss_total' value='0'/><stat name='packetLoss_download' value='0'/><stat name='packetLoss_upload' value='0'/></stats><media xmlns='http://estos.de/ns/mjs'><source type='audio' ssrc='%(assrc)s' direction='sendre'/><source type='video' ssrc='%(vssrc)s' direction='sendre'/></media></presence>"
- % {
- "tojid": "%s@%s/%s" % (ROOMNAME, ROOMDOMAIN, self.shortJid),
- "nick": self.userId,
- "assrc": self.ssrcs["audio"],
- "vssrc": self.ssrcs["video"],
- }
- )
- res = self.sendIq(ssrcMsg)
- print("reply from ssrc announce: ", res)
- time.sleep(10)
-
- def xmppLoop(self):
- self.matrixCallId = time.time()
- res = self.xmppPoke(
- "<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' to='%s' xml:lang='en' wait='60' hold='1' content='text/xml; charset=utf-8' ver='1.6' xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh'/>"
- % (self.nextRid(), HOST)
- )
-
- print(res)
- self.sid = res.body["sid"]
- print("sid %s" % (self.sid))
-
- res = self.sendIq(
- "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>"
- )
-
- res = self.xmppPoke(
- "<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' sid='%s' to='%s' xml:lang='en' xmpp:restart='true' xmlns:xmpp='urn:xmpp:xbosh'/>"
- % (self.nextRid(), self.sid, HOST)
- )
-
- res = self.sendIq(
- "<iq type='set' id='_bind_auth_2' xmlns='jabber:client'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>"
- )
- print(res)
-
- self.jid = res.body.iq.bind.jid.string
- print("jid: %s" % (self.jid))
- self.shortJid = self.jid.split("-")[0]
-
- res = self.sendIq(
- "<iq type='set' id='_session_auth_2' xmlns='jabber:client'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>"
- )
-
- # randomthing = res.body.iq['to']
- # whatsitpart = randomthing.split('-')[0]
-
- # print "other random bind thing: %s" % (randomthing)
-
- # advertise preence to the jitsi room, with our nick
- res = self.sendIq(
- "<iq type='get' to='%s' xmlns='jabber:client' id='1:sendIQ'><services xmlns='urn:xmpp:extdisco:1'><service host='%s'/></services></iq><presence to='%s@%s/d98f6c40' xmlns='jabber:client'><x xmlns='http://jabber.org/protocol/muc'/><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://jitsi.org/jitsimeet' ver='0WkSdhFnAUxrz4ImQQLdB80GFlE='/><nick xmlns='http://jabber.org/protocol/nick'>%s</nick></presence>"
- % (HOST, TURNSERVER, ROOMNAME, ROOMDOMAIN, self.userId)
- )
- self.muc = {"users": []}
- for p in res.body.findAll("presence"):
- u = {}
- u["shortJid"] = p["from"].split("/")[1]
- if p.c and p.c.nick:
- u["nick"] = p.c.nick.string
- self.muc["users"].append(u)
- print("muc: ", self.muc)
-
- # wait for stuff
- while True:
- print("waiting...")
- res = self.sendIq("")
- print("got from stream: ", res)
- if res.body.iq:
- jingles = res.body.iq.findAll("jingle")
- if len(jingles):
- self.callfrom = res.body.iq["from"]
- self.handleInvite(jingles[0])
- elif "type" in res.body and res.body["type"] == "terminate":
- self.running = False
- del xmppClients[self.matrixRoom]
- return
-
- def handleInvite(self, jingle):
- self.initiator = jingle["initiator"]
- self.callsid = jingle["sid"]
- p = subprocess.Popen(
- ["node", "unjingle/unjingle.js", "--jingle"],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- )
- print("raw jingle invite", str(jingle))
- sdp, out_err = p.communicate(str(jingle))
- print("transformed remote offer sdp", sdp)
- inviteEvent = {
- "offer": {"type": "offer", "sdp": sdp},
- "call_id": self.matrixCallId,
- "version": 0,
- "lifetime": 30000,
- }
- matrixCli.sendEvent(self.matrixRoom, "m.call.invite", inviteEvent)
-
-
-matrixCli = TrivialMatrixClient(ACCESS_TOKEN) # Undefined name
-
-gevent.joinall([gevent.spawn(matrixLoop)])
diff --git a/contrib/jitsimeetbridge/syweb-jitsi-conference.patch b/contrib/jitsimeetbridge/syweb-jitsi-conference.patch
deleted file mode 100644
index aed23c78aa..0000000000
--- a/contrib/jitsimeetbridge/syweb-jitsi-conference.patch
+++ /dev/null
@@ -1,188 +0,0 @@
-diff --git a/syweb/webclient/app/components/matrix/matrix-call.js b/syweb/webclient/app/components/matrix/matrix-call.js
-index 9fbfff0..dc68077 100644
---- a/syweb/webclient/app/components/matrix/matrix-call.js
-+++ b/syweb/webclient/app/components/matrix/matrix-call.js
-@@ -16,6 +16,45 @@ limitations under the License.
-
- 'use strict';
-
-+
-+function sendKeyframe(pc) {
-+ console.log('sendkeyframe', pc.iceConnectionState);
-+ if (pc.iceConnectionState !== 'connected') return; // safe...
-+ pc.setRemoteDescription(
-+ pc.remoteDescription,
-+ function () {
-+ pc.createAnswer(
-+ function (modifiedAnswer) {
-+ pc.setLocalDescription(
-+ modifiedAnswer,
-+ function () {
-+ // noop
-+ },
-+ function (error) {
-+ console.log('triggerKeyframe setLocalDescription failed', error);
-+ messageHandler.showError();
-+ }
-+ );
-+ },
-+ function (error) {
-+ console.log('triggerKeyframe createAnswer failed', error);
-+ messageHandler.showError();
-+ }
-+ );
-+ },
-+ function (error) {
-+ console.log('triggerKeyframe setRemoteDescription failed', error);
-+ messageHandler.showError();
-+ }
-+ );
-+}
-+
-+
-+
-+
-+
-+
-+
- var forAllVideoTracksOnStream = function(s, f) {
- var tracks = s.getVideoTracks();
- for (var i = 0; i < tracks.length; i++) {
-@@ -83,7 +122,7 @@ angular.module('MatrixCall', [])
- }
-
- // FIXME: we should prevent any calls from being placed or accepted before this has finished
-- MatrixCall.getTurnServer();
-+ //MatrixCall.getTurnServer();
-
- MatrixCall.CALL_TIMEOUT = 60000;
- MatrixCall.FALLBACK_STUN_SERVER = 'stun:stun.l.google.com:19302';
-@@ -132,6 +171,22 @@ angular.module('MatrixCall', [])
- pc.onsignalingstatechange = function() { self.onSignallingStateChanged(); };
- pc.onicecandidate = function(c) { self.gotLocalIceCandidate(c); };
- pc.onaddstream = function(s) { self.onAddStream(s); };
-+
-+ var datachan = pc.createDataChannel('RTCDataChannel', {
-+ reliable: false
-+ });
-+ console.log("data chan: "+datachan);
-+ datachan.onopen = function() {
-+ console.log("data channel open");
-+ };
-+ datachan.onmessage = function() {
-+ console.log("data channel message");
-+ };
-+ pc.ondatachannel = function(event) {
-+ console.log("have data channel");
-+ event.channel.binaryType = 'blob';
-+ };
-+
- return pc;
- }
-
-@@ -200,6 +255,12 @@ angular.module('MatrixCall', [])
- }, this.msg.lifetime - event.age);
- };
-
-+ MatrixCall.prototype.receivedInvite = function(event) {
-+ console.log("Got second invite for call "+this.call_id);
-+ this.peerConn.setRemoteDescription(new RTCSessionDescription(this.msg.offer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError);
-+ };
-+
-+
- // perverse as it may seem, sometimes we want to instantiate a call with a hangup message
- // (because when getting the state of the room on load, events come in reverse order and
- // we want to remember that a call has been hung up)
-@@ -349,7 +410,7 @@ angular.module('MatrixCall', [])
- 'mandatory': {
- 'OfferToReceiveAudio': true,
- 'OfferToReceiveVideo': this.type == 'video'
-- },
-+ }
- };
- this.peerConn.createAnswer(function(d) { self.createdAnswer(d); }, function(e) {}, constraints);
- // This can't be in an apply() because it's called by a predecessor call under glare conditions :(
-@@ -359,8 +420,20 @@ angular.module('MatrixCall', [])
- MatrixCall.prototype.gotLocalIceCandidate = function(event) {
- if (event.candidate) {
- console.log("Got local ICE "+event.candidate.sdpMid+" candidate: "+event.candidate.candidate);
-- this.sendCandidate(event.candidate);
-- }
-+ //this.sendCandidate(event.candidate);
-+ } else {
-+ console.log("have all candidates, sending answer");
-+ var content = {
-+ version: 0,
-+ call_id: this.call_id,
-+ answer: this.peerConn.localDescription
-+ };
-+ this.sendEventWithRetry('m.call.answer', content);
-+ var self = this;
-+ $rootScope.$apply(function() {
-+ self.state = 'connecting';
-+ });
-+ }
- }
-
- MatrixCall.prototype.gotRemoteIceCandidate = function(cand) {
-@@ -418,15 +491,6 @@ angular.module('MatrixCall', [])
- console.log("Created answer: "+description);
- var self = this;
- this.peerConn.setLocalDescription(description, function() {
-- var content = {
-- version: 0,
-- call_id: self.call_id,
-- answer: self.peerConn.localDescription
-- };
-- self.sendEventWithRetry('m.call.answer', content);
-- $rootScope.$apply(function() {
-- self.state = 'connecting';
-- });
- }, function() { console.log("Error setting local description!"); } );
- };
-
-@@ -448,6 +512,9 @@ angular.module('MatrixCall', [])
- $rootScope.$apply(function() {
- self.state = 'connected';
- self.didConnect = true;
-+ /*$timeout(function() {
-+ sendKeyframe(self.peerConn);
-+ }, 1000);*/
- });
- } else if (this.peerConn.iceConnectionState == 'failed') {
- this.hangup('ice_failed');
-@@ -518,6 +585,7 @@ angular.module('MatrixCall', [])
-
- MatrixCall.prototype.onRemoteStreamEnded = function(event) {
- console.log("Remote stream ended");
-+ return;
- var self = this;
- $rootScope.$apply(function() {
- self.state = 'ended';
-diff --git a/syweb/webclient/app/components/matrix/matrix-phone-service.js b/syweb/webclient/app/components/matrix/matrix-phone-service.js
-index 55dbbf5..272fa27 100644
---- a/syweb/webclient/app/components/matrix/matrix-phone-service.js
-+++ b/syweb/webclient/app/components/matrix/matrix-phone-service.js
-@@ -48,6 +48,13 @@ angular.module('matrixPhoneService', [])
- return;
- }
-
-+ // do we already have an entry for this call ID?
-+ var existingEntry = matrixPhoneService.allCalls[msg.call_id];
-+ if (existingEntry) {
-+ existingEntry.receivedInvite(msg);
-+ return;
-+ }
-+
- var call = undefined;
- if (!isLive) {
- // if this event wasn't live then this call may already be over
-@@ -108,7 +115,7 @@ angular.module('matrixPhoneService', [])
- call.hangup();
- }
- } else {
-- $rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call);
-+ $rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call);
- }
- } else if (event.type == 'm.call.answer') {
- var call = matrixPhoneService.allCalls[msg.call_id];
diff --git a/contrib/jitsimeetbridge/unjingle/strophe.jingle.sdp.js b/contrib/jitsimeetbridge/unjingle/strophe.jingle.sdp.js
deleted file mode 100644
index e99dd7bf96..0000000000
--- a/contrib/jitsimeetbridge/unjingle/strophe.jingle.sdp.js
+++ /dev/null
@@ -1,712 +0,0 @@
-/* jshint -W117 */
-// SDP STUFF
-function SDP(sdp) {
- this.media = sdp.split('\r\nm=');
- for (var i = 1; i < this.media.length; i++) {
- this.media[i] = 'm=' + this.media[i];
- if (i != this.media.length - 1) {
- this.media[i] += '\r\n';
- }
- }
- this.session = this.media.shift() + '\r\n';
- this.raw = this.session + this.media.join('');
-}
-
-exports.SDP = SDP;
-
-var jsdom = require("jsdom");
-var window = jsdom.jsdom().parentWindow;
-var $ = require('jquery')(window);
-
-var SDPUtil = require('./strophe.jingle.sdp.util.js').SDPUtil;
-
-/**
- * Returns map of MediaChannel mapped per channel idx.
- */
-SDP.prototype.getMediaSsrcMap = function() {
- var self = this;
- var media_ssrcs = {};
- for (channelNum = 0; channelNum < self.media.length; channelNum++) {
- modified = true;
- tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc:');
- var type = SDPUtil.parse_mid(SDPUtil.find_line(self.media[channelNum], 'a=mid:'));
- var channel = new MediaChannel(channelNum, type);
- media_ssrcs[channelNum] = channel;
- tmp.forEach(function (line) {
- var linessrc = line.substring(7).split(' ')[0];
- // allocate new ChannelSsrc
- if(!channel.ssrcs[linessrc]) {
- channel.ssrcs[linessrc] = new ChannelSsrc(linessrc, type);
- }
- channel.ssrcs[linessrc].lines.push(line);
- });
- tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc-group:');
- tmp.forEach(function(line){
- var semantics = line.substr(0, idx).substr(13);
- var ssrcs = line.substr(14 + semantics.length).split(' ');
- if (ssrcs.length != 0) {
- var ssrcGroup = new ChannelSsrcGroup(semantics, ssrcs);
- channel.ssrcGroups.push(ssrcGroup);
- }
- });
- }
- return media_ssrcs;
-};
-/**
- * Returns <tt>true</tt> if this SDP contains given SSRC.
- * @param ssrc the ssrc to check.
- * @returns {boolean} <tt>true</tt> if this SDP contains given SSRC.
- */
-SDP.prototype.containsSSRC = function(ssrc) {
- var channels = this.getMediaSsrcMap();
- var contains = false;
- Object.keys(channels).forEach(function(chNumber){
- var channel = channels[chNumber];
- //console.log("Check", channel, ssrc);
- if(Object.keys(channel.ssrcs).indexOf(ssrc) != -1){
- contains = true;
- }
- });
- return contains;
-};
-
-/**
- * Returns map of MediaChannel that contains only media not contained in <tt>otherSdp</tt>. Mapped by channel idx.
- * @param otherSdp the other SDP to check ssrc with.
- */
-SDP.prototype.getNewMedia = function(otherSdp) {
-
- // this could be useful in Array.prototype.
- function arrayEquals(array) {
- // if the other array is a falsy value, return
- if (!array)
- return false;
-
- // compare lengths - can save a lot of time
- if (this.length != array.length)
- return false;
-
- for (var i = 0, l=this.length; i < l; i++) {
- // Check if we have nested arrays
- if (this[i] instanceof Array && array[i] instanceof Array) {
- // recurse into the nested arrays
- if (!this[i].equals(array[i]))
- return false;
- }
- else if (this[i] != array[i]) {
- // Warning - two different object instances will never be equal: {x:20} != {x:20}
- return false;
- }
- }
- return true;
- }
-
- var myMedia = this.getMediaSsrcMap();
- var othersMedia = otherSdp.getMediaSsrcMap();
- var newMedia = {};
- Object.keys(othersMedia).forEach(function(channelNum) {
- var myChannel = myMedia[channelNum];
- var othersChannel = othersMedia[channelNum];
- if(!myChannel && othersChannel) {
- // Add whole channel
- newMedia[channelNum] = othersChannel;
- return;
- }
- // Look for new ssrcs accross the channel
- Object.keys(othersChannel.ssrcs).forEach(function(ssrc) {
- if(Object.keys(myChannel.ssrcs).indexOf(ssrc) === -1) {
- // Allocate channel if we've found ssrc that doesn't exist in our channel
- if(!newMedia[channelNum]){
- newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType);
- }
- newMedia[channelNum].ssrcs[ssrc] = othersChannel.ssrcs[ssrc];
- }
- });
-
- // Look for new ssrc groups across the channels
- othersChannel.ssrcGroups.forEach(function(otherSsrcGroup){
-
- // try to match the other ssrc-group with an ssrc-group of ours
- var matched = false;
- for (var i = 0; i < myChannel.ssrcGroups.length; i++) {
- var mySsrcGroup = myChannel.ssrcGroups[i];
- if (otherSsrcGroup.semantics == mySsrcGroup.semantics
- && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
-
- matched = true;
- break;
- }
- }
-
- if (!matched) {
- // Allocate channel if we've found an ssrc-group that doesn't
- // exist in our channel
-
- if(!newMedia[channelNum]){
- newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType);
- }
- newMedia[channelNum].ssrcGroups.push(otherSsrcGroup);
- }
- });
- });
- return newMedia;
-};
-
-// remove iSAC and CN from SDP
-SDP.prototype.mangle = function () {
- var i, j, mline, lines, rtpmap, newdesc;
- for (i = 0; i < this.media.length; i++) {
- lines = this.media[i].split('\r\n');
- lines.pop(); // remove empty last element
- mline = SDPUtil.parse_mline(lines.shift());
- if (mline.media != 'audio')
- continue;
- newdesc = '';
- mline.fmt.length = 0;
- for (j = 0; j < lines.length; j++) {
- if (lines[j].substr(0, 9) == 'a=rtpmap:') {
- rtpmap = SDPUtil.parse_rtpmap(lines[j]);
- if (rtpmap.name == 'CN' || rtpmap.name == 'ISAC')
- continue;
- mline.fmt.push(rtpmap.id);
- newdesc += lines[j] + '\r\n';
- } else {
- newdesc += lines[j] + '\r\n';
- }
- }
- this.media[i] = SDPUtil.build_mline(mline) + '\r\n';
- this.media[i] += newdesc;
- }
- this.raw = this.session + this.media.join('');
-};
-
-// remove lines matching prefix from session section
-SDP.prototype.removeSessionLines = function(prefix) {
- var self = this;
- var lines = SDPUtil.find_lines(this.session, prefix);
- lines.forEach(function(line) {
- self.session = self.session.replace(line + '\r\n', '');
- });
- this.raw = this.session + this.media.join('');
- return lines;
-}
-// remove lines matching prefix from a media section specified by mediaindex
-// TODO: non-numeric mediaindex could match mid
-SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
- var self = this;
- var lines = SDPUtil.find_lines(this.media[mediaindex], prefix);
- lines.forEach(function(line) {
- self.media[mediaindex] = self.media[mediaindex].replace(line + '\r\n', '');
- });
- this.raw = this.session + this.media.join('');
- return lines;
-}
-
-// add content's to a jingle element
-SDP.prototype.toJingle = function (elem, thecreator) {
- var i, j, k, mline, ssrc, rtpmap, tmp, line, lines;
- var self = this;
- // new bundle plan
- if (SDPUtil.find_line(this.session, 'a=group:')) {
- lines = SDPUtil.find_lines(this.session, 'a=group:');
- for (i = 0; i < lines.length; i++) {
- tmp = lines[i].split(' ');
- var semantics = tmp.shift().substr(8);
- elem.c('group', {xmlns: 'urn:xmpp:jingle:apps:grouping:0', semantics:semantics});
- for (j = 0; j < tmp.length; j++) {
- elem.c('content', {name: tmp[j]}).up();
- }
- elem.up();
- }
- }
- // old bundle plan, to be removed
- var bundle = [];
- if (SDPUtil.find_line(this.session, 'a=group:BUNDLE')) {
- bundle = SDPUtil.find_line(this.session, 'a=group:BUNDLE ').split(' ');
- bundle.shift();
- }
- for (i = 0; i < this.media.length; i++) {
- mline = SDPUtil.parse_mline(this.media[i].split('\r\n')[0]);
- if (!(mline.media === 'audio' ||
- mline.media === 'video' ||
- mline.media === 'application'))
- {
- continue;
- }
- if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
- ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first
- } else {
- ssrc = false;
- }
-
- elem.c('content', {creator: thecreator, name: mline.media});
- if (SDPUtil.find_line(this.media[i], 'a=mid:')) {
- // prefer identifier from a=mid if present
- var mid = SDPUtil.parse_mid(SDPUtil.find_line(this.media[i], 'a=mid:'));
- elem.attrs({ name: mid });
-
- // old BUNDLE plan, to be removed
- if (bundle.indexOf(mid) !== -1) {
- elem.c('bundle', {xmlns: 'http://estos.de/ns/bundle'}).up();
- bundle.splice(bundle.indexOf(mid), 1);
- }
- }
-
- if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length)
- {
- elem.c('description',
- {xmlns: 'urn:xmpp:jingle:apps:rtp:1',
- media: mline.media });
- if (ssrc) {
- elem.attrs({ssrc: ssrc});
- }
- for (j = 0; j < mline.fmt.length; j++) {
- rtpmap = SDPUtil.find_line(this.media[i], 'a=rtpmap:' + mline.fmt[j]);
- elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
- // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
- if (SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j])) {
- tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j]));
- for (k = 0; k < tmp.length; k++) {
- elem.c('parameter', tmp[k]).up();
- }
- }
- this.RtcpFbToJingle(i, elem, mline.fmt[j]); // XEP-0293 -- map a=rtcp-fb
-
- elem.up();
- }
- if (SDPUtil.find_line(this.media[i], 'a=crypto:', this.session)) {
- elem.c('encryption', {required: 1});
- var crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
- crypto.forEach(function(line) {
- elem.c('crypto', SDPUtil.parse_crypto(line)).up();
- });
- elem.up(); // end of encryption
- }
-
- if (ssrc) {
- // new style mapping
- elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
- // FIXME: group by ssrc and support multiple different ssrcs
- var ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
- ssrclines.forEach(function(line) {
- idx = line.indexOf(' ');
- var linessrc = line.substr(0, idx).substr(7);
- if (linessrc != ssrc) {
- elem.up();
- ssrc = linessrc;
- elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
- }
- var kv = line.substr(idx + 1);
- elem.c('parameter');
- if (kv.indexOf(':') == -1) {
- elem.attrs({ name: kv });
- } else {
- elem.attrs({ name: kv.split(':', 2)[0] });
- elem.attrs({ value: kv.split(':', 2)[1] });
- }
- elem.up();
- });
- elem.up();
-
- // old proprietary mapping, to be removed at some point
- tmp = SDPUtil.parse_ssrc(this.media[i]);
- tmp.xmlns = 'http://estos.de/ns/ssrc';
- tmp.ssrc = ssrc;
- elem.c('ssrc', tmp).up(); // ssrc is part of description
-
- // XEP-0339 handle ssrc-group attributes
- var ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
- ssrc_group_lines.forEach(function(line) {
- idx = line.indexOf(' ');
- var semantics = line.substr(0, idx).substr(13);
- var ssrcs = line.substr(14 + semantics.length).split(' ');
- if (ssrcs.length != 0) {
- elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
- ssrcs.forEach(function(ssrc) {
- elem.c('source', { ssrc: ssrc })
- .up();
- });
- elem.up();
- }
- });
- }
-
- if (SDPUtil.find_line(this.media[i], 'a=rtcp-mux')) {
- elem.c('rtcp-mux').up();
- }
-
- // XEP-0293 -- map a=rtcp-fb:*
- this.RtcpFbToJingle(i, elem, '*');
-
- // XEP-0294
- if (SDPUtil.find_line(this.media[i], 'a=extmap:')) {
- lines = SDPUtil.find_lines(this.media[i], 'a=extmap:');
- for (j = 0; j < lines.length; j++) {
- tmp = SDPUtil.parse_extmap(lines[j]);
- elem.c('rtp-hdrext', { xmlns: 'urn:xmpp:jingle:apps:rtp:rtp-hdrext:0',
- uri: tmp.uri,
- id: tmp.value });
- if (tmp.hasOwnProperty('direction')) {
- switch (tmp.direction) {
- case 'sendonly':
- elem.attrs({senders: 'responder'});
- break;
- case 'recvonly':
- elem.attrs({senders: 'initiator'});
- break;
- case 'sendrecv':
- elem.attrs({senders: 'both'});
- break;
- case 'inactive':
- elem.attrs({senders: 'none'});
- break;
- }
- }
- // TODO: handle params
- elem.up();
- }
- }
- elem.up(); // end of description
- }
-
- // map ice-ufrag/pwd, dtls fingerprint, candidates
- this.TransportToJingle(i, elem);
-
- if (SDPUtil.find_line(this.media[i], 'a=sendrecv', this.session)) {
- elem.attrs({senders: 'both'});
- } else if (SDPUtil.find_line(this.media[i], 'a=sendonly', this.session)) {
- elem.attrs({senders: 'initiator'});
- } else if (SDPUtil.find_line(this.media[i], 'a=recvonly', this.session)) {
- elem.attrs({senders: 'responder'});
- } else if (SDPUtil.find_line(this.media[i], 'a=inactive', this.session)) {
- elem.attrs({senders: 'none'});
- }
- if (mline.port == '0') {
- // estos hack to reject an m-line
- elem.attrs({senders: 'rejected'});
- }
- elem.up(); // end of content
- }
- elem.up();
- return elem;
-};
-
-SDP.prototype.TransportToJingle = function (mediaindex, elem) {
- var i = mediaindex;
- var tmp;
- var self = this;
- elem.c('transport');
-
- // XEP-0343 DTLS/SCTP
- if (SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:').length)
- {
- var sctpmap = SDPUtil.find_line(
- this.media[i], 'a=sctpmap:', self.session);
- if (sctpmap)
- {
- var sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
- elem.c('sctpmap',
- {
- xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
- number: sctpAttrs[0], /* SCTP port */
- protocol: sctpAttrs[1], /* protocol */
- });
- // Optional stream count attribute
- if (sctpAttrs.length > 2)
- elem.attrs({ streams: sctpAttrs[2]});
- elem.up();
- }
- }
- // XEP-0320
- var fingerprints = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
- fingerprints.forEach(function(line) {
- tmp = SDPUtil.parse_fingerprint(line);
- tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
- elem.c('fingerprint').t(tmp.fingerprint);
- delete tmp.fingerprint;
- line = SDPUtil.find_line(self.media[mediaindex], 'a=setup:', self.session);
- if (line) {
- tmp.setup = line.substr(8);
- }
- elem.attrs(tmp);
- elem.up(); // end of fingerprint
- });
- tmp = SDPUtil.iceparams(this.media[mediaindex], this.session);
- if (tmp) {
- tmp.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
- elem.attrs(tmp);
- // XEP-0176
- if (SDPUtil.find_line(this.media[mediaindex], 'a=candidate:', this.session)) { // add any a=candidate lines
- var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=candidate:', this.session);
- lines.forEach(function (line) {
- elem.c('candidate', SDPUtil.candidateToJingle(line)).up();
- });
- }
- }
- elem.up(); // end of transport
-}
-
-SDP.prototype.RtcpFbToJingle = function (mediaindex, elem, payloadtype) { // XEP-0293
- var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=rtcp-fb:' + payloadtype);
- lines.forEach(function (line) {
- var tmp = SDPUtil.parse_rtcpfb(line);
- if (tmp.type == 'trr-int') {
- elem.c('rtcp-fb-trr-int', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', value: tmp.params[0]});
- elem.up();
- } else {
- elem.c('rtcp-fb', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', type: tmp.type});
- if (tmp.params.length > 0) {
- elem.attrs({'subtype': tmp.params[0]});
- }
- elem.up();
- }
- });
-};
-
-SDP.prototype.RtcpFbFromJingle = function (elem, payloadtype) { // XEP-0293
- var media = '';
- var tmp = elem.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
- if (tmp.length) {
- media += 'a=rtcp-fb:' + '*' + ' ' + 'trr-int' + ' ';
- if (tmp.attr('value')) {
- media += tmp.attr('value');
- } else {
- media += '0';
- }
- media += '\r\n';
- }
- tmp = elem.find('>rtcp-fb[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
- tmp.each(function () {
- media += 'a=rtcp-fb:' + payloadtype + ' ' + $(this).attr('type');
- if ($(this).attr('subtype')) {
- media += ' ' + $(this).attr('subtype');
- }
- media += '\r\n';
- });
- return media;
-};
-
-// construct an SDP from a jingle stanza
-SDP.prototype.fromJingle = function (jingle) {
- var self = this;
- this.raw = 'v=0\r\n' +
- 'o=- ' + '1923518516' + ' 2 IN IP4 0.0.0.0\r\n' +// FIXME
- 's=-\r\n' +
- 't=0 0\r\n';
- // http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04#section-8
- if ($(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').length) {
- $(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').each(function (idx, group) {
- var contents = $(group).find('>content').map(function (idx, content) {
- return content.getAttribute('name');
- }).get();
- if (contents.length > 0) {
- self.raw += 'a=group:' + (group.getAttribute('semantics') || group.getAttribute('type')) + ' ' + contents.join(' ') + '\r\n';
- }
- });
- } else if ($(jingle).find('>group[xmlns="urn:ietf:rfc:5888"]').length) {
- // temporary namespace, not to be used. to be removed soon.
- $(jingle).find('>group[xmlns="urn:ietf:rfc:5888"]').each(function (idx, group) {
- var contents = $(group).find('>content').map(function (idx, content) {
- return content.getAttribute('name');
- }).get();
- if (group.getAttribute('type') !== null && contents.length > 0) {
- self.raw += 'a=group:' + group.getAttribute('type') + ' ' + contents.join(' ') + '\r\n';
- }
- });
- } else {
- // for backward compability, to be removed soon
- // assume all contents are in the same bundle group, can be improved upon later
- var bundle = $(jingle).find('>content').filter(function (idx, content) {
- //elem.c('bundle', {xmlns:'http://estos.de/ns/bundle'});
- return $(content).find('>bundle').length > 0;
- }).map(function (idx, content) {
- return content.getAttribute('name');
- }).get();
- if (bundle.length) {
- this.raw += 'a=group:BUNDLE ' + bundle.join(' ') + '\r\n';
- }
- }
-
- this.session = this.raw;
- jingle.find('>content').each(function () {
- var m = self.jingle2media($(this));
- self.media.push(m);
- });
-
- // reconstruct msid-semantic -- apparently not necessary
- /*
- var msid = SDPUtil.parse_ssrc(this.raw);
- if (msid.hasOwnProperty('mslabel')) {
- this.session += "a=msid-semantic: WMS " + msid.mslabel + "\r\n";
- }
- */
-
- this.raw = this.session + this.media.join('');
-};
-
-// translate a jingle content element into an an SDP media part
-SDP.prototype.jingle2media = function (content) {
- var media = '',
- desc = content.find('description'),
- ssrc = desc.attr('ssrc'),
- self = this,
- tmp;
- var sctp = content.find(
- '>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]');
-
- tmp = { media: desc.attr('media') };
- tmp.port = '1';
- if (content.attr('senders') == 'rejected') {
- // estos hack to reject an m-line.
- tmp.port = '0';
- }
- if (content.find('>transport>fingerprint').length || desc.find('encryption').length) {
- if (sctp.length)
- tmp.proto = 'DTLS/SCTP';
- else
- tmp.proto = 'RTP/SAVPF';
- } else {
- tmp.proto = 'RTP/AVPF';
- }
- if (!sctp.length)
- {
- tmp.fmt = desc.find('payload-type').map(
- function () { return this.getAttribute('id'); }).get();
- media += SDPUtil.build_mline(tmp) + '\r\n';
- }
- else
- {
- media += 'm=application 1 DTLS/SCTP ' + sctp.attr('number') + '\r\n';
- media += 'a=sctpmap:' + sctp.attr('number') +
- ' ' + sctp.attr('protocol');
-
- var streamCount = sctp.attr('streams');
- if (streamCount)
- media += ' ' + streamCount + '\r\n';
- else
- media += '\r\n';
- }
-
- media += 'c=IN IP4 0.0.0.0\r\n';
- if (!sctp.length)
- media += 'a=rtcp:1 IN IP4 0.0.0.0\r\n';
- //tmp = content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
- tmp = content.find('>bundle>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
- //console.log('transports: '+content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]').length);
- //console.log('bundle.transports: '+content.find('>bundle>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]').length);
- //console.log("tmp fingerprint: "+tmp.find('>fingerprint').innerHTML);
- if (tmp.length) {
- if (tmp.attr('ufrag')) {
- media += SDPUtil.build_iceufrag(tmp.attr('ufrag')) + '\r\n';
- }
- if (tmp.attr('pwd')) {
- media += SDPUtil.build_icepwd(tmp.attr('pwd')) + '\r\n';
- }
- tmp.find('>fingerprint').each(function () {
- // FIXME: check namespace at some point
- media += 'a=fingerprint:' + this.getAttribute('hash');
- media += ' ' + $(this).text();
- media += '\r\n';
- //console.log("mline "+media);
- if (this.getAttribute('setup')) {
- media += 'a=setup:' + this.getAttribute('setup') + '\r\n';
- }
- });
- }
- switch (content.attr('senders')) {
- case 'initiator':
- media += 'a=sendonly\r\n';
- break;
- case 'responder':
- media += 'a=recvonly\r\n';
- break;
- case 'none':
- media += 'a=inactive\r\n';
- break;
- case 'both':
- media += 'a=sendrecv\r\n';
- break;
- }
- media += 'a=mid:' + content.attr('name') + '\r\n';
- /*if (content.attr('name') == 'video') {
- media += 'a=x-google-flag:conference' + '\r\n';
- }*/
-
- // <description><rtcp-mux/></description>
- // see http://code.google.com/p/libjingle/issues/detail?id=309 -- no spec though
- // and http://mail.jabber.org/pipermail/jingle/2011-December/001761.html
- if (desc.find('rtcp-mux').length) {
- media += 'a=rtcp-mux\r\n';
- }
-
- if (desc.find('encryption').length) {
- desc.find('encryption>crypto').each(function () {
- media += 'a=crypto:' + this.getAttribute('tag');
- media += ' ' + this.getAttribute('crypto-suite');
- media += ' ' + this.getAttribute('key-params');
- if (this.getAttribute('session-params')) {
- media += ' ' + this.getAttribute('session-params');
- }
- media += '\r\n';
- });
- }
- desc.find('payload-type').each(function () {
- media += SDPUtil.build_rtpmap(this) + '\r\n';
- if ($(this).find('>parameter').length) {
- media += 'a=fmtp:' + this.getAttribute('id') + ' ';
- media += $(this).find('parameter').map(function () { return (this.getAttribute('name') ? (this.getAttribute('name') + '=') : '') + this.getAttribute('value'); }).get().join('; ');
- media += '\r\n';
- }
- // xep-0293
- media += self.RtcpFbFromJingle($(this), this.getAttribute('id'));
- });
-
- // xep-0293
- media += self.RtcpFbFromJingle(desc, '*');
-
- // xep-0294
- tmp = desc.find('>rtp-hdrext[xmlns="urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"]');
- tmp.each(function () {
- media += 'a=extmap:' + this.getAttribute('id') + ' ' + this.getAttribute('uri') + '\r\n';
- });
-
- content.find('>bundle>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function () {
- media += SDPUtil.candidateFromJingle(this);
- });
-
- // XEP-0339 handle ssrc-group attributes
- tmp = content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
- var semantics = this.getAttribute('semantics');
- var ssrcs = $(this).find('>source').map(function() {
- return this.getAttribute('ssrc');
- }).get();
-
- if (ssrcs.length != 0) {
- media += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
- }
- });
-
- tmp = content.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
- tmp.each(function () {
- var ssrc = this.getAttribute('ssrc');
- $(this).find('>parameter').each(function () {
- media += 'a=ssrc:' + ssrc + ' ' + this.getAttribute('name');
- if (this.getAttribute('value') && this.getAttribute('value').length)
- media += ':' + this.getAttribute('value');
- media += '\r\n';
- });
- });
-
- if (tmp.length === 0) {
- // fallback to proprietary mapping of a=ssrc lines
- tmp = content.find('description>ssrc[xmlns="http://estos.de/ns/ssrc"]');
- if (tmp.length) {
- media += 'a=ssrc:' + ssrc + ' cname:' + tmp.attr('cname') + '\r\n';
- media += 'a=ssrc:' + ssrc + ' msid:' + tmp.attr('msid') + '\r\n';
- media += 'a=ssrc:' + ssrc + ' mslabel:' + tmp.attr('mslabel') + '\r\n';
- media += 'a=ssrc:' + ssrc + ' label:' + tmp.attr('label') + '\r\n';
- }
- }
- return media;
-};
-
diff --git a/contrib/jitsimeetbridge/unjingle/strophe.jingle.sdp.util.js b/contrib/jitsimeetbridge/unjingle/strophe.jingle.sdp.util.js
deleted file mode 100644
index 042a123c32..0000000000
--- a/contrib/jitsimeetbridge/unjingle/strophe.jingle.sdp.util.js
+++ /dev/null
@@ -1,408 +0,0 @@
-/**
- * Contains utility classes used in SDP class.
- *
- */
-
-/**
- * Class holds a=ssrc lines and media type a=mid
- * @param ssrc synchronization source identifier number(a=ssrc lines from SDP)
- * @param type media type eg. "audio" or "video"(a=mid frm SDP)
- * @constructor
- */
-function ChannelSsrc(ssrc, type) {
- this.ssrc = ssrc;
- this.type = type;
- this.lines = [];
-}
-
-/**
- * Class holds a=ssrc-group: lines
- * @param semantics
- * @param ssrcs
- * @constructor
- */
-function ChannelSsrcGroup(semantics, ssrcs, line) {
- this.semantics = semantics;
- this.ssrcs = ssrcs;
-}
-
-/**
- * Helper class represents media channel. Is a container for ChannelSsrc, holds channel idx and media type.
- * @param channelNumber channel idx in SDP media array.
- * @param mediaType media type(a=mid)
- * @constructor
- */
-function MediaChannel(channelNumber, mediaType) {
- /**
- * SDP channel number
- * @type {*}
- */
- this.chNumber = channelNumber;
- /**
- * Channel media type(a=mid)
- * @type {*}
- */
- this.mediaType = mediaType;
- /**
- * The maps of ssrc numbers to ChannelSsrc objects.
- */
- this.ssrcs = {};
-
- /**
- * The array of ChannelSsrcGroup objects.
- * @type {Array}
- */
- this.ssrcGroups = [];
-}
-
-SDPUtil = {
- iceparams: function (mediadesc, sessiondesc) {
- var data = null;
- if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
- SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
- data = {
- ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
- pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
- };
- }
- return data;
- },
- parse_iceufrag: function (line) {
- return line.substring(12);
- },
- build_iceufrag: function (frag) {
- return 'a=ice-ufrag:' + frag;
- },
- parse_icepwd: function (line) {
- return line.substring(10);
- },
- build_icepwd: function (pwd) {
- return 'a=ice-pwd:' + pwd;
- },
- parse_mid: function (line) {
- return line.substring(6);
- },
- parse_mline: function (line) {
- var parts = line.substring(2).split(' '),
- data = {};
- data.media = parts.shift();
- data.port = parts.shift();
- data.proto = parts.shift();
- if (parts[parts.length - 1] === '') { // trailing whitespace
- parts.pop();
- }
- data.fmt = parts;
- return data;
- },
- build_mline: function (mline) {
- return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' ');
- },
- parse_rtpmap: function (line) {
- var parts = line.substring(9).split(' '),
- data = {};
- data.id = parts.shift();
- parts = parts[0].split('/');
- data.name = parts.shift();
- data.clockrate = parts.shift();
- data.channels = parts.length ? parts.shift() : '1';
- return data;
- },
- /**
- * Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
- * @param line eg. "a=sctpmap:5000 webrtc-datachannel"
- * @returns [SCTP port number, protocol, streams]
- */
- parse_sctpmap: function (line)
- {
- var parts = line.substring(10).split(' ');
- var sctpPort = parts[0];
- var protocol = parts[1];
- // Stream count is optional
- var streamCount = parts.length > 2 ? parts[2] : null;
- return [sctpPort, protocol, streamCount];// SCTP port
- },
- build_rtpmap: function (el) {
- var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
- if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
- line += '/' + el.getAttribute('channels');
- }
- return line;
- },
- parse_crypto: function (line) {
- var parts = line.substring(9).split(' '),
- data = {};
- data.tag = parts.shift();
- data['crypto-suite'] = parts.shift();
- data['key-params'] = parts.shift();
- if (parts.length) {
- data['session-params'] = parts.join(' ');
- }
- return data;
- },
- parse_fingerprint: function (line) { // RFC 4572
- var parts = line.substring(14).split(' '),
- data = {};
- data.hash = parts.shift();
- data.fingerprint = parts.shift();
- // TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
- return data;
- },
- parse_fmtp: function (line) {
- var parts = line.split(' '),
- i, key, value,
- data = [];
- parts.shift();
- parts = parts.join(' ').split(';');
- for (i = 0; i < parts.length; i++) {
- key = parts[i].split('=')[0];
- while (key.length && key[0] == ' ') {
- key = key.substring(1);
- }
- value = parts[i].split('=')[1];
- if (key && value) {
- data.push({name: key, value: value});
- } else if (key) {
- // rfc 4733 (DTMF) style stuff
- data.push({name: '', value: key});
- }
- }
- return data;
- },
- parse_icecandidate: function (line) {
- var candidate = {},
- elems = line.split(' ');
- candidate.foundation = elems[0].substring(12);
- candidate.component = elems[1];
- candidate.protocol = elems[2].toLowerCase();
- candidate.priority = elems[3];
- candidate.ip = elems[4];
- candidate.port = elems[5];
- // elems[6] => "typ"
- candidate.type = elems[7];
- candidate.generation = 0; // default value, may be overwritten below
- for (var i = 8; i < elems.length; i += 2) {
- switch (elems[i]) {
- case 'raddr':
- candidate['rel-addr'] = elems[i + 1];
- break;
- case 'rport':
- candidate['rel-port'] = elems[i + 1];
- break;
- case 'generation':
- candidate.generation = elems[i + 1];
- break;
- case 'tcptype':
- candidate.tcptype = elems[i + 1];
- break;
- default: // TODO
- console.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
- }
- }
- candidate.network = '1';
- candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
- return candidate;
- },
- build_icecandidate: function (cand) {
- var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
- line += ' ';
- switch (cand.type) {
- case 'srflx':
- case 'prflx':
- case 'relay':
- if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) {
- line += 'raddr';
- line += ' ';
- line += cand['rel-addr'];
- line += ' ';
- line += 'rport';
- line += ' ';
- line += cand['rel-port'];
- line += ' ';
- }
- break;
- }
- if (cand.hasOwnAttribute('tcptype')) {
- line += 'tcptype';
- line += ' ';
- line += cand.tcptype;
- line += ' ';
- }
- line += 'generation';
- line += ' ';
- line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
- return line;
- },
- parse_ssrc: function (desc) {
- // proprietary mapping of a=ssrc lines
- // TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs
- // and parse according to that
- var lines = desc.split('\r\n'),
- data = {};
- for (var i = 0; i < lines.length; i++) {
- if (lines[i].substring(0, 7) == 'a=ssrc:') {
- var idx = lines[i].indexOf(' ');
- data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
- }
- }
- return data;
- },
- parse_rtcpfb: function (line) {
- var parts = line.substr(10).split(' ');
- var data = {};
- data.pt = parts.shift();
- data.type = parts.shift();
- data.params = parts;
- return data;
- },
- parse_extmap: function (line) {
- var parts = line.substr(9).split(' ');
- var data = {};
- data.value = parts.shift();
- if (data.value.indexOf('/') != -1) {
- data.direction = data.value.substr(data.value.indexOf('/') + 1);
- data.value = data.value.substr(0, data.value.indexOf('/'));
- } else {
- data.direction = 'both';
- }
- data.uri = parts.shift();
- data.params = parts;
- return data;
- },
- find_line: function (haystack, needle, sessionpart) {
- var lines = haystack.split('\r\n');
- for (var i = 0; i < lines.length; i++) {
- if (lines[i].substring(0, needle.length) == needle) {
- return lines[i];
- }
- }
- if (!sessionpart) {
- return false;
- }
- // search session part
- lines = sessionpart.split('\r\n');
- for (var j = 0; j < lines.length; j++) {
- if (lines[j].substring(0, needle.length) == needle) {
- return lines[j];
- }
- }
- return false;
- },
- find_lines: function (haystack, needle, sessionpart) {
- var lines = haystack.split('\r\n'),
- needles = [];
- for (var i = 0; i < lines.length; i++) {
- if (lines[i].substring(0, needle.length) == needle)
- needles.push(lines[i]);
- }
- if (needles.length || !sessionpart) {
- return needles;
- }
- // search session part
- lines = sessionpart.split('\r\n');
- for (var j = 0; j < lines.length; j++) {
- if (lines[j].substring(0, needle.length) == needle) {
- needles.push(lines[j]);
- }
- }
- return needles;
- },
- candidateToJingle: function (line) {
- // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0
- // <candidate component=... foundation=... generation=... id=... ip=... network=... port=... priority=... protocol=... type=.../>
- if (line.indexOf('candidate:') === 0) {
- line = 'a=' + line;
- } else if (line.substring(0, 12) != 'a=candidate:') {
- console.log('parseCandidate called with a line that is not a candidate line');
- console.log(line);
- return null;
- }
- if (line.substring(line.length - 2) == '\r\n') // chomp it
- line = line.substring(0, line.length - 2);
- var candidate = {},
- elems = line.split(' '),
- i;
- if (elems[6] != 'typ') {
- console.log('did not find typ in the right place');
- console.log(line);
- return null;
- }
- candidate.foundation = elems[0].substring(12);
- candidate.component = elems[1];
- candidate.protocol = elems[2].toLowerCase();
- candidate.priority = elems[3];
- candidate.ip = elems[4];
- candidate.port = elems[5];
- // elems[6] => "typ"
- candidate.type = elems[7];
-
- candidate.generation = '0'; // default, may be overwritten below
- for (i = 8; i < elems.length; i += 2) {
- switch (elems[i]) {
- case 'raddr':
- candidate['rel-addr'] = elems[i + 1];
- break;
- case 'rport':
- candidate['rel-port'] = elems[i + 1];
- break;
- case 'generation':
- candidate.generation = elems[i + 1];
- break;
- case 'tcptype':
- candidate.tcptype = elems[i + 1];
- break;
- default: // TODO
- console.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
- }
- }
- candidate.network = '1';
- candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
- return candidate;
- },
- candidateFromJingle: function (cand) {
- var line = 'a=candidate:';
- line += cand.getAttribute('foundation');
- line += ' ';
- line += cand.getAttribute('component');
- line += ' ';
- line += cand.getAttribute('protocol'); //.toUpperCase(); // chrome M23 doesn't like this
- line += ' ';
- line += cand.getAttribute('priority');
- line += ' ';
- line += cand.getAttribute('ip');
- line += ' ';
- line += cand.getAttribute('port');
- line += ' ';
- line += 'typ';
- line += ' ' + cand.getAttribute('type');
- line += ' ';
- switch (cand.getAttribute('type')) {
- case 'srflx':
- case 'prflx':
- case 'relay':
- if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) {
- line += 'raddr';
- line += ' ';
- line += cand.getAttribute('rel-addr');
- line += ' ';
- line += 'rport';
- line += ' ';
- line += cand.getAttribute('rel-port');
- line += ' ';
- }
- break;
- }
- if (cand.getAttribute('protocol').toLowerCase() == 'tcp') {
- line += 'tcptype';
- line += ' ';
- line += cand.getAttribute('tcptype');
- line += ' ';
- }
- line += 'generation';
- line += ' ';
- line += cand.getAttribute('generation') || '0';
- return line + '\r\n';
- }
-};
-
-exports.SDPUtil = SDPUtil;
-
diff --git a/contrib/jitsimeetbridge/unjingle/strophe/XMLHttpRequest.js b/contrib/jitsimeetbridge/unjingle/strophe/XMLHttpRequest.js
deleted file mode 100644
index 9c45c2df18..0000000000
--- a/contrib/jitsimeetbridge/unjingle/strophe/XMLHttpRequest.js
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- * Wrapper for built-in http.js to emulate the browser XMLHttpRequest object.
- *
- * This can be used with JS designed for browsers to improve reuse of code and
- * allow the use of existing libraries.
- *
- * Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs.
- *
- * @todo SSL Support
- * @author Dan DeFelippi <dan@driverdan.com>
- * @license MIT
- */
-
-var Url = require("url")
- ,sys = require("util");
-
-exports.XMLHttpRequest = function() {
- /**
- * Private variables
- */
- var self = this;
- var http = require('http');
- var https = require('https');
-
- // Holds http.js objects
- var client;
- var request;
- var response;
-
- // Request settings
- var settings = {};
-
- // Set some default headers
- var defaultHeaders = {
- "User-Agent": "node.js",
- "Accept": "*/*",
- };
-
- var headers = defaultHeaders;
-
- /**
- * Constants
- */
- this.UNSENT = 0;
- this.OPENED = 1;
- this.HEADERS_RECEIVED = 2;
- this.LOADING = 3;
- this.DONE = 4;
-
- /**
- * Public vars
- */
- // Current state
- this.readyState = this.UNSENT;
-
- // default ready state change handler in case one is not set or is set late
- this.onreadystatechange = function() {};
-
- // Result & response
- this.responseText = "";
- this.responseXML = "";
- this.status = null;
- this.statusText = null;
-
- /**
- * Open the connection. Currently supports local server requests.
- *
- * @param string method Connection method (eg GET, POST)
- * @param string url URL for the connection.
- * @param boolean async Asynchronous connection. Default is true.
- * @param string user Username for basic authentication (optional)
- * @param string password Password for basic authentication (optional)
- */
- this.open = function(method, url, async, user, password) {
- settings = {
- "method": method,
- "url": url,
- "async": async || null,
- "user": user || null,
- "password": password || null
- };
-
- this.abort();
-
- setState(this.OPENED);
- };
-
- /**
- * Sets a header for the request.
- *
- * @param string header Header name
- * @param string value Header value
- */
- this.setRequestHeader = function(header, value) {
- headers[header] = value;
- };
-
- /**
- * Gets a header from the server response.
- *
- * @param string header Name of header to get.
- * @return string Text of the header or null if it doesn't exist.
- */
- this.getResponseHeader = function(header) {
- if (this.readyState > this.OPENED && response.headers[header]) {
- return header + ": " + response.headers[header];
- }
-
- return null;
- };
-
- /**
- * Gets all the response headers.
- *
- * @return string
- */
- this.getAllResponseHeaders = function() {
- if (this.readyState < this.HEADERS_RECEIVED) {
- throw "INVALID_STATE_ERR: Headers have not been received.";
- }
- var result = "";
-
- for (var i in response.headers) {
- result += i + ": " + response.headers[i] + "\r\n";
- }
- return result.substr(0, result.length - 2);
- };
-
- /**
- * Sends the request to the server.
- *
- * @param string data Optional data to send as request body.
- */
- this.send = function(data) {
- if (this.readyState != this.OPENED) {
- throw "INVALID_STATE_ERR: connection must be opened before send() is called";
- }
-
- var ssl = false;
- var url = Url.parse(settings.url);
-
- // Determine the server
- switch (url.protocol) {
- case 'https:':
- ssl = true;
- // SSL & non-SSL both need host, no break here.
- case 'http:':
- var host = url.hostname;
- break;
-
- case undefined:
- case '':
- var host = "localhost";
- break;
-
- default:
- throw "Protocol not supported.";
- }
-
- // Default to port 80. If accessing localhost on another port be sure
- // to use http://localhost:port/path
- var port = url.port || (ssl ? 443 : 80);
- // Add query string if one is used
- var uri = url.pathname + (url.search ? url.search : '');
-
- // Set the Host header or the server may reject the request
- this.setRequestHeader("Host", host);
-
- // Set content length header
- if (settings.method == "GET" || settings.method == "HEAD") {
- data = null;
- } else if (data) {
- this.setRequestHeader("Content-Length", Buffer.byteLength(data));
-
- if (!headers["Content-Type"]) {
- this.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
- }
- }
-
- // Use the proper protocol
- var doRequest = ssl ? https.request : http.request;
-
- var options = {
- host: host,
- port: port,
- path: uri,
- method: settings.method,
- headers: headers,
- agent: false
- };
-
- var req = doRequest(options, function(res) {
- response = res;
- response.setEncoding("utf8");
-
- setState(self.HEADERS_RECEIVED);
- self.status = response.statusCode;
-
- response.on('data', function(chunk) {
- // Make sure there's some data
- if (chunk) {
- self.responseText += chunk;
- }
- setState(self.LOADING);
- });
-
- response.on('end', function() {
- setState(self.DONE);
- });
-
- response.on('error', function() {
- self.handleError(error);
- });
- }).on('error', function(error) {
- self.handleError(error);
- });
-
- req.setHeader("Connection", "Close");
-
- // Node 0.4 and later won't accept empty data. Make sure it's needed.
- if (data) {
- req.write(data);
- }
-
- req.end();
- };
-
- this.handleError = function(error) {
- this.status = 503;
- this.statusText = error;
- this.responseText = error.stack;
- setState(this.DONE);
- };
-
- /**
- * Aborts a request.
- */
- this.abort = function() {
- headers = defaultHeaders;
- this.readyState = this.UNSENT;
- this.responseText = "";
- this.responseXML = "";
- };
-
- /**
- * Changes readyState and calls onreadystatechange.
- *
- * @param int state New state
- */
- var setState = function(state) {
- self.readyState = state;
- self.onreadystatechange();
- }
-};
diff --git a/contrib/jitsimeetbridge/unjingle/strophe/base64.js b/contrib/jitsimeetbridge/unjingle/strophe/base64.js
deleted file mode 100644
index 418caac050..0000000000
--- a/contrib/jitsimeetbridge/unjingle/strophe/base64.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// This code was written by Tyler Akins and has been placed in the
-// public domain. It would be nice if you left this header intact.
-// Base64 code from Tyler Akins -- http://rumkin.com
-
-var Base64 = (function () {
- var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-
- var obj = {
- /**
- * Encodes a string in base64
- * @param {String} input The string to encode in base64.
- */
- encode: function (input) {
- var output = "";
- var chr1, chr2, chr3;
- var enc1, enc2, enc3, enc4;
- var i = 0;
-
- do {
- chr1 = input.charCodeAt(i++);
- chr2 = input.charCodeAt(i++);
- chr3 = input.charCodeAt(i++);
-
- enc1 = chr1 >> 2;
- enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
- enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
- enc4 = chr3 & 63;
-
- if (isNaN(chr2)) {
- enc3 = enc4 = 64;
- } else if (isNaN(chr3)) {
- enc4 = 64;
- }
-
- output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
- keyStr.charAt(enc3) + keyStr.charAt(enc4);
- } while (i < input.length);
-
- return output;
- },
-
- /**
- * Decodes a base64 string.
- * @param {String} input The string to decode.
- */
- decode: function (input) {
- var output = "";
- var chr1, chr2, chr3;
- var enc1, enc2, enc3, enc4;
- var i = 0;
-
- // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
- input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
-
- do {
- enc1 = keyStr.indexOf(input.charAt(i++));
- enc2 = keyStr.indexOf(input.charAt(i++));
- enc3 = keyStr.indexOf(input.charAt(i++));
- enc4 = keyStr.indexOf(input.charAt(i++));
-
- chr1 = (enc1 << 2) | (enc2 >> 4);
- chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
- chr3 = ((enc3 & 3) << 6) | enc4;
-
- output = output + String.fromCharCode(chr1);
-
- if (enc3 != 64) {
- output = output + String.fromCharCode(chr2);
- }
- if (enc4 != 64) {
- output = output + String.fromCharCode(chr3);
- }
- } while (i < input.length);
-
- return output;
- }
- };
-
- return obj;
-})();
-
-// Nodify
-exports.Base64 = Base64;
diff --git a/contrib/jitsimeetbridge/unjingle/strophe/md5.js b/contrib/jitsimeetbridge/unjingle/strophe/md5.js
deleted file mode 100644
index 5334325e2f..0000000000
--- a/contrib/jitsimeetbridge/unjingle/strophe/md5.js
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
- * Digest Algorithm, as defined in RFC 1321.
- * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
- * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
- * Distributed under the BSD License
- * See http://pajhome.org.uk/crypt/md5 for more info.
- */
-
-var MD5 = (function () {
- /*
- * Configurable variables. You may need to tweak these to be compatible with
- * the server-side, but the defaults work in most cases.
- */
- var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
- var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
- var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
-
- /*
- * Add integers, wrapping at 2^32. This uses 16-bit operations internally
- * to work around bugs in some JS interpreters.
- */
- var safe_add = function (x, y) {
- var lsw = (x & 0xFFFF) + (y & 0xFFFF);
- var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
- return (msw << 16) | (lsw & 0xFFFF);
- };
-
- /*
- * Bitwise rotate a 32-bit number to the left.
- */
- var bit_rol = function (num, cnt) {
- return (num << cnt) | (num >>> (32 - cnt));
- };
-
- /*
- * Convert a string to an array of little-endian words
- * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
- */
- var str2binl = function (str) {
- var bin = [];
- var mask = (1 << chrsz) - 1;
- for(var i = 0; i < str.length * chrsz; i += chrsz)
- {
- bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
- }
- return bin;
- };
-
- /*
- * Convert an array of little-endian words to a string
- */
- var binl2str = function (bin) {
- var str = "";
- var mask = (1 << chrsz) - 1;
- for(var i = 0; i < bin.length * 32; i += chrsz)
- {
- str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
- }
- return str;
- };
-
- /*
- * Convert an array of little-endian words to a hex string.
- */
- var binl2hex = function (binarray) {
- var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
- var str = "";
- for(var i = 0; i < binarray.length * 4; i++)
- {
- str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
- hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
- }
- return str;
- };
-
- /*
- * Convert an array of little-endian words to a base-64 string
- */
- var binl2b64 = function (binarray) {
- var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- var str = "";
- var triplet, j;
- for(var i = 0; i < binarray.length * 4; i += 3)
- {
- triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) |
- (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) |
- ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
- for(j = 0; j < 4; j++)
- {
- if(i * 8 + j * 6 > binarray.length * 32) { str += b64pad; }
- else { str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); }
- }
- }
- return str;
- };
-
- /*
- * These functions implement the four basic operations the algorithm uses.
- */
- var md5_cmn = function (q, a, b, x, s, t) {
- return safe_add(bit_rol(safe_add(safe_add(a, q),safe_add(x, t)), s),b);
- };
-
- var md5_ff = function (a, b, c, d, x, s, t) {
- return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
- };
-
- var md5_gg = function (a, b, c, d, x, s, t) {
- return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
- };
-
- var md5_hh = function (a, b, c, d, x, s, t) {
- return md5_cmn(b ^ c ^ d, a, b, x, s, t);
- };
-
- var md5_ii = function (a, b, c, d, x, s, t) {
- return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
- };
-
- /*
- * Calculate the MD5 of an array of little-endian words, and a bit length
- */
- var core_md5 = function (x, len) {
- /* append padding */
- x[len >> 5] |= 0x80 << ((len) % 32);
- x[(((len + 64) >>> 9) << 4) + 14] = len;
-
- var a = 1732584193;
- var b = -271733879;
- var c = -1732584194;
- var d = 271733878;
-
- var olda, oldb, oldc, oldd;
- for (var i = 0; i < x.length; i += 16)
- {
- olda = a;
- oldb = b;
- oldc = c;
- oldd = d;
-
- a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
- d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
- c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
- b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
- a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
- d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
- c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
- b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
- a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
- d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
- c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
- b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
- a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
- d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
- c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
- b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
-
- a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
- d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
- c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
- b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
- a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
- d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
- c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
- b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
- a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
- d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
- c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
- b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
- a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
- d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
- c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
- b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
-
- a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
- d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
- c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
- b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
- a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
- d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
- c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
- b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
- a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
- d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
- c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
- b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
- a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
- d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
- c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
- b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
-
- a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
- d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
- c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
- b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
- a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
- d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
- c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
- b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
- a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
- d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
- c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
- b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
- a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
- d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
- c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
- b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
-
- a = safe_add(a, olda);
- b = safe_add(b, oldb);
- c = safe_add(c, oldc);
- d = safe_add(d, oldd);
- }
- return [a, b, c, d];
- };
-
-
- /*
- * Calculate the HMAC-MD5, of a key and some data
- */
- var core_hmac_md5 = function (key, data) {
- var bkey = str2binl(key);
- if(bkey.length > 16) { bkey = core_md5(bkey, key.length * chrsz); }
-
- var ipad = new Array(16), opad = new Array(16);
- for(var i = 0; i < 16; i++)
- {
- ipad[i] = bkey[i] ^ 0x36363636;
- opad[i] = bkey[i] ^ 0x5C5C5C5C;
- }
-
- var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
- return core_md5(opad.concat(hash), 512 + 128);
- };
-
- var obj = {
- /*
- * These are the functions you'll usually want to call.
- * They take string arguments and return either hex or base-64 encoded
- * strings.
- */
- hexdigest: function (s) {
- return binl2hex(core_md5(str2binl(s), s.length * chrsz));
- },
-
- b64digest: function (s) {
- return binl2b64(core_md5(str2binl(s), s.length * chrsz));
- },
-
- hash: function (s) {
- return binl2str(core_md5(str2binl(s), s.length * chrsz));
- },
-
- hmac_hexdigest: function (key, data) {
- return binl2hex(core_hmac_md5(key, data));
- },
-
- hmac_b64digest: function (key, data) {
- return binl2b64(core_hmac_md5(key, data));
- },
-
- hmac_hash: function (key, data) {
- return binl2str(core_hmac_md5(key, data));
- },
-
- /*
- * Perform a simple self-test to see if the VM is working
- */
- test: function () {
- return MD5.hexdigest("abc") === "900150983cd24fb0d6963f7d28e17f72";
- }
- };
-
- return obj;
-})();
-
-// Nodify
-exports.MD5 = MD5;
diff --git a/contrib/jitsimeetbridge/unjingle/strophe/strophe.js b/contrib/jitsimeetbridge/unjingle/strophe/strophe.js
deleted file mode 100644
index 06d426cdec..0000000000
--- a/contrib/jitsimeetbridge/unjingle/strophe/strophe.js
+++ /dev/null
@@ -1,3256 +0,0 @@
-/*
- This program is distributed under the terms of the MIT license.
- Please see the LICENSE file for details.
-
- Copyright 2006-2008, OGG, LLC
-*/
-
-/* jslint configuration: */
-/*global document, window, setTimeout, clearTimeout, console,
- XMLHttpRequest, ActiveXObject,
- Base64, MD5,
- Strophe, $build, $msg, $iq, $pres */
-
-/** File: strophe.js
- * A JavaScript library for XMPP BOSH.
- *
- * This is the JavaScript version of the Strophe library. Since JavaScript
- * has no facilities for persistent TCP connections, this library uses
- * Bidirectional-streams Over Synchronous HTTP (BOSH) to emulate
- * a persistent, stateful, two-way connection to an XMPP server. More
- * information on BOSH can be found in XEP 124.
- */
-
-/** PrivateFunction: Function.prototype.bind
- * Bind a function to an instance.
- *
- * This Function object extension method creates a bound method similar
- * to those in Python. This means that the 'this' object will point
- * to the instance you want. See
- * <a href='https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind'>MDC's bind() documentation</a> and
- * <a href='http://benjamin.smedbergs.us/blog/2007-01-03/bound-functions-and-function-imports-in-javascript/'>Bound Functions and Function Imports in JavaScript</a>
- * for a complete explanation.
- *
- * This extension already exists in some browsers (namely, Firefox 3), but
- * we provide it to support those that don't.
- *
- * Parameters:
- * (Object) obj - The object that will become 'this' in the bound function.
- * (Object) argN - An option argument that will be prepended to the
- * arguments given for the function call
- *
- * Returns:
- * The bound function.
- */
-
-/* Make it work on node.js: Nodify
- *
- * Steps:
- * 1. Create the global objects: window, document, Base64, MD5 and XMLHttpRequest
- * 2. Use the node-XMLHttpRequest module.
- * 3. Use jsdom for the document object - since it supports DOM functions.
- * 4. Replace all calls to childNodes with _childNodes (since the former doesn't
- * seem to work on jsdom).
- * 5. While getting the response from XMLHttpRequest, manually convert the text
- * data to XML.
- * 6. All calls to nodeName should replaced by nodeName.toLowerCase() since jsdom
- * seems to always convert node names to upper case.
- *
- */
-var XMLHttpRequest = require('./XMLHttpRequest.js').XMLHttpRequest;
-var Base64 = require('./base64.js').Base64;
-var MD5 = require('./md5.js').MD5;
-var jsdom = require("jsdom").jsdom;
-
-document = jsdom("<html><head></head><body></body></html>"),
-
-window = {
- XMLHttpRequest: XMLHttpRequest,
- Base64: Base64,
- MD5: MD5
-};
-
-exports.Strophe = window;
-
-if (!Function.prototype.bind) {
- Function.prototype.bind = function (obj /*, arg1, arg2, ... */)
- {
- var func = this;
- var _slice = Array.prototype.slice;
- var _concat = Array.prototype.concat;
- var _args = _slice.call(arguments, 1);
-
- return function () {
- return func.apply(obj ? obj : this,
- _concat.call(_args,
- _slice.call(arguments, 0)));
- };
- };
-}
-
-/** PrivateFunction: Array.prototype.indexOf
- * Return the index of an object in an array.
- *
- * This function is not supplied by some JavaScript implementations, so
- * we provide it if it is missing. This code is from:
- * http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
- *
- * Parameters:
- * (Object) elt - The object to look for.
- * (Integer) from - The index from which to start looking. (optional).
- *
- * Returns:
- * The index of elt in the array or -1 if not found.
- */
-if (!Array.prototype.indexOf)
-{
- Array.prototype.indexOf = function(elt /*, from*/)
- {
- var len = this.length;
-
- var from = Number(arguments[1]) || 0;
- from = (from < 0) ? Math.ceil(from) : Math.floor(from);
- if (from < 0) {
- from += len;
- }
-
- for (; from < len; from++) {
- if (from in this && this[from] === elt) {
- return from;
- }
- }
-
- return -1;
- };
-}
-
-/* All of the Strophe globals are defined in this special function below so
- * that references to the globals become closures. This will ensure that
- * on page reload, these references will still be available to callbacks
- * that are still executing.
- */
-
-(function (callback) {
-var Strophe;
-
-/** Function: $build
- * Create a Strophe.Builder.
- * This is an alias for 'new Strophe.Builder(name, attrs)'.
- *
- * Parameters:
- * (String) name - The root element name.
- * (Object) attrs - The attributes for the root element in object notation.
- *
- * Returns:
- * A new Strophe.Builder object.
- */
-function $build(name, attrs) { return new Strophe.Builder(name, attrs); }
-/** Function: $msg
- * Create a Strophe.Builder with a <message/> element as the root.
- *
- * Parmaeters:
- * (Object) attrs - The <message/> element attributes in object notation.
- *
- * Returns:
- * A new Strophe.Builder object.
- */
-function $msg(attrs) { return new Strophe.Builder("message", attrs); }
-/** Function: $iq
- * Create a Strophe.Builder with an <iq/> element as the root.
- *
- * Parameters:
- * (Object) attrs - The <iq/> element attributes in object notation.
- *
- * Returns:
- * A new Strophe.Builder object.
- */
-function $iq(attrs) { return new Strophe.Builder("iq", attrs); }
-/** Function: $pres
- * Create a Strophe.Builder with a <presence/> element as the root.
- *
- * Parameters:
- * (Object) attrs - The <presence/> element attributes in object notation.
- *
- * Returns:
- * A new Strophe.Builder object.
- */
-function $pres(attrs) { return new Strophe.Builder("presence", attrs); }
-
-/** Class: Strophe
- * An object container for all Strophe library functions.
- *
- * This class is just a container for all the objects and constants
- * used in the library. It is not meant to be instantiated, but to
- * provide a namespace for library objects, constants, and functions.
- */
-Strophe = {
- /** Constant: VERSION
- * The version of the Strophe library. Unreleased builds will have
- * a version of head-HASH where HASH is a partial revision.
- */
- VERSION: "@VERSION@",
-
- /** Constants: XMPP Namespace Constants
- * Common namespace constants from the XMPP RFCs and XEPs.
- *
- * NS.HTTPBIND - HTTP BIND namespace from XEP 124.
- * NS.BOSH - BOSH namespace from XEP 206.
- * NS.CLIENT - Main XMPP client namespace.
- * NS.AUTH - Legacy authentication namespace.
- * NS.ROSTER - Roster operations namespace.
- * NS.PROFILE - Profile namespace.
- * NS.DISCO_INFO - Service discovery info namespace from XEP 30.
- * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.
- * NS.MUC - Multi-User Chat namespace from XEP 45.
- * NS.SASL - XMPP SASL namespace from RFC 3920.
- * NS.STREAM - XMPP Streams namespace from RFC 3920.
- * NS.BIND - XMPP Binding namespace from RFC 3920.
- * NS.SESSION - XMPP Session namespace from RFC 3920.
- */
- NS: {
- HTTPBIND: "http://jabber.org/protocol/httpbind",
- BOSH: "urn:xmpp:xbosh",
- CLIENT: "jabber:client",
- AUTH: "jabber:iq:auth",
- ROSTER: "jabber:iq:roster",
- PROFILE: "jabber:iq:profile",
- DISCO_INFO: "http://jabber.org/protocol/disco#info",
- DISCO_ITEMS: "http://jabber.org/protocol/disco#items",
- MUC: "http://jabber.org/protocol/muc",
- SASL: "urn:ietf:params:xml:ns:xmpp-sasl",
- STREAM: "http://etherx.jabber.org/streams",
- BIND: "urn:ietf:params:xml:ns:xmpp-bind",
- SESSION: "urn:ietf:params:xml:ns:xmpp-session",
- VERSION: "jabber:iq:version",
- STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas"
- },
-
- /** Function: addNamespace
- * This function is used to extend the current namespaces in
- * Strophe.NS. It takes a key and a value with the key being the
- * name of the new namespace, with its actual value.
- * For example:
- * Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub");
- *
- * Parameters:
- * (String) name - The name under which the namespace will be
- * referenced under Strophe.NS
- * (String) value - The actual namespace.
- */
- addNamespace: function (name, value)
- {
- Strophe.NS[name] = value;
- },
-
- /** Constants: Connection Status Constants
- * Connection status constants for use by the connection handler
- * callback.
- *
- * Status.ERROR - An error has occurred
- * Status.CONNECTING - The connection is currently being made
- * Status.CONNFAIL - The connection attempt failed
- * Status.AUTHENTICATING - The connection is authenticating
- * Status.AUTHFAIL - The authentication attempt failed
- * Status.CONNECTED - The connection has succeeded
- * Status.DISCONNECTED - The connection has been terminated
- * Status.DISCONNECTING - The connection is currently being terminated
- * Status.ATTACHED - The connection has been attached
- */
- Status: {
- ERROR: 0,
- CONNECTING: 1,
- CONNFAIL: 2,
- AUTHENTICATING: 3,
- AUTHFAIL: 4,
- CONNECTED: 5,
- DISCONNECTED: 6,
- DISCONNECTING: 7,
- ATTACHED: 8
- },
-
- /** Constants: Log Level Constants
- * Logging level indicators.
- *
- * LogLevel.DEBUG - Debug output
- * LogLevel.INFO - Informational output
- * LogLevel.WARN - Warnings
- * LogLevel.ERROR - Errors
- * LogLevel.FATAL - Fatal errors
- */
- LogLevel: {
- DEBUG: 0,
- INFO: 1,
- WARN: 2,
- ERROR: 3,
- FATAL: 4
- },
-
- /** PrivateConstants: DOM Element Type Constants
- * DOM element types.
- *
- * ElementType.NORMAL - Normal element.
- * ElementType.TEXT - Text data element.
- */
- ElementType: {
- NORMAL: 1,
- TEXT: 3
- },
-
- /** PrivateConstants: Timeout Values
- * Timeout values for error states. These values are in seconds.
- * These should not be changed unless you know exactly what you are
- * doing.
- *
- * TIMEOUT - Timeout multiplier. A waiting request will be considered
- * failed after Math.floor(TIMEOUT * wait) seconds have elapsed.
- * This defaults to 1.1, and with default wait, 66 seconds.
- * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where
- * Strophe can detect early failure, it will consider the request
- * failed if it doesn't return after
- * Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.
- * This defaults to 0.1, and with default wait, 6 seconds.
- */
- TIMEOUT: 1.1,
- SECONDARY_TIMEOUT: 0.1,
-
- /** Function: forEachChild
- * Map a function over some or all child elements of a given element.
- *
- * This is a small convenience function for mapping a function over
- * some or all of the children of an element. If elemName is null, all
- * children will be passed to the function, otherwise only children
- * whose tag names match elemName will be passed.
- *
- * Parameters:
- * (XMLElement) elem - The element to operate on.
- * (String) elemName - The child element tag name filter.
- * (Function) func - The function to apply to each child. This
- * function should take a single argument, a DOM element.
- */
- forEachChild: function (elem, elemName, func)
- {
- var i, childNode;
-
- for (i = 0; i < elem._childNodes.length; i++) {
- childNode = elem._childNodes[i];
- if (childNode.nodeType == Strophe.ElementType.NORMAL &&
- (!elemName || this.isTagEqual(childNode, elemName))) {
- func(childNode);
- }
- }
- },
-
- /** Function: isTagEqual
- * Compare an element's tag name with a string.
- *
- * This function is case insensitive.
- *
- * Parameters:
- * (XMLElement) el - A DOM element.
- * (String) name - The element name.
- *
- * Returns:
- * true if the element's tag name matches _el_, and false
- * otherwise.
- */
- isTagEqual: function (el, name)
- {
- return el.tagName.toLowerCase() == name.toLowerCase();
- },
-
- /** PrivateVariable: _xmlGenerator
- * _Private_ variable that caches a DOM document to
- * generate elements.
- */
- _xmlGenerator: null,
-
- /** PrivateFunction: _makeGenerator
- * _Private_ function that creates a dummy XML DOM document to serve as
- * an element and text node generator.
- */
- _makeGenerator: function () {
- var doc;
-
- if (window.ActiveXObject) {
- doc = this._getIEXmlDom();
- doc.appendChild(doc.createElement('strophe'));
- } else {
- doc = document.implementation
- .createDocument('jabber:client', 'strophe', null);
- }
-
- return doc;
- },
-
- /** Function: xmlGenerator
- * Get the DOM document to generate elements.
- *
- * Returns:
- * The currently used DOM document.
- */
- xmlGenerator: function () {
- if (!Strophe._xmlGenerator) {
- Strophe._xmlGenerator = Strophe._makeGenerator();
- }
- return Strophe._xmlGenerator;
- },
-
- /** PrivateFunction: _getIEXmlDom
- * Gets IE xml doc object
- *
- * Returns:
- * A Microsoft XML DOM Object
- * See Also:
- * http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
- */
- _getIEXmlDom : function() {
- var doc = null;
- var docStrings = [
- "Msxml2.DOMDocument.6.0",
- "Msxml2.DOMDocument.5.0",
- "Msxml2.DOMDocument.4.0",
- "MSXML2.DOMDocument.3.0",
- "MSXML2.DOMDocument",
- "MSXML.DOMDocument",
- "Microsoft.XMLDOM"
- ];
-
- for (var d = 0; d < docStrings.length; d++) {
- if (doc === null) {
- try {
- doc = new ActiveXObject(docStrings[d]);
- } catch (e) {
- doc = null;
- }
- } else {
- break;
- }
- }
-
- return doc;
- },
-
- /** Function: xmlElement
- * Create an XML DOM element.
- *
- * This function creates an XML DOM element correctly across all
- * implementations. Note that these are not HTML DOM elements, which
- * aren't appropriate for XMPP stanzas.
- *
- * Parameters:
- * (String) name - The name for the element.
- * (Array|Object) attrs - An optional array or object containing
- * key/value pairs to use as element attributes. The object should
- * be in the format {'key': 'value'} or {key: 'value'}. The array
- * should have the format [['key1', 'value1'], ['key2', 'value2']].
- * (String) text - The text child data for the element.
- *
- * Returns:
- * A new XML DOM element.
- */
- xmlElement: function (name)
- {
- if (!name) { return null; }
-
- var node = Strophe.xmlGenerator().createElement(name);
-
- // FIXME: this should throw errors if args are the wrong type or
- // there are more than two optional args
- var a, i, k;
- for (a = 1; a < arguments.length; a++) {
- if (!arguments[a]) { continue; }
- if (typeof(arguments[a]) == "string" ||
- typeof(arguments[a]) == "number") {
- node.appendChild(Strophe.xmlTextNode(arguments[a]));
- } else if (typeof(arguments[a]) == "object" &&
- typeof(arguments[a].sort) == "function") {
- for (i = 0; i < arguments[a].length; i++) {
- if (typeof(arguments[a][i]) == "object" &&
- typeof(arguments[a][i].sort) == "function") {
- node.setAttribute(arguments[a][i][0],
- arguments[a][i][1]);
- }
- }
- } else if (typeof(arguments[a]) == "object") {
- for (k in arguments[a]) {
- if (arguments[a].hasOwnProperty(k)) {
- node.setAttribute(k, arguments[a][k]);
- }
- }
- }
- }
-
- return node;
- },
-
- /* Function: xmlescape
- * Excapes invalid xml characters.
- *
- * Parameters:
- * (String) text - text to escape.
- *
- * Returns:
- * Escaped text.
- */
- xmlescape: function(text)
- {
- text = text.replace(/\&/g, "&");
- text = text.replace(/</g, "<");
- text = text.replace(/>/g, ">");
- return text;
- },
-
- /** Function: xmlTextNode
- * Creates an XML DOM text node.
- *
- * Provides a cross implementation version of document.createTextNode.
- *
- * Parameters:
- * (String) text - The content of the text node.
- *
- * Returns:
- * A new XML DOM text node.
- */
- xmlTextNode: function (text)
- {
- //ensure text is escaped
- text = Strophe.xmlescape(text);
-
- return Strophe.xmlGenerator().createTextNode(text);
- },
-
- /** Function: getText
- * Get the concatenation of all text children of an element.
- *
- * Parameters:
- * (XMLElement) elem - A DOM element.
- *
- * Returns:
- * A String with the concatenated text of all text element children.
- */
- getText: function (elem)
- {
- if (!elem) { return null; }
-
- var str = "";
- if (elem._childNodes.length === 0 && elem.nodeType ==
- Strophe.ElementType.TEXT) {
- str += elem.nodeValue;
- }
-
- for (var i = 0; i < elem._childNodes.length; i++) {
- if (elem._childNodes[i].nodeType == Strophe.ElementType.TEXT) {
- str += elem._childNodes[i].nodeValue;
- }
- }
-
- return str;
- },
-
- /** Function: copyElement
- * Copy an XML DOM element.
- *
- * This function copies a DOM element and all its descendants and returns
- * the new copy.
- *
- * Parameters:
- * (XMLElement) elem - A DOM element.
- *
- * Returns:
- * A new, copied DOM element tree.
- */
- copyElement: function (elem)
- {
- var i, el;
- if (elem.nodeType == Strophe.ElementType.NORMAL) {
- el = Strophe.xmlElement(elem.tagName);
-
- for (i = 0; i < elem.attributes.length; i++) {
- el.setAttribute(elem.attributes[i].nodeName.toLowerCase(),
- elem.attributes[i].value);
- }
-
- for (i = 0; i < elem._childNodes.length; i++) {
- el.appendChild(Strophe.copyElement(elem._childNodes[i]));
- }
- } else if (elem.nodeType == Strophe.ElementType.TEXT) {
- el = Strophe.xmlTextNode(elem.nodeValue);
- }
-
- return el;
- },
-
- /** Function: escapeNode
- * Escape the node part (also called local part) of a JID.
- *
- * Parameters:
- * (String) node - A node (or local part).
- *
- * Returns:
- * An escaped node (or local part).
- */
- escapeNode: function (node)
- {
- return node.replace(/^\s+|\s+$/g, '')
- .replace(/\\/g, "\\5c")
- .replace(/ /g, "\\20")
- .replace(/\"/g, "\\22")
- .replace(/\&/g, "\\26")
- .replace(/\'/g, "\\27")
- .replace(/\//g, "\\2f")
- .replace(/:/g, "\\3a")
- .replace(/</g, "\\3c")
- .replace(/>/g, "\\3e")
- .replace(/@/g, "\\40");
- },
-
- /** Function: unescapeNode
- * Unescape a node part (also called local part) of a JID.
- *
- * Parameters:
- * (String) node - A node (or local part).
- *
- * Returns:
- * An unescaped node (or local part).
- */
- unescapeNode: function (node)
- {
- return node.replace(/\\20/g, " ")
- .replace(/\\22/g, '"')
- .replace(/\\26/g, "&")
- .replace(/\\27/g, "'")
- .replace(/\\2f/g, "/")
- .replace(/\\3a/g, ":")
- .replace(/\\3c/g, "<")
- .replace(/\\3e/g, ">")
- .replace(/\\40/g, "@")
- .replace(/\\5c/g, "\\");
- },
-
- /** Function: getNodeFromJid
- * Get the node portion of a JID String.
- *
- * Parameters:
- * (String) jid - A JID.
- *
- * Returns:
- * A String containing the node.
- */
- getNodeFromJid: function (jid)
- {
- if (jid.indexOf("@") < 0) { return null; }
- return jid.split("@")[0];
- },
-
- /** Function: getDomainFromJid
- * Get the domain portion of a JID String.
- *
- * Parameters:
- * (String) jid - A JID.
- *
- * Returns:
- * A String containing the domain.
- */
- getDomainFromJid: function (jid)
- {
- var bare = Strophe.getBareJidFromJid(jid);
- if (bare.indexOf("@") < 0) {
- return bare;
- } else {
- var parts = bare.split("@");
- parts.splice(0, 1);
- return parts.join('@');
- }
- },
-
- /** Function: getResourceFromJid
- * Get the resource portion of a JID String.
- *
- * Parameters:
- * (String) jid - A JID.
- *
- * Returns:
- * A String containing the resource.
- */
- getResourceFromJid: function (jid)
- {
- var s = jid.split("/");
- if (s.length < 2) { return null; }
- s.splice(0, 1);
- return s.join('/');
- },
-
- /** Function: getBareJidFromJid
- * Get the bare JID from a JID String.
- *
- * Parameters:
- * (String) jid - A JID.
- *
- * Returns:
- * A String containing the bare JID.
- */
- getBareJidFromJid: function (jid)
- {
- return jid ? jid.split("/")[0] : null;
- },
-
- /** Function: log
- * User overrideable logging function.
- *
- * This function is called whenever the Strophe library calls any
- * of the logging functions. The default implementation of this
- * function does nothing. If client code wishes to handle the logging
- * messages, it should override this with
- * > Strophe.log = function (level, msg) {
- * > (user code here)
- * > };
- *
- * Please note that data sent and received over the wire is logged
- * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().
- *
- * The different levels and their meanings are
- *
- * DEBUG - Messages useful for debugging purposes.
- * INFO - Informational messages. This is mostly information like
- * 'disconnect was called' or 'SASL auth succeeded'.
- * WARN - Warnings about potential problems. This is mostly used
- * to report transient connection errors like request timeouts.
- * ERROR - Some error occurred.
- * FATAL - A non-recoverable fatal error occurred.
- *
- * Parameters:
- * (Integer) level - The log level of the log message. This will
- * be one of the values in Strophe.LogLevel.
- * (String) msg - The log message.
- */
- log: function (level, msg)
- {
- return;
- },
-
- /** Function: debug
- * Log a message at the Strophe.LogLevel.DEBUG level.
- *
- * Parameters:
- * (String) msg - The log message.
- */
- debug: function(msg)
- {
- this.log(this.LogLevel.DEBUG, msg);
- },
-
- /** Function: info
- * Log a message at the Strophe.LogLevel.INFO level.
- *
- * Parameters:
- * (String) msg - The log message.
- */
- info: function (msg)
- {
- this.log(this.LogLevel.INFO, msg);
- },
-
- /** Function: warn
- * Log a message at the Strophe.LogLevel.WARN level.
- *
- * Parameters:
- * (String) msg - The log message.
- */
- warn: function (msg)
- {
- this.log(this.LogLevel.WARN, msg);
- },
-
- /** Function: error
- * Log a message at the Strophe.LogLevel.ERROR level.
- *
- * Parameters:
- * (String) msg - The log message.
- */
- error: function (msg)
- {
- this.log(this.LogLevel.ERROR, msg);
- },
-
- /** Function: fatal
- * Log a message at the Strophe.LogLevel.FATAL level.
- *
- * Parameters:
- * (String) msg - The log message.
- */
- fatal: function (msg)
- {
- this.log(this.LogLevel.FATAL, msg);
- },
-
- /** Function: serialize
- * Render a DOM element and all descendants to a String.
- *
- * Parameters:
- * (XMLElement) elem - A DOM element.
- *
- * Returns:
- * The serialized element tree as a String.
- */
- serialize: function (elem)
- {
- var result;
-
- if (!elem) { return null; }
-
- if (typeof(elem.tree) === "function") {
- elem = elem.tree();
- }
-
- var nodeName = elem.nodeName.toLowerCase();
- var i, child;
-
- if (elem.getAttribute("_realname")) {
- nodeName = elem.getAttribute("_realname").toLowerCase();
- }
-
- result = "<" + nodeName.toLowerCase();
- for (i = 0; i < elem.attributes.length; i++) {
- if(elem.attributes[i].nodeName.toLowerCase() != "_realname") {
- result += " " + elem.attributes[i].nodeName.toLowerCase() +
- "='" + elem.attributes[i].value
- .replace(/&/g, "&")
- .replace(/\'/g, "'")
- .replace(/</g, "<") + "'";
- }
- }
-
- if (elem._childNodes.length > 0) {
- result += ">";
- for (i = 0; i < elem._childNodes.length; i++) {
- child = elem._childNodes[i];
- if (child.nodeType == Strophe.ElementType.NORMAL) {
- // normal element, so recurse
- result += Strophe.serialize(child);
- } else if (child.nodeType == Strophe.ElementType.TEXT) {
- // text element
- result += child.nodeValue;
- }
- }
- result += "</" + nodeName.toLowerCase() + ">";
- } else {
- result += "/>";
- }
-
- return result;
- },
-
- /** PrivateVariable: _requestId
- * _Private_ variable that keeps track of the request ids for
- * connections.
- */
- _requestId: 0,
-
- /** PrivateVariable: Strophe.connectionPlugins
- * _Private_ variable Used to store plugin names that need
- * initialization on Strophe.Connection construction.
- */
- _connectionPlugins: {},
-
- /** Function: addConnectionPlugin
- * Extends the Strophe.Connection object with the given plugin.
- *
- * Paramaters:
- * (String) name - The name of the extension.
- * (Object) ptype - The plugin's prototype.
- */
- addConnectionPlugin: function (name, ptype)
- {
- Strophe._connectionPlugins[name] = ptype;
- }
-};
-
-/** Class: Strophe.Builder
- * XML DOM builder.
- *
- * This object provides an interface similar to JQuery but for building
- * DOM element easily and rapidly. All the functions except for toString()
- * and tree() return the object, so calls can be chained. Here's an
- * example using the $iq() builder helper.
- * > $iq({to: 'you', from: 'me', type: 'get', id: '1'})
- * > .c('query', {xmlns: 'strophe:example'})
- * > .c('example')
- * > .toString()
- * The above generates this XML fragment
- * > <iq to='you' from='me' type='get' id='1'>
- * > <query xmlns='strophe:example'>
- * > <example/>
- * > </query>
- * > </iq>
- * The corresponding DOM manipulations to get a similar fragment would be
- * a lot more tedious and probably involve several helper variables.
- *
- * Since adding children makes new operations operate on the child, up()
- * is provided to traverse up the tree. To add two children, do
- * > builder.c('child1', ...).up().c('child2', ...)
- * The next operation on the Builder will be relative to the second child.
- */
-
-/** Constructor: Strophe.Builder
- * Create a Strophe.Builder object.
- *
- * The attributes should be passed in object notation. For example
- * > var b = new Builder('message', {to: 'you', from: 'me'});
- * or
- * > var b = new Builder('messsage', {'xml:lang': 'en'});
- *
- * Parameters:
- * (String) name - The name of the root element.
- * (Object) attrs - The attributes for the root element in object notation.
- *
- * Returns:
- * A new Strophe.Builder.
- */
-Strophe.Builder = function (name, attrs)
-{
- // Set correct namespace for jabber:client elements
- if (name == "presence" || name == "message" || name == "iq") {
- if (attrs && !attrs.xmlns) {
- attrs.xmlns = Strophe.NS.CLIENT;
- } else if (!attrs) {
- attrs = {xmlns: Strophe.NS.CLIENT};
- }
- }
-
- // Holds the tree being built.
- this.nodeTree = Strophe.xmlElement(name, attrs);
-
- // Points to the current operation node.
- this.node = this.nodeTree;
-};
-
-Strophe.Builder.prototype = {
- /** Function: tree
- * Return the DOM tree.
- *
- * This function returns the current DOM tree as an element object. This
- * is suitable for passing to functions like Strophe.Connection.send().
- *
- * Returns:
- * The DOM tree as a element object.
- */
- tree: function ()
- {
- return this.nodeTree;
- },
-
- /** Function: toString
- * Serialize the DOM tree to a String.
- *
- * This function returns a string serialization of the current DOM
- * tree. It is often used internally to pass data to a
- * Strophe.Request object.
- *
- * Returns:
- * The serialized DOM tree in a String.
- */
- toString: function ()
- {
- return Strophe.serialize(this.nodeTree);
- },
-
- /** Function: up
- * Make the current parent element the new current element.
- *
- * This function is often used after c() to traverse back up the tree.
- * For example, to add two children to the same element
- * > builder.c('child1', {}).up().c('child2', {});
- *
- * Returns:
- * The Stophe.Builder object.
- */
- up: function ()
- {
- this.node = this.node.parentNode;
- return this;
- },
-
- /** Function: attrs
- * Add or modify attributes of the current element.
- *
- * The attributes should be passed in object notation. This function
- * does not move the current element pointer.
- *
- * Parameters:
- * (Object) moreattrs - The attributes to add/modify in object notation.
- *
- * Returns:
- * The Strophe.Builder object.
- */
- attrs: function (moreattrs)
- {
- for (var k in moreattrs) {
- if (moreattrs.hasOwnProperty(k)) {
- this.node.setAttribute(k, moreattrs[k]);
- }
- }
- return this;
- },
-
- /** Function: c
- * Add a child to the current element and make it the new current
- * element.
- *
- * This function moves the current element pointer to the child. If you
- * need to add another child, it is necessary to use up() to go back
- * to the parent in the tree.
- *
- * Parameters:
- * (String) name - The name of the child.
- * (Object) attrs - The attributes of the child in object notation.
- *
- * Returns:
- * The Strophe.Builder object.
- */
- c: function (name, attrs)
- {
- var child = Strophe.xmlElement(name, attrs);
- this.node.appendChild(child);
- this.node = child;
- return this;
- },
-
- /** Function: cnode
- * Add a child to the current element and make it the new current
- * element.
- *
- * This function is the same as c() except that instead of using a
- * name and an attributes object to create the child it uses an
- * existing DOM element object.
- *
- * Parameters:
- * (XMLElement) elem - A DOM element.
- *
- * Returns:
- * The Strophe.Builder object.
- */
- cnode: function (elem)
- {
- var xmlGen = Strophe.xmlGenerator();
- var newElem = xmlGen.importNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem);
- this.node.appendChild(newElem);
- this.node = newElem;
- return this;
- },
-
- /** Function: t
- * Add a child text element.
- *
- * This *does not* make the child the new current element since there
- * are no children of text elements.
- *
- * Parameters:
- * (String) text - The text data to append to the current element.
- *
- * Returns:
- * The Strophe.Builder object.
- */
- t: function (text)
- {
- var child = Strophe.xmlTextNode(text);
- this.node.appendChild(child);
- return this;
- }
-};
-
-
-/** PrivateClass: Strophe.Handler
- * _Private_ helper class for managing stanza handlers.
- *
- * A Strophe.Handler encapsulates a user provided callback function to be
- * executed when matching stanzas are received by the connection.
- * Handlers can be either one-off or persistant depending on their
- * return value. Returning true will cause a Handler to remain active, and
- * returning false will remove the Handler.
- *
- * Users will not use Strophe.Handler objects directly, but instead they
- * will use Strophe.Connection.addHandler() and
- * Strophe.Connection.deleteHandler().
- */
-
-/** PrivateConstructor: Strophe.Handler
- * Create and initialize a new Strophe.Handler.
- *
- * Parameters:
- * (Function) handler - A function to be executed when the handler is run.
- * (String) ns - The namespace to match.
- * (String) name - The element name to match.
- * (String) type - The element type to match.
- * (String) id - The element id attribute to match.
- * (String) from - The element from attribute to match.
- * (Object) options - Handler options
- *
- * Returns:
- * A new Strophe.Handler object.
- */
-Strophe.Handler = function (handler, ns, name, type, id, from, options)
-{
- this.handler = handler;
- this.ns = ns;
- this.name = name;
- this.type = type;
- this.id = id;
- this.options = options || {matchbare: false};
-
- // default matchBare to false if undefined
- if (!this.options.matchBare) {
- this.options.matchBare = false;
- }
-
- if (this.options.matchBare) {
- this.from = from ? Strophe.getBareJidFromJid(from) : null;
- } else {
- this.from = from;
- }
-
- // whether the handler is a user handler or a system handler
- this.user = true;
-};
-
-Strophe.Handler.prototype = {
- /** PrivateFunction: isMatch
- * Tests if a stanza matches the Strophe.Handler.
- *
- * Parameters:
- * (XMLElement) elem - The XML element to test.
- *
- * Returns:
- * true if the stanza matches and false otherwise.
- */
- isMatch: function (elem)
- {
- var nsMatch;
- var from = null;
-
- if (this.options.matchBare) {
- from = Strophe.getBareJidFromJid(elem.getAttribute('from'));
- } else {
- from = elem.getAttribute('from');
- }
-
- nsMatch = false;
- if (!this.ns) {
- nsMatch = true;
- } else {
- var that = this;
- Strophe.forEachChild(elem, null, function (elem) {
- if (elem.getAttribute("xmlns") == that.ns) {
- nsMatch = true;
- }
- });
-
- nsMatch = nsMatch || elem.getAttribute("xmlns") == this.ns;
- }
-
- if (nsMatch &&
- (!this.name || Strophe.isTagEqual(elem, this.name)) &&
- (!this.type || elem.getAttribute("type") == this.type) &&
- (!this.id || elem.getAttribute("id") == this.id) &&
- (!this.from || from == this.from)) {
- return true;
- }
-
- return false;
- },
-
- /** PrivateFunction: run
- * Run the callback on a matching stanza.
- *
- * Parameters:
- * (XMLElement) elem - The DOM element that triggered the
- * Strophe.Handler.
- *
- * Returns:
- * A boolean indicating if the handler should remain active.
- */
- run: function (elem)
- {
- var result = null;
- try {
- result = this.handler(elem);
- } catch (e) {
- if (e.sourceURL) {
- Strophe.fatal("error: " + this.handler +
- " " + e.sourceURL + ":" +
- e.line + " - " + e.name + ": " + e.message);
- } else if (e.fileName) {
- if (typeof(console) != "undefined") {
- console.trace();
- console.error(this.handler, " - error - ", e, e.message);
- }
- Strophe.fatal("error: " + this.handler + " " +
- e.fileName + ":" + e.lineNumber + " - " +
- e.name + ": " + e.message);
- } else {
- Strophe.fatal("error: " + this.handler);
- }
-
- throw e;
- }
-
- return result;
- },
-
- /** PrivateFunction: toString
- * Get a String representation of the Strophe.Handler object.
- *
- * Returns:
- * A String.
- */
- toString: function ()
- {
- return "{Handler: " + this.handler + "(" + this.name + "," +
- this.id + "," + this.ns + ")}";
- }
-};
-
-/** PrivateClass: Strophe.TimedHandler
- * _Private_ helper class for managing timed handlers.
- *
- * A Strophe.TimedHandler encapsulates a user provided callback that
- * should be called after a certain period of time or at regular
- * intervals. The return value of the callback determines whether the
- * Strophe.TimedHandler will continue to fire.
- *
- * Users will not use Strophe.TimedHandler objects directly, but instead
- * they will use Strophe.Connection.addTimedHandler() and
- * Strophe.Connection.deleteTimedHandler().
- */
-
-/** PrivateConstructor: Strophe.TimedHandler
- * Create and initialize a new Strophe.TimedHandler object.
- *
- * Parameters:
- * (Integer) period - The number of milliseconds to wait before the
- * handler is called.
- * (Function) handler - The callback to run when the handler fires. This
- * function should take no arguments.
- *
- * Returns:
- * A new Strophe.TimedHandler object.
- */
-Strophe.TimedHandler = function (period, handler)
-{
- this.period = period;
- this.handler = handler;
-
- this.lastCalled = new Date().getTime();
- this.user = true;
-};
-
-Strophe.TimedHandler.prototype = {
- /** PrivateFunction: run
- * Run the callback for the Strophe.TimedHandler.
- *
- * Returns:
- * true if the Strophe.TimedHandler should be called again, and false
- * otherwise.
- */
- run: function ()
- {
- this.lastCalled = new Date().getTime();
- return this.handler();
- },
-
- /** PrivateFunction: reset
- * Reset the last called time for the Strophe.TimedHandler.
- */
- reset: function ()
- {
- this.lastCalled = new Date().getTime();
- },
-
- /** PrivateFunction: toString
- * Get a string representation of the Strophe.TimedHandler object.
- *
- * Returns:
- * The string representation.
- */
- toString: function ()
- {
- return "{TimedHandler: " + this.handler + "(" + this.period +")}";
- }
-};
-
-/** PrivateClass: Strophe.Request
- * _Private_ helper class that provides a cross implementation abstraction
- * for a BOSH related XMLHttpRequest.
- *
- * The Strophe.Request class is used internally to encapsulate BOSH request
- * information. It is not meant to be used from user's code.
- */
-
-/** PrivateConstructor: Strophe.Request
- * Create and initialize a new Strophe.Request object.
- *
- * Parameters:
- * (XMLElement) elem - The XML data to be sent in the request.
- * (Function) func - The function that will be called when the
- * XMLHttpRequest readyState changes.
- * (Integer) rid - The BOSH rid attribute associated with this request.
- * (Integer) sends - The number of times this same request has been
- * sent.
- */
-Strophe.Request = function (elem, func, rid, sends)
-{
- this.id = ++Strophe._requestId;
- this.xmlData = elem;
- this.data = Strophe.serialize(elem);
- // save original function in case we need to make a new request
- // from this one.
- this.origFunc = func;
- this.func = func;
- this.rid = rid;
- this.date = NaN;
- this.sends = sends || 0;
- this.abort = false;
- this.dead = null;
- this.age = function () {
- if (!this.date) { return 0; }
- var now = new Date();
- return (now - this.date) / 1000;
- };
- this.timeDead = function () {
- if (!this.dead) { return 0; }
- var now = new Date();
- return (now - this.dead) / 1000;
- };
- this.xhr = this._newXHR();
-};
-
-Strophe.Request.prototype = {
- /** PrivateFunction: getResponse
- * Get a response from the underlying XMLHttpRequest.
- *
- * This function attempts to get a response from the request and checks
- * for errors.
- *
- * Throws:
- * "parsererror" - A parser error occured.
- *
- * Returns:
- * The DOM element tree of the response.
- */
- getResponse: function ()
- {
- // console.log("getResponse:", this.xhr.responseXML, ":", this.xhr.responseText);
-
- var node = null;
- if (this.xhr.responseXML && this.xhr.responseXML.documentElement) {
- node = this.xhr.responseXML.documentElement;
- if (node.tagName == "parsererror") {
- Strophe.error("invalid response received");
- Strophe.error("responseText: " + this.xhr.responseText);
- Strophe.error("responseXML: " +
- Strophe.serialize(this.xhr.responseXML));
- throw "parsererror";
- }
- } else if (this.xhr.responseText) {
- // Hack for node.
- var _div = document.createElement("div");
- _div.innerHTML = this.xhr.responseText;
- node = _div._childNodes[0];
-
- Strophe.error("invalid response received");
- Strophe.error("responseText: " + this.xhr.responseText);
- Strophe.error("responseXML: " +
- Strophe.serialize(this.xhr.responseXML));
- }
-
- return node;
- },
-
- /** PrivateFunction: _newXHR
- * _Private_ helper function to create XMLHttpRequests.
- *
- * This function creates XMLHttpRequests across all implementations.
- *
- * Returns:
- * A new XMLHttpRequest.
- */
- _newXHR: function ()
- {
- var xhr = null;
- if (window.XMLHttpRequest) {
- xhr = new XMLHttpRequest();
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType("text/xml");
- }
- } else if (window.ActiveXObject) {
- xhr = new ActiveXObject("Microsoft.XMLHTTP");
- }
-
- // use Function.bind() to prepend ourselves as an argument
- xhr.onreadystatechange = this.func.bind(null, this);
-
- return xhr;
- }
-};
-
-/** Class: Strophe.Connection
- * XMPP Connection manager.
- *
- * Thie class is the main part of Strophe. It manages a BOSH connection
- * to an XMPP server and dispatches events to the user callbacks as
- * data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, and legacy
- * authentication.
- *
- * After creating a Strophe.Connection object, the user will typically
- * call connect() with a user supplied callback to handle connection level
- * events like authentication failure, disconnection, or connection
- * complete.
- *
- * The user will also have several event handlers defined by using
- * addHandler() and addTimedHandler(). These will allow the user code to
- * respond to interesting stanzas or do something periodically with the
- * connection. These handlers will be active once authentication is
- * finished.
- *
- * To send data to the connection, use send().
- */
-
-/** Constructor: Strophe.Connection
- * Create and initialize a Strophe.Connection object.
- *
- * Parameters:
- * (String) service - The BOSH service URL.
- *
- * Returns:
- * A new Strophe.Connection object.
- */
-Strophe.Connection = function (service)
-{
- /* The path to the httpbind service. */
- this.service = service;
- /* The connected JID. */
- this.jid = "";
- /* request id for body tags */
- this.rid = Math.floor(Math.random() * 4294967295);
- /* The current session ID. */
- this.sid = null;
- this.streamId = null;
- /* stream:features */
- this.features = null;
-
- // SASL
- this.do_session = false;
- this.do_bind = false;
-
- // handler lists
- this.timedHandlers = [];
- this.handlers = [];
- this.removeTimeds = [];
- this.removeHandlers = [];
- this.addTimeds = [];
- this.addHandlers = [];
-
- this._idleTimeout = null;
- this._disconnectTimeout = null;
-
- this.authenticated = false;
- this.disconnecting = false;
- this.connected = false;
-
- this.errors = 0;
-
- this.paused = false;
-
- // default BOSH values
- this.hold = 1;
- this.wait = 60;
- this.window = 5;
-
- this._data = [];
- this._requests = [];
- this._uniqueId = Math.round(Math.random() * 10000);
-
- this._sasl_success_handler = null;
- this._sasl_failure_handler = null;
- this._sasl_challenge_handler = null;
-
- // setup onIdle callback every 1/10th of a second
- this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
-
- // initialize plugins
- for (var k in Strophe._connectionPlugins) {
- if (Strophe._connectionPlugins.hasOwnProperty(k)) {
- var ptype = Strophe._connectionPlugins[k];
- // jslint complaints about the below line, but this is fine
- var F = function () {};
- F.prototype = ptype;
- this[k] = new F();
- this[k].init(this);
- }
- }
-};
-
-Strophe.Connection.prototype = {
- /** Function: reset
- * Reset the connection.
- *
- * This function should be called after a connection is disconnected
- * before that connection is reused.
- */
- reset: function ()
- {
- this.rid = Math.floor(Math.random() * 4294967295);
-
- this.sid = null;
- this.streamId = null;
-
- // SASL
- this.do_session = false;
- this.do_bind = false;
-
- // handler lists
- this.timedHandlers = [];
- this.handlers = [];
- this.removeTimeds = [];
- this.removeHandlers = [];
- this.addTimeds = [];
- this.addHandlers = [];
-
- this.authenticated = false;
- this.disconnecting = false;
- this.connected = false;
-
- this.errors = 0;
-
- this._requests = [];
- this._uniqueId = Math.round(Math.random()*10000);
- },
-
- /** Function: pause
- * Pause the request manager.
- *
- * This will prevent Strophe from sending any more requests to the
- * server. This is very useful for temporarily pausing while a lot
- * of send() calls are happening quickly. This causes Strophe to
- * send the data in a single request, saving many request trips.
- */
- pause: function ()
- {
- this.paused = true;
- },
-
- /** Function: resume
- * Resume the request manager.
- *
- * This resumes after pause() has been called.
- */
- resume: function ()
- {
- this.paused = false;
- },
-
- /** Function: getUniqueId
- * Generate a unique ID for use in <iq/> elements.
- *
- * All <iq/> stanzas are required to have unique id attributes. This
- * function makes creating these easy. Each connection instance has
- * a counter which starts from zero, and the value of this counter
- * plus a colon followed by the suffix becomes the unique id. If no
- * suffix is supplied, the counter is used as the unique id.
- *
- * Suffixes are used to make debugging easier when reading the stream
- * data, and their use is recommended. The counter resets to 0 for
- * every new connection for the same reason. For connections to the
- * same server that authenticate the same way, all the ids should be
- * the same, which makes it easy to see changes. This is useful for
- * automated testing as well.
- *
- * Parameters:
- * (String) suffix - A optional suffix to append to the id.
- *
- * Returns:
- * A unique string to be used for the id attribute.
- */
- getUniqueId: function (suffix)
- {
- if (typeof(suffix) == "string" || typeof(suffix) == "number") {
- return ++this._uniqueId + ":" + suffix;
- } else {
- return ++this._uniqueId + "";
- }
- },
-
- /** Function: connect
- * Starts the connection process.
- *
- * As the connection process proceeds, the user supplied callback will
- * be triggered multiple times with status updates. The callback
- * should take two arguments - the status code and the error condition.
- *
- * The status code will be one of the values in the Strophe.Status
- * constants. The error condition will be one of the conditions
- * defined in RFC 3920 or the condition 'strophe-parsererror'.
- *
- * Please see XEP 124 for a more detailed explanation of the optional
- * parameters below.
- *
- * Parameters:
- * (String) jid - The user's JID. This may be a bare JID,
- * or a full JID. If a node is not supplied, SASL ANONYMOUS
- * authentication will be attempted.
- * (String) pass - The user's password.
- * (Function) callback The connect callback function.
- * (Integer) wait - The optional HTTPBIND wait value. This is the
- * time the server will wait before returning an empty result for
- * a request. The default setting of 60 seconds is recommended.
- * Other settings will require tweaks to the Strophe.TIMEOUT value.
- * (Integer) hold - The optional HTTPBIND hold value. This is the
- * number of connections the server will hold at one time. This
- * should almost always be set to 1 (the default).
- */
- connect: function (jid, pass, callback, wait, hold, route)
- {
- this.jid = jid;
- this.pass = pass;
- this.connect_callback = callback;
- this.disconnecting = false;
- this.connected = false;
- this.authenticated = false;
- this.errors = 0;
-
- this.wait = wait || this.wait;
- this.hold = hold || this.hold;
-
- // parse jid for domain and resource
- this.domain = Strophe.getDomainFromJid(this.jid);
-
- // build the body tag
- var body_attrs = {
- to: this.domain,
- "xml:lang": "en",
- wait: this.wait,
- hold: this.hold,
- content: "text/xml; charset=utf-8",
- ver: "1.6",
- "xmpp:version": "1.0",
- "xmlns:xmpp": Strophe.NS.BOSH
- };
- if (route) {
- body_attrs.route = route;
- }
-
- var body = this._buildBody().attrs(body_attrs);
-
- this._changeConnectStatus(Strophe.Status.CONNECTING, null);
-
- this._requests.push(
- new Strophe.Request(body.tree(),
- this._onRequestStateChange.bind(
- this, this._connect_cb.bind(this)),
- body.tree().getAttribute("rid")));
- this._throttledRequestHandler();
- },
-
- /** Function: attach
- * Attach to an already created and authenticated BOSH session.
- *
- * This function is provided to allow Strophe to attach to BOSH
- * sessions which have been created externally, perhaps by a Web
- * application. This is often used to support auto-login type features
- * without putting user credentials into the page.
- *
- * Parameters:
- * (String) jid - The full JID that is bound by the session.
- * (String) sid - The SID of the BOSH session.
- * (String) rid - The current RID of the BOSH session. This RID
- * will be used by the next request.
- * (Function) callback The connect callback function.
- * (Integer) wait - The optional HTTPBIND wait value. This is the
- * time the server will wait before returning an empty result for
- * a request. The default setting of 60 seconds is recommended.
- * Other settings will require tweaks to the Strophe.TIMEOUT value.
- * (Integer) hold - The optional HTTPBIND hold value. This is the
- * number of connections the server will hold at one time. This
- * should almost always be set to 1 (the default).
- * (Integer) wind - The optional HTTBIND window value. This is the
- * allowed range of request ids that are valid. The default is 5.
- */
- attach: function (jid, sid, rid, callback, wait, hold, wind)
- {
- this.jid = jid;
- this.sid = sid;
- this.rid = rid;
- this.connect_callback = callback;
-
- this.domain = Strophe.getDomainFromJid(this.jid);
-
- this.authenticated = true;
- this.connected = true;
-
- this.wait = wait || this.wait;
- this.hold = hold || this.hold;
- this.window = wind || this.window;
-
- this._changeConnectStatus(Strophe.Status.ATTACHED, null);
- },
-
- /** Function: xmlInput
- * User overrideable function that receives XML data coming into the
- * connection.
- *
- * The default function does nothing. User code can override this with
- * > Strophe.Connection.xmlInput = function (elem) {
- * > (user code)
- * > };
- *
- * Parameters:
- * (XMLElement) elem - The XML data received by the connection.
- */
- xmlInput: function (elem)
- {
- return;
- },
-
- /** Function: xmlOutput
- * User overrideable function that receives XML data sent to the
- * connection.
- *
- * The default function does nothing. User code can override this with
- * > Strophe.Connection.xmlOutput = function (elem) {
- * > (user code)
- * > };
- *
- * Parameters:
- * (XMLElement) elem - The XMLdata sent by the connection.
- */
- xmlOutput: function (elem)
- {
- return;
- },
-
- /** Function: rawInput
- * User overrideable function that receives raw data coming into the
- * connection.
- *
- * The default function does nothing. User code can override this with
- * > Strophe.Connection.rawInput = function (data) {
- * > (user code)
- * > };
- *
- * Parameters:
- * (String) data - The data received by the connection.
- */
- rawInput: function (data)
- {
- return;
- },
-
- /** Function: rawOutput
- * User overrideable function that receives raw data sent to the
- * connection.
- *
- * The default function does nothing. User code can override this with
- * > Strophe.Connection.rawOutput = function (data) {
- * > (user code)
- * > };
- *
- * Parameters:
- * (String) data - The data sent by the connection.
- */
- rawOutput: function (data)
- {
- return;
- },
-
- /** Function: send
- * Send a stanza.
- *
- * This function is called to push data onto the send queue to
- * go out over the wire. Whenever a request is sent to the BOSH
- * server, all pending data is sent and the queue is flushed.
- *
- * Parameters:
- * (XMLElement |
- * [XMLElement] |
- * Strophe.Builder) elem - The stanza to send.
- */
- send: function (elem)
- {
- if (elem === null) { return ; }
- if (typeof(elem.sort) === "function") {
- for (var i = 0; i < elem.length; i++) {
- this._queueData(elem[i]);
- }
- } else if (typeof(elem.tree) === "function") {
- this._queueData(elem.tree());
- } else {
- this._queueData(elem);
- }
-
- this._throttledRequestHandler();
- clearTimeout(this._idleTimeout);
- this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
- },
-
- /** Function: flush
- * Immediately send any pending outgoing data.
- *
- * Normally send() queues outgoing data until the next idle period
- * (100ms), which optimizes network use in the common cases when
- * several send()s are called in succession. flush() can be used to
- * immediately send all pending data.
- */
- flush: function ()
- {
- // cancel the pending idle period and run the idle function
- // immediately
- clearTimeout(this._idleTimeout);
- this._onIdle();
- },
-
- /** Function: sendIQ
- * Helper function to send IQ stanzas.
- *
- * Parameters:
- * (XMLElement) elem - The stanza to send.
- * (Function) callback - The callback function for a successful request.
- * (Function) errback - The callback function for a failed or timed
- * out request. On timeout, the stanza will be null.
- * (Integer) timeout - The time specified in milliseconds for a
- * timeout to occur.
- *
- * Returns:
- * The id used to send the IQ.
- */
- sendIQ: function(elem, callback, errback, timeout) {
- var timeoutHandler = null;
- var that = this;
-
- if (typeof(elem.tree) === "function") {
- elem = elem.tree();
- }
- var id = elem.getAttribute('id');
-
- // inject id if not found
- if (!id) {
- id = this.getUniqueId("sendIQ");
- elem.setAttribute("id", id);
- }
-
- var handler = this.addHandler(function (stanza) {
- // remove timeout handler if there is one
- if (timeoutHandler) {
- that.deleteTimedHandler(timeoutHandler);
- }
-
- var iqtype = stanza.getAttribute('type');
- if (iqtype == 'result') {
- if (callback) {
- callback(stanza);
- }
- } else if (iqtype == 'error') {
- if (errback) {
- errback(stanza);
- }
- } else {
- throw {
- name: "StropheError",
- message: "Got bad IQ type of " + iqtype
- };
- }
- }, null, 'iq', null, id);
-
- // if timeout specified, setup timeout handler.
- if (timeout) {
- timeoutHandler = this.addTimedHandler(timeout, function () {
- // get rid of normal handler
- that.deleteHandler(handler);
-
- // call errback on timeout with null stanza
- if (errback) {
- errback(null);
- }
- return false;
- });
- }
-
- this.send(elem);
-
- return id;
- },
-
- /** PrivateFunction: _queueData
- * Queue outgoing data for later sending. Also ensures that the data
- * is a DOMElement.
- */
- _queueData: function (element) {
- if (element === null ||
- !element.tagName ||
- !element._childNodes) {
- throw {
- name: "StropheError",
- message: "Cannot queue non-DOMElement."
- };
- }
-
- this._data.push(element);
- },
-
- /** PrivateFunction: _sendRestart
- * Send an xmpp:restart stanza.
- */
- _sendRestart: function ()
- {
- this._data.push("restart");
-
- this._throttledRequestHandler();
- clearTimeout(this._idleTimeout);
- this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
- },
-
- /** Function: addTimedHandler
- * Add a timed handler to the connection.
- *
- * This function adds a timed handler. The provided handler will
- * be called every period milliseconds until it returns false,
- * the connection is terminated, or the handler is removed. Handlers
- * that wish to continue being invoked should return true.
- *
- * Because of method binding it is necessary to save the result of
- * this function if you wish to remove a handler with
- * deleteTimedHandler().
- *
- * Note that user handlers are not active until authentication is
- * successful.
- *
- * Parameters:
- * (Integer) period - The period of the handler.
- * (Function) handler - The callback function.
- *
- * Returns:
- * A reference to the handler that can be used to remove it.
- */
- addTimedHandler: function (period, handler)
- {
- var thand = new Strophe.TimedHandler(period, handler);
- this.addTimeds.push(thand);
- return thand;
- },
-
- /** Function: deleteTimedHandler
- * Delete a timed handler for a connection.
- *
- * This function removes a timed handler from the connection. The
- * handRef parameter is *not* the function passed to addTimedHandler(),
- * but is the reference returned from addTimedHandler().
- *
- * Parameters:
- * (Strophe.TimedHandler) handRef - The handler reference.
- */
- deleteTimedHandler: function (handRef)
- {
- // this must be done in the Idle loop so that we don't change
- // the handlers during iteration
- this.removeTimeds.push(handRef);
- },
-
- /** Function: addHandler
- * Add a stanza handler for the connection.
- *
- * This function adds a stanza handler to the connection. The
- * handler callback will be called for any stanza that matches
- * the parameters. Note that if multiple parameters are supplied,
- * they must all match for the handler to be invoked.
- *
- * The handler will receive the stanza that triggered it as its argument.
- * The handler should return true if it is to be invoked again;
- * returning false will remove the handler after it returns.
- *
- * As a convenience, the ns parameters applies to the top level element
- * and also any of its immediate children. This is primarily to make
- * matching /iq/query elements easy.
- *
- * The options argument contains handler matching flags that affect how
- * matches are determined. Currently the only flag is matchBare (a
- * boolean). When matchBare is true, the from parameter and the from
- * attribute on the stanza will be matched as bare JIDs instead of
- * full JIDs. To use this, pass {matchBare: true} as the value of
- * options. The default value for matchBare is false.
- *
- * The return value should be saved if you wish to remove the handler
- * with deleteHandler().
- *
- * Parameters:
- * (Function) handler - The user callback.
- * (String) ns - The namespace to match.
- * (String) name - The stanza name to match.
- * (String) type - The stanza type attribute to match.
- * (String) id - The stanza id attribute to match.
- * (String) from - The stanza from attribute to match.
- * (String) options - The handler options
- *
- * Returns:
- * A reference to the handler that can be used to remove it.
- */
- addHandler: function (handler, ns, name, type, id, from, options)
- {
- var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
- this.addHandlers.push(hand);
- return hand;
- },
-
- /** Function: deleteHandler
- * Delete a stanza handler for a connection.
- *
- * This function removes a stanza handler from the connection. The
- * handRef parameter is *not* the function passed to addHandler(),
- * but is the reference returned from addHandler().
- *
- * Parameters:
- * (Strophe.Handler) handRef - The handler reference.
- */
- deleteHandler: function (handRef)
- {
- // this must be done in the Idle loop so that we don't change
- // the handlers during iteration
- this.removeHandlers.push(handRef);
- },
-
- /** Function: disconnect
- * Start the graceful disconnection process.
- *
- * This function starts the disconnection process. This process starts
- * by sending unavailable presence and sending BOSH body of type
- * terminate. A timeout handler makes sure that disconnection happens
- * even if the BOSH server does not respond.
- *
- * The user supplied connection callback will be notified of the
- * progress as this process happens.
- *
- * Parameters:
- * (String) reason - The reason the disconnect is occuring.
- */
- disconnect: function (reason)
- {
- this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
-
- Strophe.info("Disconnect was called because: " + reason);
- if (this.connected) {
- // setup timeout handler
- this._disconnectTimeout = this._addSysTimedHandler(
- 3000, this._onDisconnectTimeout.bind(this));
- this._sendTerminate();
- }
- },
-
- /** PrivateFunction: _changeConnectStatus
- * _Private_ helper function that makes sure plugins and the user's
- * callback are notified of connection status changes.
- *
- * Parameters:
- * (Integer) status - the new connection status, one of the values
- * in Strophe.Status
- * (String) condition - the error condition or null
- */
- _changeConnectStatus: function (status, condition)
- {
- // notify all plugins listening for status changes
- for (var k in Strophe._connectionPlugins) {
- if (Strophe._connectionPlugins.hasOwnProperty(k)) {
- var plugin = this[k];
- if (plugin.statusChanged) {
- try {
- plugin.statusChanged(status, condition);
- } catch (err) {
- Strophe.error("" + k + " plugin caused an exception " +
- "changing status: " + err);
- }
- }
- }
- }
-
- // notify the user's callback
- if (this.connect_callback) {
- try {
- this.connect_callback(status, condition);
- } catch (e) {
- Strophe.error("User connection callback caused an " +
- "exception: " + e);
- }
- }
- },
-
- /** PrivateFunction: _buildBody
- * _Private_ helper function to generate the <body/> wrapper for BOSH.
- *
- * Returns:
- * A Strophe.Builder with a <body/> element.
- */
- _buildBody: function ()
- {
- var bodyWrap = $build('body', {
- rid: this.rid++,
- xmlns: Strophe.NS.HTTPBIND
- });
-
- if (this.sid !== null) {
- bodyWrap.attrs({sid: this.sid});
- }
-
- return bodyWrap;
- },
-
- /** PrivateFunction: _removeRequest
- * _Private_ function to remove a request from the queue.
- *
- * Parameters:
- * (Strophe.Request) req - The request to remove.
- */
- _removeRequest: function (req)
- {
- Strophe.debug("removing request");
-
- var i;
- for (i = this._requests.length - 1; i >= 0; i--) {
- if (req == this._requests[i]) {
- this._requests.splice(i, 1);
- }
- }
-
- // IE6 fails on setting to null, so set to empty function
- req.xhr.onreadystatechange = function () {};
-
- this._throttledRequestHandler();
- },
-
- /** PrivateFunction: _restartRequest
- * _Private_ function to restart a request that is presumed dead.
- *
- * Parameters:
- * (Integer) i - The index of the request in the queue.
- */
- _restartRequest: function (i)
- {
- var req = this._requests[i];
- if (req.dead === null) {
- req.dead = new Date();
- }
-
- this._processRequest(i);
- },
-
- /** PrivateFunction: _processRequest
- * _Private_ function to process a request in the queue.
- *
- * This function takes requests off the queue and sends them and
- * restarts dead requests.
- *
- * Parameters:
- * (Integer) i - The index of the request in the queue.
- */
- _processRequest: function (i)
- {
- var req = this._requests[i];
- var reqStatus = -1;
-
- try {
- if (req.xhr.readyState == 4) {
- reqStatus = req.xhr.status;
- }
- } catch (e) {
- Strophe.error("caught an error in _requests[" + i +
- "], reqStatus: " + reqStatus);
- }
-
- if (typeof(reqStatus) == "undefined") {
- reqStatus = -1;
- }
-
- // make sure we limit the number of retries
- if (req.sends > 5) {
- this._onDisconnectTimeout();
- return;
- }
-
- var time_elapsed = req.age();
- var primaryTimeout = (!isNaN(time_elapsed) &&
- time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait));
- var secondaryTimeout = (req.dead !== null &&
- req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait));
- var requestCompletedWithServerError = (req.xhr.readyState == 4 &&
- (reqStatus < 1 ||
- reqStatus >= 500));
- if (primaryTimeout || secondaryTimeout ||
- requestCompletedWithServerError) {
- if (secondaryTimeout) {
- Strophe.error("Request " +
- this._requests[i].id +
- " timed out (secondary), restarting");
- }
- req.abort = true;
- req.xhr.abort();
- // setting to null fails on IE6, so set to empty function
- req.xhr.onreadystatechange = function () {};
- this._requests[i] = new Strophe.Request(req.xmlData,
- req.origFunc,
- req.rid,
- req.sends);
- req = this._requests[i];
- }
-
- if (req.xhr.readyState === 0) {
- Strophe.debug("request id " + req.id +
- "." + req.sends + " posting");
-
- req.date = new Date();
- try {
- req.xhr.open("POST", this.service, true);
- } catch (e2) {
- Strophe.error("XHR open failed.");
- if (!this.connected) {
- this._changeConnectStatus(Strophe.Status.CONNFAIL,
- "bad-service");
- }
- this.disconnect();
- return;
- }
-
- // Fires the XHR request -- may be invoked immediately
- // or on a gradually expanding retry window for reconnects
- var sendFunc = function () {
- req.xhr.send(req.data);
- };
-
- // Implement progressive backoff for reconnects --
- // First retry (send == 1) should also be instantaneous
- if (req.sends > 1) {
- // Using a cube of the retry number creats a nicely
- // expanding retry window
- var backoff = Math.pow(req.sends, 3) * 1000;
- setTimeout(sendFunc, backoff);
- } else {
- sendFunc();
- }
-
- req.sends++;
-
- this.xmlOutput(req.xmlData);
- this.rawOutput(req.data);
- } else {
- Strophe.debug("_processRequest: " +
- (i === 0 ? "first" : "second") +
- " request has readyState of " +
- req.xhr.readyState);
- }
- },
-
- /** PrivateFunction: _throttledRequestHandler
- * _Private_ function to throttle requests to the connection window.
- *
- * This function makes sure we don't send requests so fast that the
- * request ids overflow the connection window in the case that one
- * request died.
- */
- _throttledRequestHandler: function ()
- {
- if (!this._requests) {
- Strophe.debug("_throttledRequestHandler called with " +
- "undefined requests");
- } else {
- Strophe.debug("_throttledRequestHandler called with " +
- this._requests.length + " requests");
- }
-
- if (!this._requests || this._requests.length === 0) {
- return;
- }
-
- if (this._requests.length > 0) {
- this._processRequest(0);
- }
-
- if (this._requests.length > 1 &&
- Math.abs(this._requests[0].rid -
- this._requests[1].rid) < this.window) {
- this._processRequest(1);
- }
- },
-
- /** PrivateFunction: _onRequestStateChange
- * _Private_ handler for Strophe.Request state changes.
- *
- * This function is called when the XMLHttpRequest readyState changes.
- * It contains a lot of error handling logic for the many ways that
- * requests can fail, and calls the request callback when requests
- * succeed.
- *
- * Parameters:
- * (Function) func - The handler for the request.
- * (Strophe.Request) req - The request that is changing readyState.
- */
- _onRequestStateChange: function (func, req)
- {
- Strophe.debug("request id " + req.id +
- "." + req.sends + " state changed to " +
- req.xhr.readyState);
-
- if (req.abort) {
- req.abort = false;
- return;
- }
-
- // request complete
- var reqStatus;
- if (req.xhr.readyState == 4) {
- reqStatus = 0;
- try {
- reqStatus = req.xhr.status;
- } catch (e) {
- // ignore errors from undefined status attribute. works
- // around a browser bug
- }
-
- if (typeof(reqStatus) == "undefined") {
- reqStatus = 0;
- }
-
- if (this.disconnecting) {
- if (reqStatus >= 400) {
- this._hitError(reqStatus);
- return;
- }
- }
-
- var reqIs0 = (this._requests[0] == req);
- var reqIs1 = (this._requests[1] == req);
-
- if ((reqStatus > 0 && reqStatus < 500) || req.sends > 5) {
- // remove from internal queue
- this._removeRequest(req);
- Strophe.debug("request id " +
- req.id +
- " should now be removed");
- }
-
- // request succeeded
- if (reqStatus == 200) {
- // if request 1 finished, or request 0 finished and request
- // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to
- // restart the other - both will be in the first spot, as the
- // completed request has been removed from the queue already
- if (reqIs1 ||
- (reqIs0 && this._requests.length > 0 &&
- this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait))) {
- this._restartRequest(0);
- }
- // call handler
- Strophe.debug("request id " +
- req.id + "." +
- req.sends + " got 200");
- func(req);
- this.errors = 0;
- } else {
- Strophe.error("request id " +
- req.id + "." +
- req.sends + " error " + reqStatus +
- " happened");
- if (reqStatus === 0 ||
- (reqStatus >= 400 && reqStatus < 600) ||
- reqStatus >= 12000) {
- this._hitError(reqStatus);
- if (reqStatus >= 400 && reqStatus < 500) {
- this._changeConnectStatus(Strophe.Status.DISCONNECTING,
- null);
- this._doDisconnect();
- }
- }
- }
-
- if (!((reqStatus > 0 && reqStatus < 500) ||
- req.sends > 5)) {
- this._throttledRequestHandler();
- }
- }
- },
-
- /** PrivateFunction: _hitError
- * _Private_ function to handle the error count.
- *
- * Requests are resent automatically until their error count reaches
- * 5. Each time an error is encountered, this function is called to
- * increment the count and disconnect if the count is too high.
- *
- * Parameters:
- * (Integer) reqStatus - The request status.
- */
- _hitError: function (reqStatus)
- {
- this.errors++;
- Strophe.warn("request errored, status: " + reqStatus +
- ", number of errors: " + this.errors);
- if (this.errors > 4) {
- this._onDisconnectTimeout();
- }
- },
-
- /** PrivateFunction: _doDisconnect
- * _Private_ function to disconnect.
- *
- * This is the last piece of the disconnection logic. This resets the
- * connection and alerts the user's connection callback.
- */
- _doDisconnect: function ()
- {
- Strophe.info("_doDisconnect was called");
- this.authenticated = false;
- this.disconnecting = false;
- this.sid = null;
- this.streamId = null;
- this.rid = Math.floor(Math.random() * 4294967295);
-
- // tell the parent we disconnected
- if (this.connected) {
- this._changeConnectStatus(Strophe.Status.DISCONNECTED, null);
- this.connected = false;
- }
-
- // delete handlers
- this.handlers = [];
- this.timedHandlers = [];
- this.removeTimeds = [];
- this.removeHandlers = [];
- this.addTimeds = [];
- this.addHandlers = [];
- },
-
- /** PrivateFunction: _dataRecv
- * _Private_ handler to processes incoming data from the the connection.
- *
- * Except for _connect_cb handling the initial connection request,
- * this function handles the incoming data for all requests. This
- * function also fires stanza handlers that match each incoming
- * stanza.
- *
- * Parameters:
- * (Strophe.Request) req - The request that has data ready.
- */
- _dataRecv: function (req)
- {
- try {
- var elem = req.getResponse();
- } catch (e) {
- if (e != "parsererror") { throw e; }
- this.disconnect("strophe-parsererror");
- }
- if (elem === null) { return; }
-
- this.xmlInput(elem);
- this.rawInput(Strophe.serialize(elem));
-
- // remove handlers scheduled for deletion
- var i, hand;
- while (this.removeHandlers.length > 0) {
- hand = this.removeHandlers.pop();
- i = this.handlers.indexOf(hand);
- if (i >= 0) {
- this.handlers.splice(i, 1);
- }
- }
-
- // add handlers scheduled for addition
- while (this.addHandlers.length > 0) {
- this.handlers.push(this.addHandlers.pop());
- }
-
- // handle graceful disconnect
- if (this.disconnecting && this._requests.length === 0) {
- this.deleteTimedHandler(this._disconnectTimeout);
- this._disconnectTimeout = null;
- this._doDisconnect();
- return;
- }
-
- var typ = elem.getAttribute("type");
- var cond, conflict;
- if (typ !== null && typ == "terminate") {
- // Don't process stanzas that come in after disconnect
- if (this.disconnecting) {
- return;
- }
-
- // an error occurred
- cond = elem.getAttribute("condition");
- conflict = elem.getElementsByTagName("conflict");
- if (cond !== null) {
- if (cond == "remote-stream-error" && conflict.length > 0) {
- cond = "conflict";
- }
- this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
- } else {
- this._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
- }
- this.disconnect();
- return;
- }
-
- // send each incoming stanza through the handler chain
- var that = this;
- Strophe.forEachChild(elem, null, function (child) {
- var i, newList;
- // process handlers
- newList = that.handlers;
- that.handlers = [];
- for (i = 0; i < newList.length; i++) {
- var hand = newList[i];
- if (hand.isMatch(child) &&
- (that.authenticated || !hand.user)) {
- if (hand.run(child)) {
- that.handlers.push(hand);
- }
- } else {
- that.handlers.push(hand);
- }
- }
- });
- },
-
- /** PrivateFunction: _sendTerminate
- * _Private_ function to send initial disconnect sequence.
- *
- * This is the first step in a graceful disconnect. It sends
- * the BOSH server a terminate body and includes an unavailable
- * presence if authentication has completed.
- */
- _sendTerminate: function ()
- {
- Strophe.info("_sendTerminate was called");
- var body = this._buildBody().attrs({type: "terminate"});
-
- if (this.authenticated) {
- body.c('presence', {
- xmlns: Strophe.NS.CLIENT,
- type: 'unavailable'
- });
- }
-
- this.disconnecting = true;
-
- var req = new Strophe.Request(body.tree(),
- this._onRequestStateChange.bind(
- this, this._dataRecv.bind(this)),
- body.tree().getAttribute("rid"));
-
- this._requests.push(req);
- this._throttledRequestHandler();
- },
-
- /** PrivateFunction: _connect_cb
- * _Private_ handler for initial connection request.
- *
- * This handler is used to process the initial connection request
- * response from the BOSH server. It is used to set up authentication
- * handlers and start the authentication process.
- *
- * SASL authentication will be attempted if available, otherwise
- * the code will fall back to legacy authentication.
- *
- * Parameters:
- * (Strophe.Request) req - The current request.
- */
- _connect_cb: function (req)
- {
- Strophe.info("_connect_cb was called");
-
- this.connected = true;
- var bodyWrap = req.getResponse();
- if (!bodyWrap) { return; }
-
- this.xmlInput(bodyWrap);
- this.rawInput(Strophe.serialize(bodyWrap));
-
- var typ = bodyWrap.getAttribute("type");
- var cond, conflict;
- if (typ !== null && typ == "terminate") {
- // an error occurred
- cond = bodyWrap.getAttribute("condition");
- conflict = bodyWrap.getElementsByTagName("conflict");
- if (cond !== null) {
- if (cond == "remote-stream-error" && conflict.length > 0) {
- cond = "conflict";
- }
- this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
- } else {
- this._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
- }
- return;
- }
-
- // check to make sure we don't overwrite these if _connect_cb is
- // called multiple times in the case of missing stream:features
- if (!this.sid) {
- this.sid = bodyWrap.getAttribute("sid");
- }
- if (!this.stream_id) {
- this.stream_id = bodyWrap.getAttribute("authid");
- }
- var wind = bodyWrap.getAttribute('requests');
- if (wind) { this.window = parseInt(wind, 10); }
- var hold = bodyWrap.getAttribute('hold');
- if (hold) { this.hold = parseInt(hold, 10); }
- var wait = bodyWrap.getAttribute('wait');
- if (wait) { this.wait = parseInt(wait, 10); }
-
-
- var do_sasl_plain = false;
- var do_sasl_digest_md5 = false;
- var do_sasl_anonymous = false;
-
- var mechanisms = bodyWrap.getElementsByTagName("mechanism");
- var i, mech, auth_str, hashed_auth_str;
- if (mechanisms.length > 0) {
- for (i = 0; i < mechanisms.length; i++) {
- mech = Strophe.getText(mechanisms[i]);
- if (mech == 'DIGEST-MD5') {
- do_sasl_digest_md5 = true;
- } else if (mech == 'PLAIN') {
- do_sasl_plain = true;
- } else if (mech == 'ANONYMOUS') {
- do_sasl_anonymous = true;
- }
- }
- } else {
- // we didn't get stream:features yet, so we need wait for it
- // by sending a blank poll request
- var body = this._buildBody();
- this._requests.push(
- new Strophe.Request(body.tree(),
- this._onRequestStateChange.bind(
- this, this._connect_cb.bind(this)),
- body.tree().getAttribute("rid")));
- this._throttledRequestHandler();
- return;
- }
-
- if (Strophe.getNodeFromJid(this.jid) === null &&
- do_sasl_anonymous) {
- this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
- this._sasl_success_handler = this._addSysHandler(
- this._sasl_success_cb.bind(this), null,
- "success", null, null);
- this._sasl_failure_handler = this._addSysHandler(
- this._sasl_failure_cb.bind(this), null,
- "failure", null, null);
-
- this.send($build("auth", {
- xmlns: Strophe.NS.SASL,
- mechanism: "ANONYMOUS"
- }).tree());
- } else if (Strophe.getNodeFromJid(this.jid) === null) {
- // we don't have a node, which is required for non-anonymous
- // client connections
- this._changeConnectStatus(Strophe.Status.CONNFAIL,
- 'x-strophe-bad-non-anon-jid');
- this.disconnect();
- } else if (do_sasl_digest_md5) {
- this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
- this._sasl_challenge_handler = this._addSysHandler(
- this._sasl_challenge1_cb.bind(this), null,
- "challenge", null, null);
- this._sasl_failure_handler = this._addSysHandler(
- this._sasl_failure_cb.bind(this), null,
- "failure", null, null);
-
- this.send($build("auth", {
- xmlns: Strophe.NS.SASL,
- mechanism: "DIGEST-MD5"
- }).tree());
- } else if (do_sasl_plain) {
- // Build the plain auth string (barejid null
- // username null password) and base 64 encoded.
- auth_str = Strophe.getBareJidFromJid(this.jid);
- auth_str = auth_str + "\u0000";
- auth_str = auth_str + Strophe.getNodeFromJid(this.jid);
- auth_str = auth_str + "\u0000";
- auth_str = auth_str + this.pass;
-
- this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
- this._sasl_success_handler = this._addSysHandler(
- this._sasl_success_cb.bind(this), null,
- "success", null, null);
- this._sasl_failure_handler = this._addSysHandler(
- this._sasl_failure_cb.bind(this), null,
- "failure", null, null);
-
- hashed_auth_str = Base64.encode(auth_str);
- this.send($build("auth", {
- xmlns: Strophe.NS.SASL,
- mechanism: "PLAIN"
- }).t(hashed_auth_str).tree());
- } else {
- this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
- this._addSysHandler(this._auth1_cb.bind(this), null, null,
- null, "_auth_1");
-
- this.send($iq({
- type: "get",
- to: this.domain,
- id: "_auth_1"
- }).c("query", {
- xmlns: Strophe.NS.AUTH
- }).c("username", {}).t(Strophe.getNodeFromJid(this.jid)).tree());
- }
- },
-
- /** PrivateFunction: _sasl_challenge1_cb
- * _Private_ handler for DIGEST-MD5 SASL authentication.
- *
- * Parameters:
- * (XMLElement) elem - The challenge stanza.
- *
- * Returns:
- * false to remove the handler.
- */
- _sasl_challenge1_cb: function (elem)
- {
- var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/;
-
- var challenge = Base64.decode(Strophe.getText(elem));
- var cnonce = MD5.hexdigest(Math.random() * 1234567890);
- var realm = "";
- var host = null;
- var nonce = "";
- var qop = "";
- var matches;
-
- // remove unneeded handlers
- this.deleteHandler(this._sasl_failure_handler);
-
- while (challenge.match(attribMatch)) {
- matches = challenge.match(attribMatch);
- challenge = challenge.replace(matches[0], "");
- matches[2] = matches[2].replace(/^"(.+)"$/, "$1");
- switch (matches[1]) {
- case "realm":
- realm = matches[2];
- break;
- case "nonce":
- nonce = matches[2];
- break;
- case "qop":
- qop = matches[2];
- break;
- case "host":
- host = matches[2];
- break;
- }
- }
-
- var digest_uri = "xmpp/" + this.domain;
- if (host !== null) {
- digest_uri = digest_uri + "/" + host;
- }
-
- var A1 = MD5.hash(Strophe.getNodeFromJid(this.jid) +
- ":" + realm + ":" + this.pass) +
- ":" + nonce + ":" + cnonce;
- var A2 = 'AUTHENTICATE:' + digest_uri;
-
- var responseText = "";
- responseText += 'username=' +
- this._quote(Strophe.getNodeFromJid(this.jid)) + ',';
- responseText += 'realm=' + this._quote(realm) + ',';
- responseText += 'nonce=' + this._quote(nonce) + ',';
- responseText += 'cnonce=' + this._quote(cnonce) + ',';
- responseText += 'nc="00000001",';
- responseText += 'qop="auth",';
- responseText += 'digest-uri=' + this._quote(digest_uri) + ',';
- responseText += 'response=' + this._quote(
- MD5.hexdigest(MD5.hexdigest(A1) + ":" +
- nonce + ":00000001:" +
- cnonce + ":auth:" +
- MD5.hexdigest(A2))) + ',';
- responseText += 'charset="utf-8"';
-
- this._sasl_challenge_handler = this._addSysHandler(
- this._sasl_challenge2_cb.bind(this), null,
- "challenge", null, null);
- this._sasl_success_handler = this._addSysHandler(
- this._sasl_success_cb.bind(this), null,
- "success", null, null);
- this._sasl_failure_handler = this._addSysHandler(
- this._sasl_failure_cb.bind(this), null,
- "failure", null, null);
-
- this.send($build('response', {
- xmlns: Strophe.NS.SASL
- }).t(Base64.encode(responseText)).tree());
-
- return false;
- },
-
- /** PrivateFunction: _quote
- * _Private_ utility function to backslash escape and quote strings.
- *
- * Parameters:
- * (String) str - The string to be quoted.
- *
- * Returns:
- * quoted string
- */
- _quote: function (str)
- {
- return '"' + str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
- //" end string workaround for emacs
- },
-
-
- /** PrivateFunction: _sasl_challenge2_cb
- * _Private_ handler for second step of DIGEST-MD5 SASL authentication.
- *
- * Parameters:
- * (XMLElement) elem - The challenge stanza.
- *
- * Returns:
- * false to remove the handler.
- */
- _sasl_challenge2_cb: function (elem)
- {
- // remove unneeded handlers
- this.deleteHandler(this._sasl_success_handler);
- this.deleteHandler(this._sasl_failure_handler);
-
- this._sasl_success_handler = this._addSysHandler(
- this._sasl_success_cb.bind(this), null,
- "success", null, null);
- this._sasl_failure_handler = this._addSysHandler(
- this._sasl_failure_cb.bind(this), null,
- "failure", null, null);
- this.send($build('response', {xmlns: Strophe.NS.SASL}).tree());
- return false;
- },
-
- /** PrivateFunction: _auth1_cb
- * _Private_ handler for legacy authentication.
- *
- * This handler is called in response to the initial <iq type='get'/>
- * for legacy authentication. It builds an authentication <iq/> and
- * sends it, creating a handler (calling back to _auth2_cb()) to
- * handle the result
- *
- * Parameters:
- * (XMLElement) elem - The stanza that triggered the callback.
- *
- * Returns:
- * false to remove the handler.
- */
- _auth1_cb: function (elem)
- {
- // build plaintext auth iq
- var iq = $iq({type: "set", id: "_auth_2"})
- .c('query', {xmlns: Strophe.NS.AUTH})
- .c('username', {}).t(Strophe.getNodeFromJid(this.jid))
- .up()
- .c('password').t(this.pass);
-
- if (!Strophe.getResourceFromJid(this.jid)) {
- // since the user has not supplied a resource, we pick
- // a default one here. unlike other auth methods, the server
- // cannot do this for us.
- this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';
- }
- iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));
-
- this._addSysHandler(this._auth2_cb.bind(this), null,
- null, null, "_auth_2");
-
- this.send(iq.tree());
-
- return false;
- },
-
- /** PrivateFunction: _sasl_success_cb
- * _Private_ handler for succesful SASL authentication.
- *
- * Parameters:
- * (XMLElement) elem - The matching stanza.
- *
- * Returns:
- * false to remove the handler.
- */
- _sasl_success_cb: function (elem)
- {
- Strophe.info("SASL authentication succeeded.");
-
- // remove old handlers
- this.deleteHandler(this._sasl_failure_handler);
- this._sasl_failure_handler = null;
- if (this._sasl_challenge_handler) {
- this.deleteHandler(this._sasl_challenge_handler);
- this._sasl_challenge_handler = null;
- }
-
- this._addSysHandler(this._sasl_auth1_cb.bind(this), null,
- "stream:features", null, null);
-
- // we must send an xmpp:restart now
- this._sendRestart();
-
- return false;
- },
-
- /** PrivateFunction: _sasl_auth1_cb
- * _Private_ handler to start stream binding.
- *
- * Parameters:
- * (XMLElement) elem - The matching stanza.
- *
- * Returns:
- * false to remove the handler.
- */
- _sasl_auth1_cb: function (elem)
- {
- // save stream:features for future usage
- this.features = elem;
-
- var i, child;
-
- for (i = 0; i < elem._childNodes.length; i++) {
- child = elem._childNodes[i];
- if (child.nodeName.toLowerCase() == 'bind') {
- this.do_bind = true;
- }
-
- if (child.nodeName.toLowerCase() == 'session') {
- this.do_session = true;
- }
- }
-
- if (!this.do_bind) {
- this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
- return false;
- } else {
- this._addSysHandler(this._sasl_bind_cb.bind(this), null, null,
- null, "_bind_auth_2");
-
- var resource = Strophe.getResourceFromJid(this.jid);
- if (resource) {
- this.send($iq({type: "set", id: "_bind_auth_2"})
- .c('bind', {xmlns: Strophe.NS.BIND})
- .c('resource', {}).t(resource).tree());
- } else {
- this.send($iq({type: "set", id: "_bind_auth_2"})
- .c('bind', {xmlns: Strophe.NS.BIND})
- .tree());
- }
- }
-
- return false;
- },
-
- /** PrivateFunction: _sasl_bind_cb
- * _Private_ handler for binding result and session start.
- *
- * Parameters:
- * (XMLElement) elem - The matching stanza.
- *
- * Returns:
- * false to remove the handler.
- */
- _sasl_bind_cb: function (elem)
- {
- if (elem.getAttribute("type") == "error") {
- Strophe.info("SASL binding failed.");
- this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
- return false;
- }
-
- // TODO - need to grab errors
- var bind = elem.getElementsByTagName("bind");
- var jidNode;
- if (bind.length > 0) {
- // Grab jid
- jidNode = bind[0].getElementsByTagName("jid");
- if (jidNode.length > 0) {
- this.jid = Strophe.getText(jidNode[0]);
-
- if (this.do_session) {
- this._addSysHandler(this._sasl_session_cb.bind(this),
- null, null, null, "_session_auth_2");
-
- this.send($iq({type: "set", id: "_session_auth_2"})
- .c('session', {xmlns: Strophe.NS.SESSION})
- .tree());
- } else {
- this.authenticated = true;
- this._changeConnectStatus(Strophe.Status.CONNECTED, null);
- }
- }
- } else {
- Strophe.info("SASL binding failed.");
- this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
- return false;
- }
- },
-
- /** PrivateFunction: _sasl_session_cb
- * _Private_ handler to finish successful SASL connection.
- *
- * This sets Connection.authenticated to true on success, which
- * starts the processing of user handlers.
- *
- * Parameters:
- * (XMLElement) elem - The matching stanza.
- *
- * Returns:
- * false to remove the handler.
- */
- _sasl_session_cb: function (elem)
- {
- if (elem.getAttribute("type") == "result") {
- this.authenticated = true;
- this._changeConnectStatus(Strophe.Status.CONNECTED, null);
- } else if (elem.getAttribute("type") == "error") {
- Strophe.info("Session creation failed.");
- this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
- return false;
- }
-
- return false;
- },
-
- /** PrivateFunction: _sasl_failure_cb
- * _Private_ handler for SASL authentication failure.
- *
- * Parameters:
- * (XMLElement) elem - The matching stanza.
- *
- * Returns:
- * false to remove the handler.
- */
- _sasl_failure_cb: function (elem)
- {
- // delete unneeded handlers
- if (this._sasl_success_handler) {
- this.deleteHandler(this._sasl_success_handler);
- this._sasl_success_handler = null;
- }
- if (this._sasl_challenge_handler) {
- this.deleteHandler(this._sasl_challenge_handler);
- this._sasl_challenge_handler = null;
- }
-
- this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
- return false;
- },
-
- /** PrivateFunction: _auth2_cb
- * _Private_ handler to finish legacy authentication.
- *
- * This handler is called when the result from the jabber:iq:auth
- * <iq/> stanza is returned.
- *
- * Parameters:
- * (XMLElement) elem - The stanza that triggered the callback.
- *
- * Returns:
- * false to remove the handler.
- */
- _auth2_cb: function (elem)
- {
- if (elem.getAttribute("type") == "result") {
- this.authenticated = true;
- this._changeConnectStatus(Strophe.Status.CONNECTED, null);
- } else if (elem.getAttribute("type") == "error") {
- this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
- this.disconnect();
- }
-
- return false;
- },
-
- /** PrivateFunction: _addSysTimedHandler
- * _Private_ function to add a system level timed handler.
- *
- * This function is used to add a Strophe.TimedHandler for the
- * library code. System timed handlers are allowed to run before
- * authentication is complete.
- *
- * Parameters:
- * (Integer) period - The period of the handler.
- * (Function) handler - The callback function.
- */
- _addSysTimedHandler: function (period, handler)
- {
- var thand = new Strophe.TimedHandler(period, handler);
- thand.user = false;
- this.addTimeds.push(thand);
- return thand;
- },
-
- /** PrivateFunction: _addSysHandler
- * _Private_ function to add a system level stanza handler.
- *
- * This function is used to add a Strophe.Handler for the
- * library code. System stanza handlers are allowed to run before
- * authentication is complete.
- *
- * Parameters:
- * (Function) handler - The callback function.
- * (String) ns - The namespace to match.
- * (String) name - The stanza name to match.
- * (String) type - The stanza type attribute to match.
- * (String) id - The stanza id attribute to match.
- */
- _addSysHandler: function (handler, ns, name, type, id)
- {
- var hand = new Strophe.Handler(handler, ns, name, type, id);
- hand.user = false;
- this.addHandlers.push(hand);
- return hand;
- },
-
- /** PrivateFunction: _onDisconnectTimeout
- * _Private_ timeout handler for handling non-graceful disconnection.
- *
- * If the graceful disconnect process does not complete within the
- * time allotted, this handler finishes the disconnect anyway.
- *
- * Returns:
- * false to remove the handler.
- */
- _onDisconnectTimeout: function ()
- {
- Strophe.info("_onDisconnectTimeout was called");
-
- // cancel all remaining requests and clear the queue
- var req;
- while (this._requests.length > 0) {
- req = this._requests.pop();
- req.abort = true;
- req.xhr.abort();
- // jslint complains, but this is fine. setting to empty func
- // is necessary for IE6
- req.xhr.onreadystatechange = function () {};
- }
-
- // actually disconnect
- this._doDisconnect();
-
- return false;
- },
-
- /** PrivateFunction: _onIdle
- * _Private_ handler to process events during idle cycle.
- *
- * This handler is called every 100ms to fire timed handlers that
- * are ready and keep poll requests going.
- */
- _onIdle: function ()
- {
- var i, thand, since, newList;
-
- // add timed handlers scheduled for addition
- // NOTE: we add before remove in the case a timed handler is
- // added and then deleted before the next _onIdle() call.
- while (this.addTimeds.length > 0) {
- this.timedHandlers.push(this.addTimeds.pop());
- }
-
- // remove timed handlers that have been scheduled for deletion
- while (this.removeTimeds.length > 0) {
- thand = this.removeTimeds.pop();
- i = this.timedHandlers.indexOf(thand);
- if (i >= 0) {
- this.timedHandlers.splice(i, 1);
- }
- }
-
- // call ready timed handlers
- var now = new Date().getTime();
- newList = [];
- for (i = 0; i < this.timedHandlers.length; i++) {
- thand = this.timedHandlers[i];
- if (this.authenticated || !thand.user) {
- since = thand.lastCalled + thand.period;
- if (since - now <= 0) {
- if (thand.run()) {
- newList.push(thand);
- }
- } else {
- newList.push(thand);
- }
- }
- }
- this.timedHandlers = newList;
-
- var body, time_elapsed;
-
- // if no requests are in progress, poll
- if (this.authenticated && this._requests.length === 0 &&
- this._data.length === 0 && !this.disconnecting) {
- Strophe.info("no requests during idle cycle, sending " +
- "blank request");
- this._data.push(null);
- }
-
- if (this._requests.length < 2 && this._data.length > 0 &&
- !this.paused) {
- body = this._buildBody();
- for (i = 0; i < this._data.length; i++) {
- if (this._data[i] !== null) {
- if (this._data[i] === "restart") {
- body.attrs({
- to: this.domain,
- "xml:lang": "en",
- "xmpp:restart": "true",
- "xmlns:xmpp": Strophe.NS.BOSH
- });
- } else {
- body.cnode(this._data[i]).up();
- }
- }
- }
- delete this._data;
- this._data = [];
- this._requests.push(
- new Strophe.Request(body.tree(),
- this._onRequestStateChange.bind(
- this, this._dataRecv.bind(this)),
- body.tree().getAttribute("rid")));
- this._processRequest(this._requests.length - 1);
- }
-
- if (this._requests.length > 0) {
- time_elapsed = this._requests[0].age();
- if (this._requests[0].dead !== null) {
- if (this._requests[0].timeDead() >
- Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {
- this._throttledRequestHandler();
- }
- }
-
- if (time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait)) {
- Strophe.warn("Request " +
- this._requests[0].id +
- " timed out, over " + Math.floor(Strophe.TIMEOUT * this.wait) +
- " seconds since last activity");
- this._throttledRequestHandler();
- }
- }
-
- // reactivate the timer
- clearTimeout(this._idleTimeout);
- this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
- }
-};
-
-if (callback) {
- callback(Strophe, $build, $msg, $iq, $pres);
-}
-
-})(function () {
- window.Strophe = arguments[0];
- window.$build = arguments[1];
- window.$msg = arguments[2];
- window.$iq = arguments[3];
- window.$pres = arguments[4];
-});
diff --git a/contrib/jitsimeetbridge/unjingle/unjingle.js b/contrib/jitsimeetbridge/unjingle/unjingle.js
deleted file mode 100644
index 3dfe759914..0000000000
--- a/contrib/jitsimeetbridge/unjingle/unjingle.js
+++ /dev/null
@@ -1,48 +0,0 @@
-var strophe = require("./strophe/strophe.js").Strophe;
-
-var Strophe = strophe.Strophe;
-var $iq = strophe.$iq;
-var $msg = strophe.$msg;
-var $build = strophe.$build;
-var $pres = strophe.$pres;
-
-var jsdom = require("jsdom");
-var window = jsdom.jsdom().parentWindow;
-var $ = require('jquery')(window);
-
-var stropheJingle = require("./strophe.jingle.sdp.js");
-
-
-var input = '';
-
-process.stdin.on('readable', function() {
- var chunk = process.stdin.read();
- if (chunk !== null) {
- input += chunk;
- }
-});
-
-process.stdin.on('end', function() {
- if (process.argv[2] == '--jingle') {
- var elem = $(input);
- // app does:
- // sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
- //console.log(elem.find('>content'));
- var sdp = new stropheJingle.SDP('');
- sdp.fromJingle(elem);
- console.log(sdp.raw);
- } else if (process.argv[2] == '--sdp') {
- var sdp = new stropheJingle.SDP(input);
- var accept = $iq({to: '%(tojid)s',
- type: 'set'})
- .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
- //action: 'session-accept',
- action: '%(action)s',
- initiator: '%(initiator)s',
- responder: '%(responder)s',
- sid: '%(sid)s' });
- sdp.toJingle(accept, 'responder');
- console.log(Strophe.serialize(accept));
- }
-});
-
diff --git a/debian/changelog b/debian/changelog
index b6a51d6903..b1e61e7c8a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+matrix-synapse-py3 (1.60.0~rc2+nmu1) UNRELEASED; urgency=medium
+
+ * Non-maintainer upload.
+ * Remove unused `jitsimeetbridge` experiment from `contrib` directory.
+
+ -- Synapse Packaging team <packages@matrix.org> Sun, 29 May 2022 14:44:45 +0100
+
matrix-synapse-py3 (1.60.0~rc2) stable; urgency=medium
* New Synapse release 1.60.0rc2.
diff --git a/debian/copyright b/debian/copyright
index 95c21ea12a..902b18fa41 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -22,29 +22,6 @@ Files: synapse/config/repository.py
Copyright: 2014-2015, matrix.org
License: Apache-2.0
-Files: contrib/jitsimeetbridge/unjingle/strophe/base64.js
-Copyright: Public Domain (Tyler Akins http://rumkin.com)
-License: public-domain
- This code was written by Tyler Akins and has been placed in the
- public domain. It would be nice if you left this header intact.
- Base64 code from Tyler Akins -- http://rumkin.com
-
-Files: contrib/jitsimeetbridge/unjingle/strophe/md5.js
-Copyright: 1999-2002, Paul Johnston & Contributors
-License: BSD-3-clause
-
-Files: contrib/jitsimeetbridge/unjingle/strophe/strophe.js
-Copyright: 2006-2008, OGG, LLC
-License: Expat
-
-Files: contrib/jitsimeetbridge/unjingle/strophe/XMLHttpRequest.js
-Copyright: 2010 passive.ly LLC
-License: Expat
-
-Files: contrib/jitsimeetbridge/unjingle/*.js
-Copyright: 2014 Jitsi
-License: Apache-2.0
-
Files: debian/*
Copyright: 2016-2017, Erik Johnston <erik@matrix.org>
2017, Rahul De <rahulde@swecha.net>
|