Merge feature branch 'waf-build'
diff --git a/README.md b/README.md
index 2d804c1..5074046 100644
--- a/README.md
+++ b/README.md
@@ -116,7 +116,7 @@
 hard-coded.)
 
 To install, either download
-https://github.com/remap/ndn-js/blob/master/js/ndnProtocol.xpi
+https://github.com/named-data/ndn-js/blob/master/js/ndnProtocol.xpi
 or use js/ndnProtocol.xpi in the distribution.  In Firefox, open
 Tools > Add-ons.  In the "gear" or "wrench" menu, click Install Add-on From File and open
 ndnProtocol.xpi.  Restart Firefox.
diff --git a/js/ContentObject.js b/js/ContentObject.js
index 0427df6..ca6a819 100644
--- a/js/ContentObject.js
+++ b/js/ContentObject.js
@@ -140,13 +140,10 @@
 			this.signedInfo = new SignedInfo();
 			this.signedInfo.from_ccnb(decoder);
 		}
-		
-		this.content = decoder.readBinaryElement(CCNProtocolDTags.Content);
 
+        this.content = decoder.readBinaryElement(CCNProtocolDTags.Content, null, true);
 		
-		//this.endContent = decoder.offset;
 		this.endSIG = decoder.offset;
-
 		
 		decoder.readEndElement();
 		
diff --git a/js/NDN.js b/js/NDN.js
index 3af60e8..d5a4a0e 100644
--- a/js/NDN.js
+++ b/js/NDN.js
@@ -229,7 +229,7 @@
         // Set interest timer.
         var timeoutMilliseconds = (interest.interestLifetime || 4000);
         var timeoutCallback = function() {
-			if (LOG > 3) console.log("Interest time out: " + interest.name.to_uri());
+			if (LOG > 1) console.log("Interest time out: " + interest.name.to_uri());
 				
 			// Remove PIT entry from NDN.PITTable, even if we add it again later to re-express
             //   the interest because we don't want to match it in the mean time.
@@ -241,7 +241,7 @@
 			// Raise closure callback
 			if (closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, 
                   new UpcallInfo(thisNDN, interest, 0, null)) == Closure.RESULT_REEXPRESS) {
-			    if (LOG > 3) console.log("Re-express interest: " + interest.name.to_uri());
+			    if (LOG > 1) console.log("Re-express interest: " + interest.name.to_uri());
                 pitEntry.timerID = setTimeout(timeoutCallback, timeoutMilliseconds);
                 NDN.PITTable.push(pitEntry);
                 thisNDN.transport.send(binaryInterest);
@@ -529,7 +529,7 @@
         
     this.host = hostAndPort.host;
     this.port = hostAndPort.port;   
-    if (LOG>3) console.log("Connect: trying host from getHostAndPort: " + this.host);
+    if (LOG>0) console.log("connectAndExecute: trying host from getHostAndPort: " + this.host);
     
     // Fetch any content.
     var interest = new Interest(new Name("/"));
@@ -537,7 +537,7 @@
 
     var thisNDN = this;
 	var timerID = setTimeout(function() {
-        if (LOG>3) console.log("Connect: timeout waiting for host " + thisNDN.host);
+        if (LOG>0) console.log("connectAndExecute: timeout waiting for host " + thisNDN.host);
         // Try again.
         thisNDN.connectAndExecute(onConnected);
 	}, 3000);
@@ -568,6 +568,7 @@
 	this.ndn.readyStatus = NDN.OPENED;
 	this.ndn.onopen();
 
+    if (LOG>0) console.log("connectAndExecute: connected to host " + this.ndn.host);
     this.onConnected();
 
     return Closure.RESULT_OK;
diff --git a/js/XpcomTransport.js b/js/XpcomTransport.js
index ddd150d..178639b 100644
--- a/js/XpcomTransport.js
+++ b/js/XpcomTransport.js
@@ -44,6 +44,7 @@
 	var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance
         (Components.interfaces.nsIInputStreamPump);
 	this.socket = transportService.createTransport(null, 0, ndn.host, ndn.port, null);
+    if (LOG > 0) console.log('XpcomTransport: Connected to ' + ndn.host + ":" + ndn.port);
     this.connectedHost = ndn.host;
     this.connectedPort = ndn.port;
     this.outStream = this.socket.openOutputStream(1, 0, 0);
diff --git a/js/encoding/BinaryXMLDecoder.js b/js/encoding/BinaryXMLDecoder.js
index f1380af..882a7fc 100644
--- a/js/encoding/BinaryXMLDecoder.js
+++ b/js/encoding/BinaryXMLDecoder.js
@@ -339,19 +339,16 @@
 	};
 
 
-// returns a byte[]
+// Returns a Uint8Array.
 BinaryXMLDecoder.prototype.readBinaryElement = function(
 		//long 
 		startTag,
 		//TreeMap<String, String> 
-		attributes){
-	//byte [] 
-	var blob = null;
-	
+		attributes,
+		//boolean
+		allowNull){
 	this.readStartElement(startTag, attributes);
-	blob = this.readBlob();	
-
-	return blob;
+	return this.readBlob(allowNull);	
 };
 	
 	
@@ -383,15 +380,21 @@
 	};
 	
 
-//returns a uint8array
-BinaryXMLDecoder.prototype.readBlob = function() {
-			//uint8array
-			
-			var blob = this.decodeBlob();	
-			this.readEndElement();
-			return blob;
-
-	};
+/*
+ * Read a blob as well as the end element. Returns a Uint8Array (or null for missing blob).
+ * If the blob is missing and allowNull is false (default), throw an exception.  Otherwise,
+ *   just read the end element and return null.
+ */
+BinaryXMLDecoder.prototype.readBlob = function(allowNull) {
+    if (this.istream[this.offset] == XML_CLOSE && allowNull) {
+        this.readEndElement();
+        return null;
+    }
+    
+	var blob = this.decodeBlob();	
+	this.readEndElement();
+	return blob;
+};
 
 
 //CCNTime
