# HG changeset patch # User Sonny Piers # Date 1327710420 -3600 # Node ID b6e4bc19ff5afe43e35c121024466cc6383c6421 # Parent fc577e5b2f4aec5482385b63346376b7c021db78# Parent 82bffc4a07a9604b43fe025747063969635416c8 undoing diff --git a/jid.js b/jid.js new file mode 100644 --- /dev/null +++ b/jid.js @@ -0,0 +1,114 @@ +'use strict'; + +/** + Copyright (c) 2012, Emmanuel Gil Peyrot + + 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. +*/ + + +/** + * @constructor Creates a new JID object. + * @param {String} [aJID] The host, bare or full JID. + * @memberOf Lightstring + */ +Lightstring.JID = function(aJID) { + this.node = null; + this.domain = null; + this.resource = null; + + if (aJID) + this.full = aJID; + + //TODO: use a stringprep library to validate the input. +}; + +Lightstring.JID.prototype = { + toString: function() { + return this.full; + }, + + equals: function(aJID) { + if (!(aJID instanceof Lightstring.JID)) + aJID = new Lightstring.JID(aJID); + + return (this.node === aJID.node && + this.domain === aJID.domain && + this.resource === aJID.resource) + }, + + get bare() { + if (!this.domain) + return null; + + if (this.node) + return this.node + '@' + this.domain; + + return this.domain; + }, + + set bare(aJID) { + if (!aJID) + return; + + var s = aJID.indexOf('/'); + if (s != -1) + aJID = aJID.substring(0, s); + + s = aJID.indexOf('@'); + if (s == -1) { + this.node = null; + this.domain = aJID; + } else { + this.node = aJID.substring(0, s); + this.domain = aJID.substring(s+1); + } + }, + + get full() { + if (!this.domain) + return null; + + var full = this.domain; + + if (this.node) + full = this.node + '@' + full; + + if (this.resource) + full = full + '/' + this.resource; + + return full; + }, + + set full(aJID) { + if (!aJID) + return; + + var s = aJID.indexOf('/'); + if (s == -1) + this.resource = null; + else { + this.resource = aJID.substring(s+1); + aJID = aJID.substring(0, s); + } + + s = aJID.indexOf('@'); + if (s == -1) { + this.node = null; + this.domain = aJID; + } else { + this.node = aJID.substring(0, s); + this.domain = aJID.substring(s+1); + } + } +}; diff --git a/lightstring.js b/lightstring.js --- a/lightstring.js +++ b/lightstring.js @@ -35,13 +35,13 @@ var Lightstring = { stream: { open: function(aService) { //FIXME no ending "/" - node-xmpp-bosh bug - return ""; + return ""; }, close: function() { - return ''; + return ""; } } }, @@ -98,8 +98,8 @@ Lightstring.Connection = function(aServi //FIXME support SCRAM-SHA1 && allow specify method preferences if ('DIGEST-MD5' in mechanisms) that.send( - "" + "" ); else if ('PLAIN' in mechanisms) { var token = btoa( @@ -110,8 +110,8 @@ Lightstring.Connection = function(aServi that.password ); that.send( - "" + token + '' + "" + token + "" ); } } @@ -119,29 +119,35 @@ Lightstring.Connection = function(aServi else { that.emit('features', stanza); //Bind http://xmpp.org/rfcs/rfc3920.html#bind + var bind = + "" + + "" + + (that.jid.resource? "" + that.jid.resource + "": "") + + "" + + ""; that.send( - "\ - \ - ", - function() { - //Session http://xmpp.org/rfcs/rfc3921.html#session - that.send( - "\ - \ - ", - function() { - that.emit('connected'); - } - ); - }); + bind, + function(stanza) { + //Session http://xmpp.org/rfcs/rfc3921.html#session + that.jid = new Lightstring.JID(stanza.textContent); + that.send( + "" + + "" + + "", + function() { + that.emit('connected'); + } + ); + } + ); } }); this.on('success', function(stanza, that) { that.send( - "" + "" ); }); this.on('failure', function(stanza, that) { @@ -185,17 +191,16 @@ Lightstring.Connection = function(aServi } } - var digest_uri = 'xmpp/' + that.host; - if (host !== null) { + var digest_uri = 'xmpp/' + that.jid.domain; + if (host !== null) digest_uri = digest_uri + '/' + host; - } - var A1 = MD5.hash(that.node + + var A1 = MD5.hash(that.jid.node + ':' + realm + ':' + that.password) + - ':' + nonce + ':' + cnonce; + ':' + nonce + ':' + cnonce; var A2 = 'AUTHENTICATE:' + digest_uri; var responseText = ''; - responseText += 'username=' + _quote(that.node) + ','; + responseText += 'username=' + _quote(that.jid.node) + ','; responseText += 'realm=' + _quote(realm) + ','; responseText += 'nonce=' + _quote(nonce) + ','; responseText += 'cnonce=' + _quote(cnonce) + ','; @@ -209,9 +214,9 @@ Lightstring.Connection = function(aServi MD5.hexdigest(A2))) + ','; responseText += 'charset="utf-8"'; that.send( - "" - + btoa(responseText) + - ''); + "" + + btoa(responseText) + + ""); }); }; Lightstring.Connection.prototype = { @@ -222,17 +227,11 @@ Lightstring.Connection.prototype = { */ connect: function(aJid, aPassword) { this.emit('connecting'); - if (aJid) - this.jid = aJid; - if (this.jid) { - this.host = this.jid.split('@')[1]; - this.node = this.jid.split('@')[0]; - this.resource = this.jid.split('/')[1]; - } + this.jid = new Lightstring.JID(aJid); if (aPassword) this.password = aPassword; - if (!this.jid) + if (!this.jid.bare) throw 'Lightstring: Connection.jid is undefined.'; if (!this.password) throw 'Lightstring: Connection.password is undefined.'; @@ -252,7 +251,7 @@ Lightstring.Connection.prototype = { if (this.protocol !== 'xmpp') console.error('Lightstring: The server located at '+ that.service + ' doesn\'t seems to be XMPP aware.'); - var stream = Lightstring.stanza.stream.open(that.host); + var stream = Lightstring.stanza.stream.open(that.jid.domain); that.socket.send(stream); that.emit('XMLOutput', stream); @@ -270,8 +269,17 @@ Lightstring.Connection.prototype = { that.emit('DOMInput', elm); that.emit(elm.tagName, elm); +<<<<<<< HEAD if (elm.tagName === 'iq') that.emit(elm.getAttribute('id'), elm); +======= + if (elm.tagName === 'iq') { + var payload = elm.firstChild; + if (payload) + that.emit('iq/' + payload.namespaceURI + ':' + payload.localName, elm); + that.emit(elm.getAttribute('id'), elm); //FIXME: possible attack vector. + } +>>>>>>> f6a7c0f93d154c2cd34dfdda4ab8eec808b91b34 }); }, /** @@ -303,9 +311,8 @@ Lightstring.Connection.prototype = { if (aCallback) this.on(elm.getAttribute('id'), aCallback); } - else if (aCallback) { + else if (aCallback) this.emit('warning', 'Callback can\'t be called with non-iq stanza.'); - } this.socket.send(str); diff --git a/plugins.js b/plugins.js --- a/plugins.js +++ b/plugins.js @@ -34,26 +34,26 @@ Lightstring.presence = function(aConnect ////////// Lightstring.NS.roster = 'jabber:iq:roster'; Lightstring.stanza.roster = { - 'get': function() { - return ""; - }, - add: function(aAddress, aGroups, aCustomName) { - var iq = $iq({type: 'set'}).c('query', {xmlns: Lightstring.NS.roster}).c('item', {jid: aAddress}).tree(); - if(aCustomName) iq.querySelector('item').setAttribute(aCustomName); - for (var i=0; i"; + }, + add: function(aAddress, aGroups, aCustomName) { + var iq = $iq({type: 'set'}).c('query', {xmlns: Lightstring.NS.roster}).c('item', {jid: aAddress}).tree(); + if(aCustomName) iq.querySelector('item').setAttribute(aCustomName); + for (var i=0; i"; - else - return ""; - } + 'get': function(aTo) { + if(aTo) + return ""; + else + return ""; + } }; //FIXME we should return a proper vcard, not an XMPP one Lightstring.getVcard = function(aConnection, aTo, aCallback) { - aConnection.send(Lightstring.stanza.vcard.get(aTo), function(answer, err){ - if(answer) { - var vcard = answer.querySelector('vCard'); - if(vcard) - aCallback(vcard); - } - else - aCallback(null); - }); + aConnection.send(Lightstring.stanza.vcard.get(aTo), function(answer, err){ + if(answer) { + var vcard = answer.querySelector('vCard'); + if(vcard) + aCallback(vcard); + } + else + aCallback(null); + }); } ///////// //Disco// @@ -109,56 +109,77 @@ Lightstring.getVcard = function(aConnect Lightstring.NS['disco#info'] = "http://jabber.org/protocol/disco#info"; Lightstring.NS['disco#items'] = "http://jabber.org/protocol/disco#items"; Lightstring.stanza.disco = { - items: function(aTo, aNode) { - if(aTo) - var iq = ""; - else - var iq = ""; - - if(aNode) - var query = ""; - else - var query = ""; - - return iq+query+""; - }, - info: function(aTo, aNode) { - if(aTo) - var iq = ""; - else - var iq = ""; - if(aNode) - var query = ""; - else - var query = ""; - - return iq+query+""; - } + items: function(aTo, aNode) { + if(aTo) + var iq = ""; + else + var iq = ""; + + if(aNode) + var query = ""; + else + var query = ""; + + return iq+query+""; + }, + info: function(aTo, aNode) { + if(aTo) + var iq = ""; + else + var iq = ""; + if(aNode) + var query = ""; + else + var query = ""; + + return iq+query+""; + } }; Lightstring.discoItems = function(aConnection, aTo, aCallback) { - aConnection.send(Lightstring.stanza.disco.items(aTo), function(answer){ - var items = []; + aConnection.send(Lightstring.stanza.disco.items(aTo), function(answer){ + var items = []; var elms = answer.querySelectorAll('item'); for(var i = 0; i < elms.length; i++) { var node = elms[i]; - var item = { - jid: node.getAttribute('jid'), - name: node.getAttribute('name'), - node: node.getAttribute('node') - } - items.push(item); - }; - if(aCallback) - aCallback(items); - }); + var item = { + jid: node.getAttribute('jid'), + name: node.getAttribute('name'), + node: node.getAttribute('node') + } + items.push(item); + }; + if(aCallback) + aCallback(items); + }); }; Lightstring.discoInfo = function(aConnection, aTo, aNode, aCallback) { - aConnection.send(Lightstring.stanza.disco.info(aTo, aNode), function(answer){ - var field = answer.querySelector('field[var="pubsub#creator"] > value'); - var creator = field ? field.textContent : ''; - //FIXME callback the entire data - aCallback(creator); - }); + aConnection.send(Lightstring.stanza.disco.info(aTo, aNode), function(answer){ + var identities = []; + var features = []; + + var children = answer.firstChild.children; + var length = children.length; + + for (var i = 0; i < length; i++) { + var child = children[i]; + + if (child.localName === 'feature') + features.push(child.getAttributeNS(null, 'var')); + + else if (child.localName === 'identity') { + var identity = { + category: child.getAttributeNS(null, 'category'), + type: child.getAttributeNS(null, 'type') + }; + var name = child.getAttributeNS(null, 'name'); + if (name) + identity.name = name; + identities.push(identity); + } + } + + aCallback({identities: identities, features: features}); + }); }; ////////// //PubSub// @@ -167,6 +188,7 @@ Lightstring.NS.x = "jabber:x:data"; Lightstring.NS.pubsub = "http://jabber.org/protocol/pubsub"; Lightstring.NS.pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; Lightstring.stanza.pubsub = { +<<<<<<< HEAD getConfig: function(aTo, aNode) { return ""; }, @@ -215,88 +237,128 @@ Lightstring.stanza.pubsub = { iq += ""; return iq; }, +======= + getConfig: function(aTo, aNode) { + return ""; + }, + items: function(aTo, aNode) { + return ""; + }, + affiliations: function(aTo, aNode) { + return ""; + }, + publish: function(aTo, aNode, aItem, aId) { + return ""+aItem+""; + }, + retract: function(aTo, aNode, aItem) { + return ""; + }, + 'delete': function(aTo, aNode, aURI) { + return ""; + }, + create: function(aTo, aNode, aFields) { + var iq = ""; + if(aFields) { + iq += "" + aFields.forEach(function(field) { + iq += field; + }); + iq += ""; + } + iq += ""; + return iq; + }, + setAffiliations: function(aTo, aNode, aAffiliations) { + var iq = ""; + for(var i = 0; i < aAffiliations.length; i++) { + iq += "" + } + iq += ""; + return iq; + }, +>>>>>>> f6a7c0f93d154c2cd34dfdda4ab8eec808b91b34 }; Lightstring.pubsubItems = function(aConnection, aTo, aNode, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.items(aTo, aNode), function(answer){ - var items = []; + aConnection.send(Lightstring.stanza.pubsub.items(aTo, aNode), function(answer){ + var items = []; var elms = answer.querySelectorAll('item'); for(var i = 0; i < elms.length; i++) { var node = elms[i]; - var item = { - id: node.getAttribute('id'), - name: node.querySelector('title').textContent, - src: node.querySelector('content').getAttribute('src'), - type: node.querySelector('content').getAttribute('type'), - } - var miniature = node.querySelector('link'); - if(miniature) - item.miniature = miniature.getAttribute('href'); - items.push(item); - }; - if(aCallback) - aCallback(items); - }); + var item = { + id: node.getAttribute('id'), + name: node.querySelector('title').textContent, + src: node.querySelector('content').getAttribute('src'), + type: node.querySelector('content').getAttribute('type'), + } + var miniature = node.querySelector('link'); + if(miniature) + item.miniature = miniature.getAttribute('href'); + items.push(item); + }; + if(aCallback) + aCallback(items); + }); } Lightstring.pubsubCreate = function(aConnection, aTo, aNode, aFields, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.create(aTo, aNode, aFields), function(answer) { - if(answer.getAttribute('type') === 'result') - aCallback(null, answer); - else - aCallback(answer, null); - }); + aConnection.send(Lightstring.stanza.pubsub.create(aTo, aNode, aFields), function(answer) { + if(answer.getAttribute('type') === 'result') + aCallback(null, answer); + else + aCallback(answer, null); + }); }; Lightstring.pubsubConfig = function(aConnection, aTo, aNode, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.getConfig(aTo, aNode), function(answer){ - var accessmodel = answer.querySelector('field[var="pubsub#access_model"]').lastChild.textContent; - if(accessmodel) - aCallback(accessmodel); - else - aCallback(null); - }); + aConnection.send(Lightstring.stanza.pubsub.getConfig(aTo, aNode), function(answer){ + var accessmodel = answer.querySelector('field[var="pubsub#access_model"]').lastChild.textContent; + if(accessmodel) + aCallback(accessmodel); + else + aCallback(null); + }); } Lightstring.pubsubRetract = function(aConnection, aTo, aNode, aItem, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.retract(aTo, aNode, aItem), function(answer){ - if(aCallback) - aCallback(answer); - }); + aConnection.send(Lightstring.stanza.pubsub.retract(aTo, aNode, aItem), function(answer){ + if(aCallback) + aCallback(answer); + }); } Lightstring.pubsubPublish = function(aConnection, aTo, aNode, aItem, aId, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.publish(aTo, aNode, aItem, aId), function(answer){ - if(answer.getAttribute('type') === 'result') - aCallback(null, answer); - else - aCallback(answer, null); - }); + aConnection.send(Lightstring.stanza.pubsub.publish(aTo, aNode, aItem, aId), function(answer){ + if(answer.getAttribute('type') === 'result') + aCallback(null, answer); + else + aCallback(answer, null); + }); } Lightstring.pubsubDelete = function(aConnection, aTo, aNode, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.delete(aTo, aNode), function(answer){ - if(aCallback) - aCallback(answer); - }); + aConnection.send(Lightstring.stanza.pubsub.delete(aTo, aNode), function(answer){ + if(aCallback) + aCallback(answer); + }); } Lightstring.pubsubGetAffiliations = function(aConnection, aTo, aNode, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.affiliations(aTo, aNode), function(answer) { - if((answer.getAttribute('type') === 'result') && aCallback) { - var affiliations = {}; - answer.querySelectorAll('affiliation').forEach(function(affiliation) { - affiliations[affiliation.getAttribute("jid")] = affiliation.getAttribute("affiliation"); - }) - aCallback(affiliations); - } - }); + aConnection.send(Lightstring.stanza.pubsub.affiliations(aTo, aNode), function(answer) { + if((answer.getAttribute('type') === 'result') && aCallback) { + var affiliations = {}; + answer.querySelectorAll('affiliation').forEach(function(affiliation) { + affiliations[affiliation.getAttribute("jid")] = affiliation.getAttribute("affiliation"); + }) + aCallback(affiliations); + } + }); }; Lightstring.pubsubSetAffiliations = function(aConnection, aTo, aNode, aAffiliations, aCallback) { - aConnection.send(Lightstring.stanza.pubsub.setAffiliations(aTo, aNode, aAffiliations)); + aConnection.send(Lightstring.stanza.pubsub.setAffiliations(aTo, aNode, aAffiliations)); }; ////// //IM// ////// Lightstring.stanza.message = { - normal: function(aTo, aSubject, aText) { - return ""+aSubject+""+aText+""; - }, - chat: function(aTo, aText) { - return ""+aText+""; - } + normal: function(aTo, aSubject, aText) { + return ""+aSubject+""+aText+""; + }, + chat: function(aTo, aText) { + return ""+aText+""; + } };