Mercurial > eldonilo > lightstring
changeset 13:9aeb0750b9d1
fix an error with the stream builder
author | Sonny Piers <sonny.piers@gmail.com> |
---|---|
date | Sun, 15 Jan 2012 15:23:51 +0100 |
parents | 9fbd0e3678b5 |
children | 033a2496fb0f |
files | lightstring.js |
diffstat | 1 files changed, 101 insertions(+), 88 deletions(-) [+] |
line wrap: on
line diff
--- a/lightstring.js +++ b/lightstring.js @@ -23,14 +23,28 @@ var Lightstring = { /** * @namespace Holds XMPP namespaces. - * */ - NS: {}, + NS: { + stream: 'http://etherx.jabber.org/streams', + jabberClient: 'jabber:client' + }, /** * @namespace Holds XMPP stanza builders. - * */ - stanza: {}, + stanza: { + stream: { + open: function(aService) { + //FIXME no ending "/" - node-xmpp-bosh bug + return "<stream:stream to='" + aService + "'\ + xmlns='" + Lightstring.NS.jabberClient + "'\ + xmlns:stream='" + Lightstring.NS.stream + "'\ + version='1.0'/>"; + }, + close: function() { + return '</stream:stream>'; + } + } + }, /** * @private */ @@ -41,20 +55,20 @@ var Lightstring = { serializer: new XMLSerializer(), /** * @function Transforms a XML string to a DOM object. - * @param {String} aString XML string - * @returns {Object} Domified XML. + * @param {String} aString XML string. + * @return {Object} Domified XML. */ xml2dom: function(aString) { return this.parser.parseFromString(aString, 'text/xml').documentElement; }, /** * @function Transforms a DOM object to a XML string. - * @param {Object} aString DOM object - * @returns {String} Stringified DOM. + * @param {Object} aString DOM object. + * @return {String} Stringified DOM. */ dom2xml: function(aElement) { return this.serializer.serializeToString(aElement); - }, + } }; /** @@ -62,14 +76,14 @@ var Lightstring = { * @param {String} [aService] The Websocket service URL. * @memberOf Lightstring */ -Lightstring.Connection = function (aService) { - if(aService) +Lightstring.Connection = function(aService) { + if (aService) this.service = aService; this.handlers = {}; this.iqid = 1024; this.getNewId = function() { this.iqid++; - return 'sendiq:'+this.iqid; + return 'sendiq:' + this.iqid; }; /** * @function Create and open a websocket then go though the XMPP authentification process. @@ -78,47 +92,44 @@ Lightstring.Connection = function (aServ */ this.connect = function(aJid, aPassword) { this.emit('connecting'); - if(aJid) + if (aJid) this.jid = aJid; - if(this.jid) { - this.domain = this.jid.split('@')[1]; + if (this.jid) { + this.host = this.jid.split('@')[1]; this.node = this.jid.split('@')[0]; this.resource = this.jid.split('/')[1]; } - if(aPassword) + if (aPassword) this.password = aPassword; - if(!this.jid) - throw "Lightstring: Connection.jid is undefined."; - if(!this.password) - throw "Lightstring: Connection.password is undefined."; - if(!this.service) - throw "Lightstring: Connection.service is undefined."; + if (!this.jid) + throw 'Lightstring: Connection.jid is undefined.'; + if (!this.password) + throw 'Lightstring: Connection.password is undefined.'; + if (!this.service) + throw 'Lightstring: Connection.service is undefined.'; //"Bug 695635 - tracking bug: unprefix WebSockets" https://bugzil.la/695635 try { this.socket = new WebSocket(this.service, 'xmpp'); } - catch(error) { + catch (error) { this.socket = new MozWebSocket(this.service, 'xmpp'); } var that = this; this.socket.addEventListener('open', function() { - if(this.protocol !== 'xmpp') - throw "Lightstring: The server located at "+that.service+" is not XMPP aware."; - //FIXME no ending "/" - node-xmpp-bosh bug - var stream = - "<stream:stream to='"+that.domain+"'\ - xmlns='jabber:client'\ - xmlns:stream='http://etherx.jabber.org/streams'\ - version='1.0'/>"; - - that.socket.send(stream) + if (this.protocol !== 'xmpp') + throw 'Lightstring: The server located at '+ that.service + ' is not XMPP aware.'; + + var stream = Lightstring.stanza.stream.open(that.host); + + that.socket.send(stream); that.emit('XMLOutput', stream); }); this.socket.addEventListener('error', function(e) { that.emit('error', e.data); + console.log(e.data); }); this.socket.addEventListener('close', function(e) { that.emit('disconnected', e.data); @@ -129,70 +140,72 @@ Lightstring.Connection = function (aServ that.emit('DOMInput', elm); that.emit(elm.tagName, elm); - if(elm.tagName === 'iq') + if (elm.tagName === 'iq') that.emit(elm.getAttribute('id'), elm); }); }; /** * @function Send a message. * @param {String|Object} aStanza The message to send. - * @param {Function} [aCallback] A callback that will be called when the answer will answer. + * @param {Function} [aCallback] Executed on answer. (stanza must be iq) */ this.send = function(aStanza, aCallback) { - if(typeof aStanza === 'string') { + if (typeof aStanza === 'string') { var str = aStanza; var elm = Lightstring.xml2dom(str); } - else if(aStanza instanceof Element) { + else if (aStanza instanceof Element) { var elm = aStanza; var str = this.dom2xml(elm); } else { that.emit('error', 'Unsupported data type.'); } - - - if(elm.tagName === 'iq') { + + + if (elm.tagName === 'iq') { var id = elm.getAttribute('id'); - if(!id) { - elm.setAttribute('id', this.getNewId()) - str = Lightstring.dom2xml(elm) + if (!id) { + elm.setAttribute('id', this.getNewId()); + str = Lightstring.dom2xml(elm); } - if(aCallback) + if (aCallback) this.on(elm.getAttribute('id'), aCallback); } - else if(aCallback) { + else if (aCallback) { that.emit('warning', 'Callback can\'t be called with non-iq stanza.'); } - - + + this.socket.send(str); this.emit('XMLOutput', str); this.emit('DOMOutput', elm); }; /** - * @function Close the XMPP stream and the socket. + * @function Closes the XMPP stream and the socket. */ this.disconnect = function() { this.emit('disconnecting'); - this.send('</stream:stream>'); + var stream = Lighstring.stanza.stream.close(); + this.send(stream); + that.emit('XMLOutput', stream); this.socket.close(); }; /** - * @function Emit an event. + * @function Emits an event. * @param {String} aName The event name. * @param {Function|Array|Object} [aData] Data about the event. */ this.emit = function(aName, aData) { var handlers = this.handlers[aName]; - if(!handlers) + if (!handlers) return; //FIXME Better idea than passing the context as argument? - for(var i=0; i<handlers.length; i++) + for (var i = 0; i < handlers.length; i++) handlers[i](aData, this); - if(aName.match('sendiq:')) + if (aName.match('sendiq:')) delete this.handlers[aName]; }; /** @@ -201,7 +214,7 @@ Lightstring.Connection = function (aServ * @param {Function} aCallback The callback to call when the event is emitted. */ this.on = function(aName, callback) { - if(!this.handlers[aName]) + if (!this.handlers[aName]) this.handlers[aName] = []; this.handlers[aName].push(callback); }; @@ -214,30 +227,30 @@ Lightstring.Connection = function (aServ this.on('stream:features', function(stanza, that) { var nodes = stanza.querySelectorAll('mechanism'); //SASL/Auth features - if(nodes.length > 0) { + if (nodes.length > 0) { that.emit('mechanisms', stanza); var mechanisms = {}; - for(var i=0; i<nodes.length; i++) + for (var i = 0; i < nodes.length; i++) mechanisms[nodes[i].textContent] = true; - - - //FIXME support SCRAM-SHA1 && allow specify method preferences - if('DIGEST-MD5' in mechanisms) + + + //FIXME support SCRAM-SHA1 && allow specify method preferences + if ('DIGEST-MD5' in mechanisms) that.send( "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'\ mechanism='DIGEST-MD5'/>" ); - else if('PLAIN' in mechanisms) { + else if ('PLAIN' in mechanisms) { var token = btoa( that.jid + - "\u0000" + + '\u0000' + that.jid.node + - "\u0000" + + '\u0000' + that.password ); that.send( "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'\ - mechanism='PLAIN'>"+token+"</auth>" + mechanism='PLAIN'>" + token + '</auth>' ); } } @@ -264,7 +277,7 @@ Lightstring.Connection = function (aServ }); this.on('success', function(stanza, that) { that.send( - "<stream:stream to='"+that.domain+"'\ + "<stream:stream to='" + that.domain + "'\ xmlns='jabber:client'\ xmlns:stream='http://etherx.jabber.org/streams'\ version='1.0' />" @@ -275,52 +288,52 @@ Lightstring.Connection = function (aServ }); this.on('challenge', function(stanza, that) { //FIXME this is mostly Strophe code - + function _quote(str) { - return '"' + str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"'; + return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; }; - + var challenge = atob(stanza.textContent); - + var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/; var cnonce = MD5.hexdigest(Math.random() * 1234567890); - var realm = ""; + var realm = ''; var host = null; - var nonce = ""; - var qop = ""; + var nonce = ''; + var qop = ''; var matches; while (challenge.match(attribMatch)) { matches = challenge.match(attribMatch); - challenge = challenge.replace(matches[0], ""); - matches[2] = matches[2].replace(/^"(.+)"$/, "$1"); + challenge = challenge.replace(matches[0], ''); + matches[2] = matches[2].replace(/^"(.+)"$/, '$1'); switch (matches[1]) { - case "realm": + case 'realm': realm = matches[2]; break; - case "nonce": + case 'nonce': nonce = matches[2]; break; - case "qop": + case 'qop': qop = matches[2]; break; - case "host": + case 'host': host = matches[2]; break; } } - var digest_uri = "xmpp/" + that.domain; + var digest_uri = 'xmpp/' + that.domain; if (host !== null) { - digest_uri = digest_uri + "/" + host; + digest_uri = digest_uri + '/' + host; } var A1 = MD5.hash(that.node + - ":" + realm + ":" + that.password) + - ":" + nonce + ":" + cnonce; + ':' + realm + ':' + that.password) + + ':' + nonce + ':' + cnonce; var A2 = 'AUTHENTICATE:' + digest_uri; - var responseText = ""; + var responseText = ''; responseText += 'username=' + _quote(that.node) + ','; responseText += 'realm=' + _quote(realm) + ','; responseText += 'nonce=' + _quote(nonce) + ','; @@ -329,14 +342,14 @@ Lightstring.Connection = function (aServ responseText += 'qop="auth",'; responseText += 'digest-uri=' + _quote(digest_uri) + ','; responseText += 'response=' + _quote( - MD5.hexdigest(MD5.hexdigest(A1) + ":" + - nonce + ":00000001:" + - cnonce + ":auth:" + + MD5.hexdigest(MD5.hexdigest(A1) + ':' + + nonce + ':00000001:' + + cnonce + ':auth:' + MD5.hexdigest(A2))) + ','; responseText += 'charset="utf-8"'; that.send( "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" - +btoa(responseText)+ - "</response>"); + + btoa(responseText) + + '</response>'); }); };