@@ -413,8 +416,8 @@
 
 	//if(lontimestamp<0) lontimestamp =  - lontimestamp;
 
-	if(LOG>3) console.log('DECODED DATE WITH VALUE');
-	if(LOG>3) console.log(lontimestamp);
+	if(LOG>4) console.log('DECODED DATE WITH VALUE');
+	if(LOG>4) console.log(lontimestamp);
 	
 
 	//CCNTime 
@@ -462,33 +465,26 @@
 		
 	} while (more);
 	
-	if(LOG>3)console.log('TYPE is '+ type + ' VAL is '+ val);
+	if(LOG>4)console.log('TYPE is '+ type + ' VAL is '+ val);
 
 	return new TypeAndVal(type, val);
 };
 
-
-
 //TypeAndVal
-BinaryXMLDecoder.peekTypeAndVal = function() {
+BinaryXMLDecoder.prototype.peekTypeAndVal = function() {
 	//TypeAndVal 
 	var tv = null;
-	
-	//this.istream.mark(LONG_BYTES*2);		
-	
 	var previousOffset = this.offset;
 	
 	try {
 		tv = this.decodeTypeAndVal();
 	} finally {
-		//this.istream.reset();
 		this.offset = previousOffset;
 	}
 	
 	return tv;
 };
 
-
 //Uint8Array
 BinaryXMLDecoder.prototype.decodeBlob = function(
 		//int 
@@ -528,10 +524,10 @@
 		//TypeAndVal 
 		var tv = this.decodeTypeAndVal();
 		
-		if(LOG>3)console.log('TV is '+tv);
-		if(LOG>3)console.log(tv);
+		if(LOG>4)console.log('TV is '+tv);
+		if(LOG>4)console.log(tv);
 		
-		if(LOG>3)console.log('Type of TV is '+typeof tv);
+		if(LOG>4)console.log('Type of TV is '+typeof tv);
 	
 		if ((null == tv) || (XML_UDATA != tv.type())) { // if we just have closers left, will get back null
 			//if (Log.isLoggable(Log.FAC_ENCODING, Level.FINEST))
diff --git a/ndnProtocol/components/ndnProtocolService.js b/ndnProtocol/components/ndnProtocolService.js
index 975a6b5..8f58a45 100644
--- a/ndnProtocol/components/ndnProtocolService.js
+++ b/ndnProtocol/components/ndnProtocolService.js
@@ -16,9 +16,7 @@
 Components.utils.import("chrome://modules/content/ContentChannel.jsm");

 Components.utils.import("chrome://modules/content/NdnProtocolInfo.jsm");

 

-function NdnProtocol() {

-    this.ndn = new NDN({ getTransport: function() { return new XpcomTransport(); }, 

-                       verify: false });

+function NdnProtocol() {    

 }

 

 NdnProtocol.prototype = {

@@ -67,8 +65,6 @@
 

     newChannel: function(aURI)

     {

-        var thisNdnProtocol = this;

-        

         try {            

             var uriParts = NdnProtocolInfo.splitUri(aURI.spec);

     

@@ -86,8 +82,8 @@
             var requestContent = function(contentListener) {                

                 var name = new Name(uriParts.name);

                 // Use the same NDN object each time.

-                thisNdnProtocol.ndn.expressInterest(name, new ExponentialReExpressClosure 

-                    (new ContentClosure(thisNdnProtocol.ndn, contentListener, name, 

+                NdnProtocolInfo.ndn.expressInterest(name, new ExponentialReExpressClosure 

+                    (new ContentClosure(NdnProtocolInfo.ndn, contentListener, name, 

                             aURI, searchWithoutNdn + uriParts.hash, segmentTemplate)),

                     template);

             };

@@ -142,16 +138,13 @@
 

 ContentClosure.prototype.upcall = function(kind, upcallInfo) {

   try {

-    // Assume this is only called once we're connected, report the host and port.

-    NdnProtocolInfo.setConnectedNdnHub(this.ndn.host, this.ndn.port);

-    

     if (this.contentListener.done)

         // We are getting unexpected extra results.

         return Closure.RESULT_ERR;

     

     if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {

         if (!this.didOnStart) {

-            // We have not received a segments to start the content yet, so assume the URI can't be fetched.

+            // We have not received a segment to start the content yet, so assume the URI can't be fetched.

             this.contentListener.onStart("text/plain", "utf-8", this.aURI);

             this.contentListener.onReceivedContent

                 ("The latest interest timed out after " + upcallInfo.interest.interestLifetime + " milliseconds.");

@@ -174,6 +167,9 @@
         return Closure.RESULT_ERR;

     }

     

+    // Assume this is only called once we're connected, report the host and port.

+    NdnProtocolInfo.setConnectedNdnHub(this.ndn.host, this.ndn.port);

+    

     // If !this.uriEndsWithSegmentNumber, we use the segmentNumber to load multiple segments.

     // If this.uriEndsWithSegmentNumber, then we leave segmentNumber null.

     var segmentNumber = null;

diff --git a/ndnProtocol/content/ndnToolbar.js b/ndnProtocol/content/ndnToolbar.js
index fcff0e6..ddd5cda 100644
--- a/ndnProtocol/content/ndnToolbar.js
+++ b/ndnProtocol/content/ndnToolbar.js
@@ -62,3 +62,24 @@
 }
 
 window.addEventListener("load", function() { NdnProtocolInfo.addNdnHubChangedListener(onNdnHubChanged); }, false);
+
+function ndnToolbarSetHub() {
+    var host = prompt("Enter hub host:", NdnProtocolInfo.ndn.host);
+    if (!host)
+        return;
+    
+    host = host.trim();
+    if (host == "")
+        return;
+    if (host == NdnProtocolInfo.ndn.host)
+        // No change.
+        return;
+    
+    var port = 9695;
+    NdnProtocolInfo.ndn.createRoute(host, port);
+    document.getElementById("ndnHubLabel").setAttribute("value", "Hub: trying " + host + ":" + port);
+    
+    if (window._content.document.location.protocol == "ndn:")
+        // Reload with the new hub.
+        window._content.document.location = window._content.document.location.href;
+}
diff --git a/ndnProtocol/content/toolbar.xul b/ndnProtocol/content/toolbar.xul
index 68af3bf..8f51e93 100644
--- a/ndnProtocol/content/toolbar.xul
+++ b/ndnProtocol/content/toolbar.xul
@@ -19,6 +19,8 @@
         label="Latest" oncommand="ndnToolbarGetVersion('latest')"/>
       <toolbarseparator/>
       <label id="ndnHubLabel" value="Hub: not connected"/>
+      <toolbarbutton id="ndnToolbarSetHubButton"
+        label="Set..." oncommand="ndnToolbarSetHub()"/>
   </toolbar>
 </toolbox>
 
diff --git a/ndnProtocol/modules/NdnProtocolInfo.jsm b/ndnProtocol/modules/NdnProtocolInfo.jsm
index 3a5fc79..396aeb9 100644
--- a/ndnProtocol/modules/NdnProtocolInfo.jsm
+++ b/ndnProtocol/modules/NdnProtocolInfo.jsm
@@ -10,12 +10,17 @@
 const Cr = Components.results;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("chrome://modules/content/ndn-js.jsm");
 
 var NdnProtocolInfo = function NdnProtocolInfo(){
 };
 
-NdnProtocolInfo.ndnHubHost = null;
-NdnProtocolInfo.ndnHubPort = null;
+NdnProtocolInfo.ndn = new NDN({ getTransport: function() { return new XpcomTransport(); }, 
+                              verify: false });
+
+// These are set once a connection is established.
+NdnProtocolInfo.connectedNdnHubHost = null;
+NdnProtocolInfo.connectedNdnHubPort = null;
 NdnProtocolInfo.ndnHubChangedListenerList = [];
 
 /*
@@ -25,9 +30,9 @@
 NdnProtocolInfo.addNdnHubChangedListener = function(listener) {
     NdnProtocolInfo.ndnHubChangedListenerList.push(listener);
     
-    if (NdnProtocolInfo.ndnHubHost != null && NdnProtocolInfo.ndnHubPort != null) {
+    if (NdnProtocolInfo.connectedNdnHubHost != null && NdnProtocolInfo.connectedNdnHubPort != null) {
         try {
-            listener(NdnProtocolInfo.ndnHubHost, NdnProtocolInfo.ndnHubPort);
+            listener(NdnProtocolInfo.connectedNdnHubHost, NdnProtocolInfo.connectedNdnHubPort);
         }
         catch (ex) {
             // Ignore error from the listener.
@@ -40,12 +45,12 @@
  * listener in ndnHubChangedListenerList.
  */
 NdnProtocolInfo.setConnectedNdnHub = function(host, port) {
-    if (host == NdnProtocolInfo.ndnHubHost && port == NdnProtocolInfo.ndnHubPort)
+    if (host == NdnProtocolInfo.connectedNdnHubHost && port == NdnProtocolInfo.connectedNdnHubPort)
         // No change.
         return;
     
-    NdnProtocolInfo.ndnHubHost = host;
-    NdnProtocolInfo.ndnHubPort = port;
+    NdnProtocolInfo.connectedNdnHubHost = host;
+    NdnProtocolInfo.connectedNdnHubPort = port;
     for (var i = 0; i < NdnProtocolInfo.ndnHubChangedListenerList.length; ++i) {
         try {
             NdnProtocolInfo.ndnHubChangedListenerList[i](host, port);
diff --git a/ndnProtocol/modules/ndn-js.jsm b/ndnProtocol/modules/ndn-js.jsm
index 0719199..5d2a92d 100644
--- a/ndnProtocol/modules/ndn-js.jsm
+++ b/ndnProtocol/modules/ndn-js.jsm
@@ -49,13 +49,13 @@
 "Parameters","InfoString",null,"StatusResponse","StatusCode","StatusText","SyncNode","SyncNodeKind","SyncNodeElement","SyncVersion","SyncNodeElements","SyncContentHash","SyncLeafCount","SyncTreeDepth","SyncByteCount","ConfigSlice","ConfigSliceList","ConfigSliceOp"],CCNTime=function(a){this.NANOS_MAX=999877929;"number"==typeof a?this.msec=a:1<LOG&&console.log("UNRECOGNIZED TYPE FOR TIME")};CCNTime.prototype.getJavascriptDate=function(){var a=new Date;a.setTime(this.msec);return a};
 var ExponentialReExpressClosure=function(a,b){Closure.call(this);this.callerClosure=a;b=b||{};this.maxInterestLifetime=b.maxInterestLifetime||16E3};
 ExponentialReExpressClosure.prototype.upcall=function(a,b){try{if(a==Closure.UPCALL_INTEREST_TIMED_OUT){var c=b.interest.interestLifetime;if(null==c)return this.callerClosure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,b);c*=2;if(c>this.maxInterestLifetime)return this.callerClosure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,b);var d=b.interest.clone();d.interestLifetime=c;b.ndn.expressInterest(d.name,this,d);return Closure.RESULT_OK}return this.callerClosure.upcall(a,b)}catch(e){return console.log("ExponentialReExpressClosure.upcall exception: "+
-e),Closure.RESULT_ERR}};var Name=function Name(b){if("string"==typeof b)3<LOG&&console.log("Content Name String "+b),this.components=Name.createNameArray(b);else if("object"===typeof b){4<LOG&&console.log("Content Name Array "+b);this.components=[];for(var c=0;c<b.length;++c)this.add(b[c])}else null==b?this.components=[]:1<LOG&&console.log("NO CONTENT NAME GIVEN")};Name.prototype.getName=function(){return this.to_uri()};
+e),Closure.RESULT_ERR}};var Name=function Name(b){if("string"==typeof b)3<LOG&&console.log("Content Name String "+b),this.components=Name.createNameArray(b);else if("object"===typeof b)if(this.components=[],b instanceof Name)this.add(b);else for(var c=0;c<b.length;++c)this.add(b[c]);else null==b?this.components=[]:1<LOG&&console.log("NO CONTENT NAME GIVEN")};Name.prototype.getName=function(){return this.to_uri()};
 Name.createNameArray=function(a){a=a.trim();if(0>=a.length)return[];var b=a.indexOf(":");if(0<=b){var c=a.indexOf("/");if(0>c||b<c)a=a.substr(b+1,a.length-b-1).trim()}if("/"==a[0])if(2<=a.length&&"/"==a[1]){b=a.indexOf("/",2);if(0>b)return[];a=a.substr(b+1,a.length-b-1).trim()}else a=a.substr(1,a.length-1).trim();a=a.split("/");for(b=0;b<a.length;++b)c=Name.fromEscapedString(a[b]),null==c?(a.splice(b,1),--b):a[b]=c;return a};
 Name.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());for(this.components=[];a.peekStartElement(CCNProtocolDTags.Component);)this.add(a.readBinaryElement(CCNProtocolDTags.Component));a.readEndElement()};Name.prototype.to_ccnb=function(a){if(null==this.components)throw Error("CANNOT ENCODE EMPTY CONTENT NAME");a.writeStartElement(this.getElementLabel());for(var b=this.components.length,c=0;c<b;c++)a.writeElement(CCNProtocolDTags.Component,this.components[c]);a.writeEndElement()};
 Name.prototype.getElementLabel=function(){return CCNProtocolDTags.Name};
-Name.prototype.add=function(a){var b;if("string"==typeof a)b=DataUtils.stringToUtf8Array(a);else if("object"==typeof a&&a instanceof Uint8Array)b=new Uint8Array(a);else if("object"==typeof a&&a instanceof ArrayBuffer)b=new Uint8Array(new ArrayBuffer(a.byteLength)),b.set(new Uint8Array(a));else if("object"==typeof a)b=new Uint8Array(a);else throw Error("Cannot add Name element at index "+this.components.length+": Invalid type");return this.components.push(b)};
-Name.prototype.to_uri=function(){if(0==this.components.length)return"/";for(var a="",b=0;b<this.components.length;++b)a+="/"+Name.toEscapedString(this.components[b]);return a};Name.prototype.addSegment=function(a){var a=DataUtils.nonNegativeIntToBigEndian(a),b=new Uint8Array(a.length+1);b.set(a,1);this.components.push(b);return this};Name.prototype.getPrefix=function(a){return new Name(this.components.slice(0,a))};
-Name.prototype.getComponent=function(a){var b=new ArrayBuffer(this.components[a].length);(new Uint8Array(b)).set(this.components[a]);return b};Name.prototype.indexOfFileName=function(){for(var a=this.components.length-1;0<=a;--a){var b=this.components[a];if(!(0>=b.length)&&!(0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};
+Name.prototype.add=function(a){var b;if("string"==typeof a)b=DataUtils.stringToUtf8Array(a);else if("object"==typeof a&&a instanceof Uint8Array)b=new Uint8Array(a);else if("object"==typeof a&&a instanceof ArrayBuffer)b=new Uint8Array(new ArrayBuffer(a.byteLength)),b.set(new Uint8Array(a));else{if("object"==typeof a&&a instanceof Name){a=a==this?this.components.slice(0,this.components.length):a.components;for(b=0;b<a.length;++b)this.components.push(new Uint8Array(a[b]));return this}if("object"==typeof a)b=
+new Uint8Array(a);else throw Error("Cannot add Name element at index "+this.components.length+": Invalid type");}this.components.push(b);return this};Name.prototype.to_uri=function(){if(0==this.components.length)return"/";for(var a="",b=0;b<this.components.length;++b)a+="/"+Name.toEscapedString(this.components[b]);return a};Name.prototype.addSegment=function(a){var a=DataUtils.nonNegativeIntToBigEndian(a),b=new Uint8Array(a.length+1);b.set(a,1);this.components.push(b);return this};
+Name.prototype.getPrefix=function(a){return new Name(this.components.slice(0,a))};Name.prototype.getComponent=function(a){var b=new ArrayBuffer(this.components[a].length);(new Uint8Array(b)).set(this.components[a]);return b};Name.prototype.indexOfFileName=function(){for(var a=this.components.length-1;0<=a;--a){var b=this.components[a];if(!(0>=b.length)&&!(0==b[0]||192==b[0]||193==b[0]||245<=b[0]&&255>=b[0]))return a}return-1};
 Name.prototype.equalsName=function(a){if(this.components.length!=a.components.length)return!1;for(var b=this.components.length-1;0<=b;--b)if(!DataUtils.arraysEqual(this.components[b],a.components[b]))return!1;return!0};Name.prototype.getContentDigestValue=function(){for(var a=this.components.length-1;0<=a;--a){var b=Name.getComponentContentDigestValue(this.components[a]);if(null!=b)return b}return null};
 Name.getComponentContentDigestValue=function(a){return a.length==Name.ContentDigestPrefix.length+32+Name.ContentDigestSuffix.length&&DataUtils.arraysEqual(a.subarray(0,Name.ContentDigestPrefix.length),Name.ContentDigestPrefix)&&DataUtils.arraysEqual(a.subarray(a.length-Name.ContentDigestSuffix.length,a.length),Name.ContentDigestSuffix)?a.subarray(Name.ContentDigestPrefix.length,Name.ContentDigestPrefix.length+32):null};Name.ContentDigestPrefix=new Uint8Array([193,46,77,46,71,193,1,170,2,133]);
 Name.ContentDigestSuffix=new Uint8Array([0]);Name.toEscapedString=function(a){for(var b="",c=!1,d=0;d<a.length;++d)if(46!=a[d]){c=!0;break}if(c)for(d=0;d<a.length;++d)c=a[d],b=48<=c&&57>=c||65<=c&&90>=c||97<=c&&122>=c||43==c||45==c||46==c||95==c?b+String.fromCharCode(c):b+("%"+(16>c?"0":"")+c.toString(16).toUpperCase());else{b="...";for(d=0;d<a.length;++d)b+="."}return b};
@@ -64,7 +64,7 @@
 ContentObject.prototype.sign=function(){var a=this.encodeObject(this.name),b=this.encodeObject(this.signedInfo),c=this.encodeContent(),d=new ArrayBuffer(a.length+b.length+c.length),d=new Uint8Array(d);d.set(a,0);d.set(b,a.length);d.set(c,a.length+b.length);4<LOG&&console.log("Signature Data is (binary) "+d);4<LOG&&console.log("Signature Data is (RawString)");4<LOG&&console.log(DataUtils.toString(d));a=new RSAKey;a.readPrivateKeyFromPEMString(globalKeyManager.privateKey);a=a.signByteArrayWithSHA256(d);
 4<LOG&&console.log("SIGNATURE SAVED IS");4<LOG&&console.log(a);4<LOG&&console.log(DataUtils.toNumbers(a.trim()));this.signature.signature=DataUtils.toNumbers(a.trim())};ContentObject.prototype.encodeObject=function(a){var b=new BinaryXMLEncoder;a.to_ccnb(b);return b.getReducedOstream()};ContentObject.prototype.encodeContent=function(){var a=new BinaryXMLEncoder;a.writeElement(CCNProtocolDTags.Content,this.content);return a.getReducedOstream()};
 ContentObject.prototype.saveRawData=function(a){this.rawSignatureData=a.subarray(this.startSIG,this.endSIG)};
-ContentObject.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());a.peekStartElement(CCNProtocolDTags.Signature)&&(this.signature=new Signature,this.signature.from_ccnb(a));this.startSIG=a.offset;this.name=new Name;this.name.from_ccnb(a);a.peekStartElement(CCNProtocolDTags.SignedInfo)&&(this.signedInfo=new SignedInfo,this.signedInfo.from_ccnb(a));this.content=a.readBinaryElement(CCNProtocolDTags.Content);this.endSIG=a.offset;a.readEndElement();this.saveRawData(a.istream)};
+ContentObject.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());a.peekStartElement(CCNProtocolDTags.Signature)&&(this.signature=new Signature,this.signature.from_ccnb(a));this.startSIG=a.offset;this.name=new Name;this.name.from_ccnb(a);a.peekStartElement(CCNProtocolDTags.SignedInfo)&&(this.signedInfo=new SignedInfo,this.signedInfo.from_ccnb(a));this.content=a.readBinaryElement(CCNProtocolDTags.Content,null,!0);this.endSIG=a.offset;a.readEndElement();this.saveRawData(a.istream)};
 ContentObject.prototype.to_ccnb=function(a){a.writeStartElement(this.getElementLabel());null!=this.signature&&this.signature.to_ccnb(a);this.startSIG=a.offset;null!=this.name&&this.name.to_ccnb(a);null!=this.signedInfo&&this.signedInfo.to_ccnb(a);a.writeElement(CCNProtocolDTags.Content,this.content);this.endSIG=a.offset;a.writeEndElement();this.saveRawData(a.ostream)};ContentObject.prototype.getElementLabel=function(){return CCNProtocolDTags.ContentObject};
 var Signature=function(a,b,c){this.Witness=a;this.signature=b;this.digestAlgorithm=c};
 Signature.prototype.from_ccnb=function(a){a.readStartElement(this.getElementLabel());4<LOG&&console.log("STARTED DECODING SIGNATURE");a.peekStartElement(CCNProtocolDTags.DigestAlgorithm)&&(4<LOG&&console.log("DIGIEST ALGORITHM FOUND"),this.digestAlgorithm=a.readUTF8Element(CCNProtocolDTags.DigestAlgorithm));a.peekStartElement(CCNProtocolDTags.Witness)&&(4<LOG&&console.log("WITNESS FOUND"),this.Witness=a.readBinaryElement(CCNProtocolDTags.Witness));4<LOG&&console.log("SIGNATURE FOUND");this.signature=
@@ -145,12 +145,13 @@
 g));}};BinaryXMLDecoder.prototype.peekStartElementAsString=function(){var a=null,b=this.offset;try{var c=this.decodeTypeAndVal();if(null!=c)if(c.type()==XML_TAG){var d;d="string"==typeof c.val()?parseInt(c.val())+1:c.val()+1;a=this.decodeUString(d)}else c.type()==XML_DTAG&&(a=tagToString(c.val()))}catch(e){}finally{try{this.offset=b}catch(f){throw Log.logStackTrace(Log.FAC_ENCODING,Level.WARNING,f),new ContentDecodingException(Error("Cannot reset stream! "+f.getMessage(),f));}}return a};
 BinaryXMLDecoder.prototype.peekStartElement=function(a){if("string"==typeof a){var b=this.peekStartElementAsString();return null!=b&&b==a?!0:!1}if("number"==typeof a)return b=this.peekStartElementAsLong(),null!=b&&b==a?!0:!1;throw new ContentDecodingException(Error("SHOULD BE STRING OR NUMBER"));};
 BinaryXMLDecoder.prototype.peekStartElementAsLong=function(){var a=null,b=this.offset;try{var c=this.decodeTypeAndVal();if(null!=c)if(c.type()==XML_TAG){if(c.val()+1>DEBUG_MAX_LEN)throw new ContentDecodingException(Error("Decoding error: length "+c.val()+1+" longer than expected maximum length!"));var d;d="string"==typeof c.val()?parseInt(c.val())+1:c.val()+1;var e=this.decodeUString(d),a=stringToTag(e)}else c.type()==XML_DTAG&&(a=c.val())}catch(f){}finally{try{this.offset=b}catch(g){throw Log.logStackTrace(Log.FAC_ENCODING,
-Level.WARNING,g),Error("Cannot reset stream! "+g.getMessage(),g);}}return a};BinaryXMLDecoder.prototype.readBinaryElement=function(a,b){var c=null;this.readStartElement(a,b);return c=this.readBlob()};
+Level.WARNING,g),Error("Cannot reset stream! "+g.getMessage(),g);}}return a};BinaryXMLDecoder.prototype.readBinaryElement=function(a,b,c){this.readStartElement(a,b);return this.readBlob(c)};
 BinaryXMLDecoder.prototype.readEndElement=function(){4<LOG&&console.log("this.offset is "+this.offset);var a=this.istream[this.offset];this.offset++;4<LOG&&console.log("XML_CLOSE IS "+XML_CLOSE);4<LOG&&console.log("next is "+a);if(a!=XML_CLOSE)throw console.log("Expected end element, got: "+a),new ContentDecodingException(Error("Expected end element, got: "+a));};BinaryXMLDecoder.prototype.readUString=function(){var a=this.decodeUString();this.readEndElement();return a};
