# HG changeset patch # User Sonny Piers # Date 1324236393 -3600 # Node ID 96087680669f6c7ca089ae906978e55f07afe01d # Parent 96898e3812a52e8ce6962f18327724069e07ac08 Delete base64.js since I don't care about IE support for the moment. diff --git a/XMPP.js b/XMPP.js deleted file mode 100644 --- a/XMPP.js +++ /dev/null @@ -1,221 +0,0 @@ -'use strict'; - -var Lightstring = { - NS: {}, - stanza: {}, -}; - -Lightstring.Connection = function (aURL) { - var parser = new DOMParser(); - var serializer = new XMLSerializer(); - this.handlers = {}; - this.iqid = 1024; - this.getNewId = function() { - this.iqid++; - return 'sendiq:'+this.iqid; - }; - this.parse = function(str) { - return parser.parseFromString(str, 'text/xml').documentElement; - }; - this.serialize = function(elm) { - return serializer.serializeToString(elm); - }; - this.connect = function(jid, password) { - this.domain = jid.split('@')[1]; - this.node = jid.split('@')[0]; - this.jid = jid; - this.password = password; - if(typeof WebSocket === 'undefined') - this.socket = new MozWebSocket(aURL); - else - this.socket = new WebSocket(aURL); - - var that = this; - this.socket.addEventListener('open', function() { - that.emit('open'); - that.send(""); - }); - this.socket.addEventListener('error', function(err) { - that.emit('error'); - }); - this.socket.addEventListener('close', function(close) { - that.emit('close'); - that.emit('disconnected'); - }); - this.socket.addEventListener('message', function(e) { - that.emit('XMLInput', e.data); - var elm = that.parse(e.data); - that.emit('DOMInput', elm); - that.emit(elm.tagName, elm); - - if((elm.tagName === 'iq')) - that.emit(elm.getAttribute('id'), elm); - }); - }; - - - - this.send = function(stanza, callback) { - //FIXME support for E4X - //~ if(typeof stanza === 'xml') { - //~ stanza = stanza.toXMLString(); - //~ } - if(stanza.cnode) { - stanza = stanza.toString(); - //~ console.log(typeof stanza); - } - if(typeof stanza === 'string') { - var str = stanza; - var elm = this.parse(stanza); - } - else { - var elm = stanza; - var str = this.serialize(stanza); - } - - - if(elm.tagName === 'iq') { - var id = elm.getAttribute('id'); - if(!id) { - elm.setAttribute('id', this.getNewId()) - str = this.serialize(elm) - } - if(callback) this.on(elm.getAttribute('id'), callback); - } - - - this.socket.send(str); - this.emit('XMLOutput', str); - this.emit('DOMOutput', elm); - }; - this.disconnect = function() { - this.send(''); - this.emit('disconnected'); - this.socket.close(); - }; - //FIXME Callbacks sucks, better idea? - this.emit = function(name, data) { - var handlers = this.handlers[name]; - if(!handlers) - return; - - //FIXME Better idea than passing the context as argument? - for(var i=0; i 0) { - that.emit('mechanisms', stanza); - var mechanisms = {}; - for(var i=0; i"); - else if('PLAIN' in mechanisms) { - var token = btoa(that.jid + "\u0000" + that.jid.split('@')[0] + "\u0000" + that.password); - that.send(""+token+""); - } - } - //XMPP features - else { - that.emit('features', stanza); - //Bind http://xmpp.org/rfcs/rfc3920.html#bind - that.send("", function() { - //Session http://xmpp.org/rfcs/rfc3921.html#session - that.send("", function() { - that.emit('connected'); - }); - }); - } - }); - //Internal - this.on('success', function(stanza, that) { - that.send(""); - }); - //Internal - this.on('challenge', function(stanza, that) { - //FIXME this is mostly Strophe code - - function _quote(str) { - 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 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/" + that.domain; - if (host !== null) { - digest_uri = digest_uri + "/" + host; - } - - var A1 = MD5.hash(that.node + - ":" + realm + ":" + that.password) + - ":" + nonce + ":" + cnonce; - var A2 = 'AUTHENTICATE:' + digest_uri; - - var responseText = ""; - responseText += 'username=' + _quote(that.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"'; - - that.send(""+btoa(responseText)+""); - }); -}; diff --git a/base64.js b/base64.js deleted file mode 100644 --- a/base64.js +++ /dev/null @@ -1,80 +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; -})(); diff --git a/lightstring.js b/lightstring.js new file mode 100644 --- /dev/null +++ b/lightstring.js @@ -0,0 +1,221 @@ +'use strict'; + +var Lightstring = { + NS: {}, + stanza: {}, +}; + +Lightstring.Connection = function (aURL) { + var parser = new DOMParser(); + var serializer = new XMLSerializer(); + this.handlers = {}; + this.iqid = 1024; + this.getNewId = function() { + this.iqid++; + return 'sendiq:'+this.iqid; + }; + this.parse = function(str) { + return parser.parseFromString(str, 'text/xml').documentElement; + }; + this.serialize = function(elm) { + return serializer.serializeToString(elm); + }; + this.connect = function(jid, password) { + this.domain = jid.split('@')[1]; + this.node = jid.split('@')[0]; + this.jid = jid; + this.password = password; + if(typeof WebSocket === 'undefined') + this.socket = new MozWebSocket(aURL); + else + this.socket = new WebSocket(aURL); + + var that = this; + this.socket.addEventListener('open', function() { + that.emit('open'); + that.send(""); + }); + this.socket.addEventListener('error', function(err) { + that.emit('error'); + }); + this.socket.addEventListener('close', function(close) { + that.emit('close'); + that.emit('disconnected'); + }); + this.socket.addEventListener('message', function(e) { + that.emit('XMLInput', e.data); + var elm = that.parse(e.data); + that.emit('DOMInput', elm); + that.emit(elm.tagName, elm); + + if((elm.tagName === 'iq')) + that.emit(elm.getAttribute('id'), elm); + }); + }; + + + + this.send = function(stanza, callback) { + //FIXME support for E4X + //~ if(typeof stanza === 'xml') { + //~ stanza = stanza.toXMLString(); + //~ } + if(stanza.cnode) { + stanza = stanza.toString(); + //~ console.log(typeof stanza); + } + if(typeof stanza === 'string') { + var str = stanza; + var elm = this.parse(stanza); + } + else { + var elm = stanza; + var str = this.serialize(stanza); + } + + + if(elm.tagName === 'iq') { + var id = elm.getAttribute('id'); + if(!id) { + elm.setAttribute('id', this.getNewId()) + str = this.serialize(elm) + } + if(callback) this.on(elm.getAttribute('id'), callback); + } + + + this.socket.send(str); + this.emit('XMLOutput', str); + this.emit('DOMOutput', elm); + }; + this.disconnect = function() { + this.send(''); + this.emit('disconnected'); + this.socket.close(); + }; + //FIXME Callbacks sucks, better idea? + this.emit = function(name, data) { + var handlers = this.handlers[name]; + if(!handlers) + return; + + //FIXME Better idea than passing the context as argument? + for(var i=0; i 0) { + that.emit('mechanisms', stanza); + var mechanisms = {}; + for(var i=0; i"); + else if('PLAIN' in mechanisms) { + var token = btoa(that.jid + "\u0000" + that.jid.split('@')[0] + "\u0000" + that.password); + that.send(""+token+""); + } + } + //XMPP features + else { + that.emit('features', stanza); + //Bind http://xmpp.org/rfcs/rfc3920.html#bind + that.send("", function() { + //Session http://xmpp.org/rfcs/rfc3921.html#session + that.send("", function() { + that.emit('connected'); + }); + }); + } + }); + //Internal + this.on('success', function(stanza, that) { + that.send(""); + }); + //Internal + this.on('challenge', function(stanza, that) { + //FIXME this is mostly Strophe code + + function _quote(str) { + 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 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/" + that.domain; + if (host !== null) { + digest_uri = digest_uri + "/" + host; + } + + var A1 = MD5.hash(that.node + + ":" + realm + ":" + that.password) + + ":" + nonce + ":" + cnonce; + var A2 = 'AUTHENTICATE:' + digest_uri; + + var responseText = ""; + responseText += 'username=' + _quote(that.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"'; + + that.send(""+btoa(responseText)+""); + }); +};