changeset 27:b6e4bc19ff5a

undoing
author Sonny Piers <sonny.piers@gmail.com>
date Sat, 28 Jan 2012 01:27:00 +0100
parents fc577e5b2f4a (current diff) 82bffc4a07a9 (diff)
children 630b9579fe4a
files lightstring.js plugins.js
diffstat 3 files changed, 368 insertions(+), 185 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/jid.js
@@ -0,0 +1,114 @@
+'use strict';
+
+/**
+  Copyright (c) 2012, Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+
+  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);
+    }
+  }
+};
--- a/lightstring.js
+++ b/lightstring.js
@@ -35,13 +35,13 @@ var Lightstring = {
     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'/>";
+        return "<stream:stream to='" + aService + "'" +
+                             " xmlns='" + Lightstring.NS.jabberClient + "'" +
+                             " xmlns:stream='" + Lightstring.NS.stream + "'" +
+                             " version='1.0'/>";
       },
       close: function() {
-        return '</stream:stream>';
+        return "</stream:stream>";
       }
     }
   },
@@ -98,8 +98,8 @@ Lightstring.Connection = function(aServi
       //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'/>"
+          "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" +
+               " mechanism='DIGEST-MD5'/>"
         );
       else if ('PLAIN' in mechanisms) {
         var token = btoa(
@@ -110,8 +110,8 @@ Lightstring.Connection = function(aServi
           that.password
         );
         that.send(
-          "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'\
-                 mechanism='PLAIN'>" + token + '</auth>'
+          "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" +
+               " mechanism='PLAIN'>" + token + "</auth>"
         );
       }
     }
@@ -119,29 +119,35 @@ Lightstring.Connection = function(aServi
     else {
       that.emit('features', stanza);
       //Bind http://xmpp.org/rfcs/rfc3920.html#bind
+      var bind =
+        "<iq type='set' xmlns='jabber:client'>" +
+          "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>" +
+            (that.jid.resource? "<resource>" + that.jid.resource + "</resource>": "") +
+          "</bind>" +
+        "</iq>";
       that.send(
-        "<iq type='set' xmlns='jabber:client'>\
-          <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>\
-         </iq>",
-        function() {
-        //Session http://xmpp.org/rfcs/rfc3921.html#session
-        that.send(
-          "<iq type='set' xmlns='jabber:client'>\
-            <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>\
-          </iq>",
-          function() {
-            that.emit('connected');
-          }
-        );
-      });
+        bind,
+        function(stanza) {
+          //Session http://xmpp.org/rfcs/rfc3921.html#session
+          that.jid = new Lightstring.JID(stanza.textContent);
+          that.send(
+            "<iq type='set' xmlns='jabber:client'>" +
+              "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" +
+            "</iq>",
+            function() {
+              that.emit('connected');
+            }
+          );
+        }
+      );
     }
   });
   this.on('success', function(stanza, that) {
     that.send(
-      "<stream:stream to='" + that.host + "'\
-                      xmlns='jabber:client'\
-                      xmlns:stream='http://etherx.jabber.org/streams'\
-                      version='1.0' />"
+      "<stream:stream to='" + that.jid.domain + "'" +
+                    " xmlns='jabber:client'" +
+                    " xmlns:stream='http://etherx.jabber.org/streams'" +
+                    " version='1.0'/>"
     );
   });
   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(
-      "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
-        + btoa(responseText) +
-      '</response>');
+      "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" +
+        btoa(responseText) +
+      "</response>");
   });
 };
 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);
--- 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 "<iq type='get'><query xmlns='"+Lightstring.NS.roster+"'/></iq>";
-	},
-	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<aGroups.length; i++) {
-			if(i === 0) iq.querySelector('item').appendChild(document.createElement('group'));
-			iq.querySelector('group').appendChild(document.createElement(aGroups[i]));
-		}
-		return iq;
-	},
-	remove: function(aAddress) {
-		return $iq({type: 'set'}).c('query', {xmlns: Lightstring.NS.roster}).c('item', {jid: aAddress, subscription: 'remove'}).tree();
-	}
+  'get': function() {
+    return "<iq type='get'><query xmlns='"+Lightstring.NS.roster+"'/></iq>";
+  },
+  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<aGroups.length; i++) {
+      if(i === 0) iq.querySelector('item').appendChild(document.createElement('group'));
+      iq.querySelector('group').appendChild(document.createElement(aGroups[i]));
+    }
+    return iq;
+  },
+  remove: function(aAddress) {
+    return $iq({type: 'set'}).c('query', {xmlns: Lightstring.NS.roster}).c('item', {jid: aAddress, subscription: 'remove'}).tree();
+  }
 };
 Lightstring.getRoster = function(connection, aCallback) {
-	connection.send(this.stanza.roster.get(), function(answer){
-		var contacts = [];
-		var elems = answer.querySelectorAll('item');
+  connection.send(this.stanza.roster.get(), function(answer){
+    var contacts = [];
+    var elems = answer.querySelectorAll('item');
     for(var i = 0; i<elms.length; i++) {
       var item = elms[i];
       var jid = item.getAttribute('jid');
@@ -75,33 +75,33 @@ Lightstring.getRoster = function(connect
       }
 
       contacts.push(contact);
-		};
-		aCallback(contacts);
-	});
+    };
+    aCallback(contacts);
+  });
 }
 /////////
 //vCard//
 /////////
 Lightstring.NS.vcard = 'vcard-temp';
 Lightstring.stanza.vcard = {
-	'get': function(aTo) {
-		if(aTo)
-			return "<iq type='get' to='"+aTo+"'><vCard xmlns='"+Lightstring.NS.vcard+"'/></iq>";
-		else
-			return "<iq type='get'><vCard xmlns='"+Lightstring.NS.vcard+"'/></iq>";
-	}
+  'get': function(aTo) {
+    if(aTo)
+      return "<iq type='get' to='"+aTo+"'><vCard xmlns='"+Lightstring.NS.vcard+"'/></iq>";
+    else
+      return "<iq type='get'><vCard xmlns='"+Lightstring.NS.vcard+"'/></iq>";
+  }
 };
 //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 = "<iq type='get' to='"+aTo+"'>";
-		else
-			var iq = "<iq type='get'>";
-		
-		if(aNode)
-			var query = "<query xmlns='"+Lightstring.NS['disco#items']+"' node='"+aNode+"'/>";
-		else
-			var query = "<query xmlns='"+Lightstring.NS['disco#items']+"'/>";
-			
-		return iq+query+"</iq>";
-	},
-	info: function(aTo, aNode) {
-		if(aTo)
-			var iq = "<iq type='get' to='"+aTo+"'>";
-		else
-			var iq = "<iq type='get'>";
-		if(aNode)
-			var query = "<query xmlns='"+Lightstring.NS['disco#info']+"' node='"+aNode+"'/>";
-		else
-			var query = "<query xmlns='"+Lightstring.NS['disco#info']+"'/>";
-			
-		return iq+query+"</iq>";
-	}
+  items: function(aTo, aNode) {
+    if(aTo)
+      var iq = "<iq type='get' to='"+aTo+"'>";
+    else
+      var iq = "<iq type='get'>";
+    
+    if(aNode)
+      var query = "<query xmlns='"+Lightstring.NS['disco#items']+"' node='"+aNode+"'/>";
+    else
+      var query = "<query xmlns='"+Lightstring.NS['disco#items']+"'/>";
+      
+    return iq+query+"</iq>";
+  },
+  info: function(aTo, aNode) {
+    if(aTo)
+      var iq = "<iq type='get' to='"+aTo+"'>";
+    else
+      var iq = "<iq type='get'>";
+    if(aNode)
+      var query = "<query xmlns='"+Lightstring.NS['disco#info']+"' node='"+aNode+"'/>";
+    else
+      var query = "<query xmlns='"+Lightstring.NS['disco#info']+"'/>";
+      
+    return iq+query+"</iq>";
+  }
 };
 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  "<iq type='get' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub_owner+"'><configure node='"+aNode+"'/></pubsub></iq>";
 	},
@@ -215,88 +237,128 @@ Lightstring.stanza.pubsub = {
 		iq += "</affiliations></pubsub></iq>";
 		return iq;
 	},
+=======
+  getConfig: function(aTo, aNode) {
+    return  "<iq type='get' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub_owner+"'><configure node='"+aNode+"'/></pubsub></iq>";
+  },
+  items: function(aTo, aNode) {
+    return  "<iq type='get' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub+"'><items node='"+aNode+"'/></pubsub></iq>";
+  },
+  affiliations: function(aTo, aNode) {
+    return "<iq type='get' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub_owner+"'><affiliations node='"+aNode+"'/></pubsub></iq>";
+  },
+  publish: function(aTo, aNode, aItem, aId) {
+    return  "<iq type='set' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub+"'><publish node='"+aNode+"'><item id='"+aId+"'>"+aItem+"</item></publish></pubsub></iq>";
+  },
+  retract: function(aTo, aNode, aItem) {
+    return  "<iq type='set' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub+"'><retract node='"+aNode+"'><item id='"+aItem+"'/></retract></pubsub></iq>";
+  },
+  'delete': function(aTo, aNode, aURI) {
+    return  "<iq type='set' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub_owner+"'><delete node='"+aNode+"'/></pubsub></iq>";
+  },
+  create: function(aTo, aNode, aFields) {
+    var iq = "<iq type='set' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub+"'><create node='"+aNode+"'/>";
+    if(aFields) {
+      iq += "<configure><x xmlns='"+Lightstring.NS.x+"' type='submit'>"
+      aFields.forEach(function(field) {
+        iq += field;
+      });
+      iq += "</x></configure>";
+    }
+    iq += "</pubsub></iq>";
+    return iq;
+  },
+  setAffiliations: function(aTo, aNode, aAffiliations) {
+    var iq = "<iq type='set' to='"+aTo+"'><pubsub xmlns='"+Lightstring.NS.pubsub_owner+"'><affiliations node='"+aNode+"'>";
+    for(var i = 0; i < aAffiliations.length; i++) {
+      iq += "<affiliation jid='"+aAffiliations[i][0]+"' affiliation='"+aAffiliations[i][1]+"'/>"
+    }
+    iq += "</affiliations></pubsub></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 "<message type='normal' to='"+aTo+"'><subject>"+aSubject+"</subject><body>"+aText+"</body></message>";
-	},
-	chat: function(aTo, aText) {
-		return "<message type='chat' to='"+aTo+"'><body>"+aText+"</body></message>";
-	}
+  normal: function(aTo, aSubject, aText) {
+    return "<message type='normal' to='"+aTo+"'><subject>"+aSubject+"</subject><body>"+aText+"</body></message>";
+  },
+  chat: function(aTo, aText) {
+    return "<message type='chat' to='"+aTo+"'><body>"+aText+"</body></message>";
+  }
 };