# HG changeset patch # User Sonny Piers # Date 1328120681 -3600 # Node ID 20da4fb6797735a5f868cfdbeb5ef5f429ddb5bb # Parent b1e75cdbb0ad6affb28531883294bedf030b7eb3 Auth PLAIN as plugin. Several fixes. diff --git a/lightstring.js b/lightstring.js --- a/lightstring.js +++ b/lightstring.js @@ -126,145 +126,6 @@ Lightstring.Connection = function(aServi this.service = aService; this.handlers = {}; this.callbacks = {}; - this.on('stream:features', function(stanza) { - var nodes = stanza.DOM.querySelectorAll('mechanism'); - //SASL/Auth features - if (nodes.length > 0) { - this.emit('mechanisms', stanza); - var mechanisms = {}; - 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) - this.send( - "" - ); - else if ('PLAIN' in mechanisms) { - var token = btoa( - this.jid + - '\u0000' + - this.jid.node + - '\u0000' + - this.password - ); - this.send( - "" + token + "" - ); - } - } - //XMPP features - else { - this.emit('features', stanza); - var that = this; - //Bind http://xmpp.org/rfcs/rfc3920.html#bind - var bind = - "" + - "" + - (this.jid.resource? "" + this.jid.resource + "": "") + - "" + - ""; - this.send( - bind, - //Success - function(stanza) { - //Session http://xmpp.org/rfcs/rfc3921.html#session - this.jid = new Lightstring.JID(stanza.DOM.textContent); - that.send( - "" + - "" + - "", - function() { - that.emit('connected'); - } - ); - }, - //Error - function(stanza) { - //TODO: Error? - } - ); - } - }); - this.on('success', function(stanza) { - this.send( - "" - ); - }); - this.on('failure', function(stanza) { - this.emit('conn-error', stanza.DOM.firstChild.tagName); - }); - this.on('challenge', function(stanza) { - //FIXME this is mostly Strophe code - - function _quote(str) { - return '"' + str.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'; - }; - - var challenge = atob(stanza.DOM.textContent); - - var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/; - - var cnonce = MD5.hexdigest(Math.random() * 1234567890); - var realm = ''; - var host = null; - 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'); - 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.jid.domain; - if (host !== null) - digest_uri = digest_uri + '/' + host; - var A1 = MD5.hash(this.jid.node + - ':' + realm + ':' + this.password) + - ':' + nonce + ':' + cnonce; - var A2 = 'AUTHENTICATE:' + digest_uri; - - var responseText = ''; - responseText += 'username=' + _quote(this.jid.node) + ','; - responseText += 'realm=' + _quote(realm) + ','; - responseText += 'nonce=' + _quote(nonce) + ','; - responseText += 'cnonce=' + _quote(cnonce) + ','; - responseText += 'nc="00000001",'; - responseText += 'qop="auth",'; - responseText += 'digest-uri=' + _quote(digest_uri) + ','; - responseText += 'response=' + _quote( - MD5.hexdigest(MD5.hexdigest(A1) + ':' + - nonce + ':00000001:' + - cnonce + ':auth:' + - MD5.hexdigest(A2))) + ','; - responseText += 'charset="utf-8"'; - this.send( - "" + - btoa(responseText) + - ""); - }); }; Lightstring.Connection.prototype = { /** @@ -319,13 +180,47 @@ Lightstring.Connection.prototype = { //TODO node-xmpp-bosh sends a self-closing stream:stream tag; it is wrong! that.emit('input', stanza); - + if(!stanza.DOM) return; - that.emit(stanza.DOM.tagName, stanza); + + var name = stanza.DOM.localName; + if (name === 'features') { + //SASL mechanisms + if (stanza.DOM.firstChild.localName === 'mechanisms') { + stanza.mechanisms = []; + var nodes = stanza.DOM.querySelectorAll('mechanism'); + for (var i = 0; i < nodes.length; i++) + stanza.mechanisms.push(nodes[i].textContent); + that.emit('mechanisms', stanza); + } + //XMPP features + else if (stanza.DOM.firstChild.localName === 'c') { + //TODO: stanza.features + that.emit('features', stanza); + } + } + else if (name === 'challenge') { - if (stanza.DOM.tagName === 'iq') { + + } + else if (name === 'response') { + + + } + else if (name === 'success') { + that.emit('success', stanza); + } + else if(name === 'stream') { + + + } + + + + //Iq callbacks + else if (name === 'iq') { var payload = stanza.DOM.firstChild; if (payload) that.emit('iq/' + payload.namespaceURI + ':' + payload.localName, stanza); @@ -345,12 +240,6 @@ Lightstring.Connection.prototype = { callback.error(stanza); delete that.callbacks[id]; - - //TODO: really needed? - } else if (stanza.DOM.tagName === 'message') { - var payloads = stanza.DOM.children; - for (var i = 0; i < payloads.length; i++) - that.emit('message/' + payloads[i].namespaceURI + ':' + payloads[i].localName, stanza); } }); }, @@ -424,7 +313,7 @@ Lightstring.Connection.prototype = { //Methods this[name] = {}; - for (var method in plugins.methods) + for (var method in plugin.methods) this[name][method].bind(this); if (plugin.init) @@ -441,7 +330,7 @@ Lightstring.Connection.prototype = { if (!handlers) return; - if (aData.localName !== 'iq') { + if (aData && aData.DOM && aData.DOM.localName !== 'iq') { for (var i = 0; i < handlers.length; i++) handlers[i].call(this, aData); diff --git a/plugins/PLAIN.js b/plugins/PLAIN.js new file mode 100644 --- /dev/null +++ b/plugins/PLAIN.js @@ -0,0 +1,77 @@ +'use strict'; + +/** + Copyright (c) 2012, Sonny Piers + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +Lightstring.plugins['PLAIN'] = { + handlers: { + 'mechanisms': function (stanza) { + if(stanza.mechanisms.indexOf('PLAIN') === -1) + return; + + var token = btoa( + this.jid + + '\u0000' + + this.jid.node + + '\u0000' + + this.password + ); + this.send( + "" + token + "" + ); + }, + //TODO twice success event + 'success': function (stanza) { + this.send( + "" + ); + }, + 'features': function (stanza) { + var that = this; + //TODO check if bind supported + var bind = + "" + + "" + + (this.jid.resource? "" + this.jid.resource + "": "") + + "" + + ""; + this.send( + bind, + //Success + function(stanza) { + //Session http://xmpp.org/rfcs/rfc3921.html#session + that.jid = new Lightstring.JID(stanza.DOM.textContent); + that.send( + "" + + "" + + "", + function() { + that.emit('connected'); + } + ); + }, + //Error + function(stanza) { + //TODO: Error? + } + ); + } + } +};