-BinaryXMLDecoder.prototype.readBlob=function(){var a=this.decodeBlob();this.readEndElement();return a};BinaryXMLDecoder.prototype.readDateTime=function(a){var a=this.readBinaryElement(a),a=DataUtils.toHex(a),a=parseInt(a,16),b=1E3*(a/4096);3<LOG&&console.log("DECODED DATE WITH VALUE");3<LOG&&console.log(b);b=new CCNTime(b);if(null==b)throw new ContentDecodingException(Error("Cannot parse timestamp: "+DataUtils.printHexBytes(a)));return b};
-BinaryXMLDecoder.prototype.decodeTypeAndVal=function(){var a=-1,b=0,c=!0;do{var d=this.istream[this.offset];if(0>d||0==d&&0==b)return null;(c=0==(d&XML_TT_NO_MORE))?(b<<=XML_REG_VAL_BITS,b|=d&XML_REG_VAL_MASK):(a=d&XML_TT_MASK,b<<=XML_TT_VAL_BITS,b|=d>>>XML_TT_BITS&XML_TT_VAL_MASK);this.offset++}while(c);3<LOG&&console.log("TYPE is "+a+" VAL is "+b);return new TypeAndVal(a,b)};BinaryXMLDecoder.peekTypeAndVal=function(){var a=null,b=this.offset;try{a=this.decodeTypeAndVal()}finally{this.offset=b}return a};
-BinaryXMLDecoder.prototype.decodeBlob=function(a){if(null==a)return a=this.decodeTypeAndVal(),a="string"==typeof a.val()?parseInt(a.val()):a.val(),this.decodeBlob(a);var b=this.istream.subarray(this.offset,this.offset+a);this.offset+=a;return b};
-BinaryXMLDecoder.prototype.decodeUString=function(a){if(null==a){var a=this.offset,b=this.decodeTypeAndVal();3<LOG&&console.log("TV is "+b);3<LOG&&console.log(b);3<LOG&&console.log("Type of TV is "+typeof b);return null==b||XML_UDATA!=b.type()?(this.offset=a,""):this.decodeUString(b.val())}a=this.decodeBlob(a);return DataUtils.toString(a)};TypeAndVal=function(a,b){this.t=a;this.v=b};TypeAndVal.prototype.type=function(){return this.t};TypeAndVal.prototype.val=function(){return this.v};
+BinaryXMLDecoder.prototype.readBlob=function(a){if(this.istream[this.offset]==XML_CLOSE&&a)return this.readEndElement(),null;a=this.decodeBlob();this.readEndElement();return a};
+BinaryXMLDecoder.prototype.readDateTime=function(a){var a=this.readBinaryElement(a),a=DataUtils.toHex(a),a=parseInt(a,16),b=1E3*(a/4096);4<LOG&&console.log("DECODED DATE WITH VALUE");4<LOG&&console.log(b);b=new CCNTime(b);if(null==b)throw new ContentDecodingException(Error("Cannot parse timestamp: "+DataUtils.printHexBytes(a)));return b};
+BinaryXMLDecoder.prototype.decodeTypeAndVal=function(){var a=-1,b=0,c=!0;do{var d=this.istream[this.offset];if(0>d||0==d&&0==b)return null;(c=0==(d&XML_TT_NO_MORE))?(b<<=XML_REG_VAL_BITS,b|=d&XML_REG_VAL_MASK):(a=d&XML_TT_MASK,b<<=XML_TT_VAL_BITS,b|=d>>>XML_TT_BITS&XML_TT_VAL_MASK);this.offset++}while(c);4<LOG&&console.log("TYPE is "+a+" VAL is "+b);return new TypeAndVal(a,b)};
+BinaryXMLDecoder.prototype.peekTypeAndVal=function(){var a=null,b=this.offset;try{a=this.decodeTypeAndVal()}finally{this.offset=b}return a};BinaryXMLDecoder.prototype.decodeBlob=function(a){if(null==a)return a=this.decodeTypeAndVal(),a="string"==typeof a.val()?parseInt(a.val()):a.val(),this.decodeBlob(a);var b=this.istream.subarray(this.offset,this.offset+a);this.offset+=a;return b};
+BinaryXMLDecoder.prototype.decodeUString=function(a){if(null==a){var a=this.offset,b=this.decodeTypeAndVal();4<LOG&&console.log("TV is "+b);4<LOG&&console.log(b);4<LOG&&console.log("Type of TV is "+typeof b);return null==b||XML_UDATA!=b.type()?(this.offset=a,""):this.decodeUString(b.val())}a=this.decodeBlob(a);return DataUtils.toString(a)};TypeAndVal=function(a,b){this.t=a;this.v=b};TypeAndVal.prototype.type=function(){return this.t};TypeAndVal.prototype.val=function(){return this.v};
 BinaryXMLDecoder.prototype.readIntegerElement=function(a){4<LOG&&console.log("READING INTEGER "+a);4<LOG&&console.log("TYPE OF "+typeof a);a=this.readUTF8Element(a);return parseInt(a)};BinaryXMLDecoder.prototype.readUTF8Element=function(a,b){this.readStartElement(a,b);return this.readUString()};BinaryXMLDecoder.prototype.seek=function(a){this.offset=a};function ContentDecodingException(a){this.message=a.message;for(var b in a)this[b]=a[b]}ContentDecodingException.prototype=Error();
 ContentDecodingException.prototype.name="ContentDecodingException";var BinaryXMLStructureDecoder=function(){this.gotElementEnd=!1;this.level=this.offset=0;this.state=BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE;this.headerLength=0;this.useHeaderBuffer=!1;this.headerBuffer=new DynamicUint8Array(5);this.nBytesToRead=0};BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE=0;BinaryXMLStructureDecoder.READ_BYTES=1;
 BinaryXMLStructureDecoder.prototype.findElementEnd=function(a){if(this.gotElementEnd)return!0;for(var b=new BinaryXMLDecoder(a);;){if(this.offset>=a.length)return!1;switch(this.state){case BinaryXMLStructureDecoder.READ_HEADER_OR_CLOSE:if(0==this.headerLength&&a[this.offset]==XML_CLOSE){++this.offset;--this.level;if(0==this.level)return!0;if(0>this.level)throw Error("BinaryXMLStructureDecoder: Unexepected close tag at offset "+(this.offset-1));this.startHeader();break}for(var c=this.headerLength;;){if(this.offset>=
@@ -297,7 +298,7 @@
 function getEntryForRegisteredPrefix(a){for(var b=0;b<NDN.CSTable.length;b++)if(null!=NDN.CSTable[b].name.match(a))return NDN.CSTable[b];return null}NDN.makeShuffledGetHostAndPort=function(a,b){a=a.slice(0,a.length);DataUtils.shuffle(a);return function(){return 0==a.length?null:{host:a.splice(0,1)[0],port:b}}};
 NDN.prototype.expressInterest=function(a,b,c){var d=new Interest(a);null!=c?(d.minSuffixComponents=c.minSuffixComponents,d.maxSuffixComponents=c.maxSuffixComponents,d.publisherPublicKeyDigest=c.publisherPublicKeyDigest,d.exclude=c.exclude,d.childSelector=c.childSelector,d.answerOriginKind=c.answerOriginKind,d.scope=c.scope,d.interestLifetime=c.interestLifetime):d.interestLifetime=4E3;if(null==this.host||null==this.port)if(null==this.getHostAndPort)console.log("ERROR: host OR port NOT SET");else{var e=
 this;this.connectAndExecute(function(){e.reconnectAndExpressInterest(d,b)})}else this.reconnectAndExpressInterest(d,b)};NDN.prototype.reconnectAndExpressInterest=function(a,b){if(this.transport.connectedHost!=this.host||this.transport.connectedPort!=this.port){var c=this;this.transport.connect(c,function(){c.expressInterestHelper(a,b)})}else this.expressInterestHelper(a,b)};
-NDN.prototype.expressInterestHelper=function(a,b){var c=encodeToBinaryInterest(a),d=this;if(null!=b){var e=new PITEntry(a,b);NDN.PITTable.push(e);b.pitEntry=e;var f=a.interestLifetime||4E3,g=function(){3<LOG&&console.log("Interest time out: "+a.name.to_uri());var h=NDN.PITTable.indexOf(e);0<=h&&NDN.PITTable.splice(h,1);b.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,new UpcallInfo(d,a,0,null))==Closure.RESULT_REEXPRESS&&(3<LOG&&console.log("Re-express interest: "+a.name.to_uri()),e.timerID=setTimeout(g,
+NDN.prototype.expressInterestHelper=function(a,b){var c=encodeToBinaryInterest(a),d=this;if(null!=b){var e=new PITEntry(a,b);NDN.PITTable.push(e);b.pitEntry=e;var f=a.interestLifetime||4E3,g=function(){1<LOG&&console.log("Interest time out: "+a.name.to_uri());var h=NDN.PITTable.indexOf(e);0<=h&&NDN.PITTable.splice(h,1);b.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,new UpcallInfo(d,a,0,null))==Closure.RESULT_REEXPRESS&&(1<LOG&&console.log("Re-express interest: "+a.name.to_uri()),e.timerID=setTimeout(g,
 f),NDN.PITTable.push(e),d.transport.send(c))};e.timerID=setTimeout(g,f)}this.transport.send(c)};
 NDN.prototype.registerPrefix=function(a,b,c){var d=this,e=function(){if(null==d.ccndid){var e=new Interest(NDN.ccndIdFetcher);e.interestLifetime=4E3;3<LOG&&console.log("Expressing interest for ccndid from ccnd.");d.reconnectAndExpressInterest(e,new NDN.FetchCcndidClosure(d,a,b,c))}else d.registerPrefixHelper(a,b,c)};null==this.host||null==this.port?null==this.getHostAndPort?console.log("ERROR: host OR port NOT SET"):this.connectAndExecute(e):e()};
 NDN.FetchCcndidClosure=function(a,b,c,d){Closure.call(this);this.ndn=a;this.name=b;this.callerClosure=c;this.flag=d};
@@ -311,9 +312,9 @@
 if(g.type==KeyLocatorType.KEYNAME)if(3<LOG&&console.log("KeyLocator contains KEYNAME"),g.keyName.contentName.match(a.name))3<LOG&&console.log("Content is key itself"),d=decodeSubjectPublicKeyInfo(a.content),c=d.verifyByteArray(a.rawSignatureData,f,c),c=!0==c?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD,b.upcall(c,new UpcallInfo(this,null,0,a));else{var h=NDN.getKeyByName(g.keyName);h?(3<LOG&&console.log("Local key cache hit"),d=h.rsaKey,c=d.verifyByteArray(a.rawSignatureData,f,c),c=!0==c?Closure.UPCALL_CONTENT:
 Closure.UPCALL_CONTENT_BAD,b.upcall(c,new UpcallInfo(this,null,0,a))):(3<LOG&&console.log("Fetch key according to keylocator"),a=new d(a,b,g.keyName,c,f),this.expressInterest(g.keyName.contentName.getPrefix(4),a))}else g.type==KeyLocatorType.KEY?(3<LOG&&console.log("Keylocator contains KEY"),d=decodeSubjectPublicKeyInfo(a.signedInfo.locator.publicKey),c=d.verifyByteArray(a.rawSignatureData,f,c),c=!0==c?Closure.UPCALL_CONTENT:Closure.UPCALL_CONTENT_BAD,b.upcall(Closure.UPCALL_CONTENT,new UpcallInfo(this,
 null,0,a))):(a=g.certificate,console.log("KeyLocator contains CERT"),console.log(a))}}}}else console.log("Incoming packet is not Interest or ContentObject. Discard now.")};
-NDN.prototype.connectAndExecute=function(a){var b=this.getHostAndPort();if(null==b)console.log("ERROR: No more hosts from getHostAndPort"),this.host=null;else if(b.host==this.host&&b.port==this.port)console.log("ERROR: The host returned by getHostAndPort is not alive: "+this.host+":"+this.port);else{this.host=b.host;this.port=b.port;3<LOG&&console.log("Connect: trying host from getHostAndPort: "+this.host);b=new Interest(new Name("/"));b.interestLifetime=4E3;var c=this,d=setTimeout(function(){3<LOG&&
-console.log("Connect: timeout waiting for host "+c.host);c.connectAndExecute(a)},3E3);this.reconnectAndExpressInterest(b,new NDN.ConnectClosure(this,a,d))}};NDN.ConnectClosure=function(a,b,c){Closure.call(this);this.ndn=a;this.onConnected=b;this.timerID=c};NDN.ConnectClosure.prototype.upcall=function(a){if(!(a==Closure.UPCALL_CONTENT||a==Closure.UPCALL_CONTENT_UNVERIFIED))return Closure.RESULT_ERR;clearTimeout(this.timerID);this.ndn.readyStatus=NDN.OPENED;this.ndn.onopen();this.onConnected();return Closure.RESULT_OK};
-var BinaryXmlElementReader=function(a){this.elementListener=a;this.dataParts=[];this.structureDecoder=new BinaryXMLStructureDecoder};
+NDN.prototype.connectAndExecute=function(a){var b=this.getHostAndPort();if(null==b)console.log("ERROR: No more hosts from getHostAndPort"),this.host=null;else if(b.host==this.host&&b.port==this.port)console.log("ERROR: The host returned by getHostAndPort is not alive: "+this.host+":"+this.port);else{this.host=b.host;this.port=b.port;0<LOG&&console.log("connectAndExecute: trying host from getHostAndPort: "+this.host);b=new Interest(new Name("/"));b.interestLifetime=4E3;var c=this,d=setTimeout(function(){0<
+LOG&&console.log("connectAndExecute: timeout waiting for host "+c.host);c.connectAndExecute(a)},3E3);this.reconnectAndExpressInterest(b,new NDN.ConnectClosure(this,a,d))}};NDN.ConnectClosure=function(a,b,c){Closure.call(this);this.ndn=a;this.onConnected=b;this.timerID=c};
+NDN.ConnectClosure.prototype.upcall=function(a){if(!(a==Closure.UPCALL_CONTENT||a==Closure.UPCALL_CONTENT_UNVERIFIED))return Closure.RESULT_ERR;clearTimeout(this.timerID);this.ndn.readyStatus=NDN.OPENED;this.ndn.onopen();0<LOG&&console.log("connectAndExecute: connected to host "+this.ndn.host);this.onConnected();return Closure.RESULT_OK};var BinaryXmlElementReader=function(a){this.elementListener=a;this.dataParts=[];this.structureDecoder=new BinaryXMLStructureDecoder};
 BinaryXmlElementReader.prototype.onReceivedData=function(a){for(;;)if(this.structureDecoder.seek(0),this.structureDecoder.findElementEnd(a)){this.dataParts.push(a.subarray(0,this.structureDecoder.offset));var b=DataUtils.concatArrays(this.dataParts);this.dataParts=[];try{this.elementListener.onReceivedElement(b)}catch(c){console.log("BinaryXmlElementReader: ignoring exception from onReceivedElement: "+c)}a=a.subarray(this.structureDecoder.offset,a.length);this.structureDecoder=new BinaryXMLStructureDecoder;
 if(0==a.length)break}else{this.dataParts.push(a);3<LOG&&console.log("Incomplete packet received. Length "+a.length+". Wait for more input.");break}};
 /** 
@@ -362,6 +363,7 @@
 	var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].createInstance
         (Components.interfaces.nsIInputStreamPump);
 	this.socket = transportService.createTransport(null, 0, ndn.host, ndn.port, null);
+    if (LOG > 0) console.log('XpcomTransport: Connected to ' + ndn.host + ":" + ndn.port);
     this.connectedHost = ndn.host;
     this.connectedPort = ndn.port;
     this.outStream = this.socket.openOutputStream(1, 0, 0);
diff --git a/tools/ndnProtocol.xpi b/tools/ndnProtocol.xpi
index ee9d716..e14c06a 100644
--- a/tools/ndnProtocol.xpi
+++ b/tools/ndnProtocol.xpi
Binary files differ