Add interest timeout
diff --git a/js/tools/build/ndn-js-uncomp.js b/js/tools/build/ndn-js-uncomp.js
index d8ebcfa..fa00a92 100644
--- a/js/tools/build/ndn-js-uncomp.js
+++ b/js/tools/build/ndn-js-uncomp.js
@@ -1,4 +1,4 @@
-/* 
+/**
  * @author: Jeff Thompson
  * See COPYING for copyright and distribution information.
  * Provide the callback closure for the async communication methods in the NDN class.
@@ -18,6 +18,8 @@
 	// Use instance variables to return data to callback
 	this.ndn_data = null;  // this holds the ndn_closure
     this.ndn_data_dirty = false;
+    
+    this.timerID = -1;  // Store the interest timer; used to cancel the timer upon receiving interest
 };
 
 // Upcall result
@@ -62,13 +64,13 @@
 	ret += "\nContentObject: " + this.contentObject;
 	return ret;
 }
-/*
+/**
  * @author: Meki Cherkaoui, Jeff Thompson, Wentao Shang
  * See COPYING for copyright and distribution information.
  * This class represents the top-level object for communicating with an NDN host.
  */
 
-var LOG = 3;
+var LOG = 0;
 
 /**
  * settings is an associative array with the following defaults:
@@ -76,8 +78,8 @@
  *   host: 'localhost',
  *   port: 9696,
  *   getTransport: function() { return new WebSocketTransport(); }
- *   onopen: function() { console.log("NDN connection established."); }
- *   onclose: function() { console.log("NDN connection closed."); }
+ *   onopen: function() { if (LOG > 3) console.log("NDN connection established."); }
+ *   onclose: function() { if (LOG > 3) console.log("NDN connection closed."); }
  * }
  */
 var NDN = function NDN(settings) {
@@ -88,14 +90,16 @@
     this.transport = getTransport();
     this.readyStatus = NDN.UNOPEN;
     // Event handler
-    this.onopen = (settings.onopen || function() { console.log("NDN connection established."); });
-    this.onclose = (settings.onclose || function() { console.log("NDN connection closed."); });
+    this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
+    this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("NDN connection closed."); });
 };
 
 NDN.UNOPEN = 0;  // created but not opened yet
 NDN.OPENED = 1;  // connection to ccnd opened
 NDN.CLOSED = 2;  // connection to ccnd closed
 
+NDN.InterestTimeOut = 5000; // 5000 ms timeout for pending interest
+
 /* Java Socket Bridge and XPCOM transport */
 
 NDN.prototype.createRoute = function(host,port){
@@ -141,7 +145,7 @@
 NDN.prototype.registerPrefix = function(name, closure, flag) {
     return this.transport.registerPrefix(this, name, closure, flag);
 }
-/* 
+/** 
  * @author: Wentao Shang
  * See COPYING for copyright and distribution information.
  * Implement getAsync and putAsync used by NDN using nsISocketTransportService.
@@ -156,27 +160,6 @@
 	this.structureDecoder = new BinaryXMLStructureDecoder();
 };
 
-WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
-	if (this.ws != null) {
-		//TODO: check local content store first
-
-        var binaryInterest = encodeToBinaryInterest(interest);
-		var bytearray = new Uint8Array(binaryInterest.length);
-		bytearray.set(binaryInterest);
-		
-		var pitEntry = new PITEntry(interest.name.getName(), closure);
-		PITTable.push(pitEntry);
-		
-		this.ws.send(bytearray.buffer);
-		console.log('ws.send() returned.');
-	}
-	else{
-		console.log('WebSocket connection is not established.');
-		return null;
-	}
-};
-
-
 var ccndIdFetcher = '/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY';
 
 WebSocketTransport.prototype.connectWebSocket = function(ndn) {
@@ -184,7 +167,7 @@
 		delete this.ws;
 	
 	this.ws = new WebSocket('ws://' + ndn.host + ':' + ndn.port);
-	console.log('ws connection created.');
+	if (LOG > 0) console.log('ws connection created.');
 	
 	this.ws.binaryType = "arraybuffer";
 	
@@ -198,7 +181,7 @@
 		} else if (result instanceof ArrayBuffer) {
 	        var bytearray = new Uint8Array(result);
 	        
-			if (LOG>3) console.log('BINARY RESPONSE IS ' + bytearray);
+			if (LOG>3) console.log('BINARY RESPONSE IS ' + DataUtils.toHex(bytearray));
 			
 			try {
 				if (bytearray.length + self.buffer.byteOffset >= self.buffer.byteLength) {
@@ -231,13 +214,13 @@
 			var decoder = new BinaryXMLDecoder(self.buffer);
 			// Dispatch according to packet type
 			if (decoder.peekStartElement(CCNProtocolDTags.Interest)) {  // Interest packet
-				console.log('Interest packet received.');
+				if (LOG > 3) console.log('Interest packet received.');
 				
 				var interest = new Interest();
 				interest.from_ccnb(decoder);
 				if (LOG>3) console.log(interest);
 				var nameStr = escape(interest.name.getName());
-				console.log(nameStr);
+				if (LOG > 3) console.log(nameStr);
 				
 				var entry = getEntryForRegisteredPrefix(nameStr);
 				if (entry != null) {
@@ -246,38 +229,44 @@
 				}
 				
 			} else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) {  // Content packet
-				console.log('ContentObject packet received.');
+				if (LOG > 3) console.log('ContentObject packet received.');
 				
 				var co = new ContentObject();
 				co.from_ccnb(decoder);
-				if (LOG>3) console.log(co);
+				if (LOG > 3) console.log(co);
 				nameStr = co.name.getName();
-				console.log(nameStr);
+				if (LOG > 3) console.log(nameStr);
 				
 				if (self.ccndid == null && nameStr.match(ccndIdFetcher) != null) {
 					// We are in starting phase, record publisherPublicKeyDigest in self.ccndid
 					if(!co.signedInfo || !co.signedInfo.publisher 
 						|| !co.signedInfo.publisher.publisherPublicKeyDigest) {
-						console.log("Cannot contact router");
+						console.log("Cannot contact router, close NDN now.");
 						
 						// Close NDN if we fail to connect to a ccn router
 						ndn.readyStatus = NDN.CLOSED;
 						ndn.onclose();
-						console.log("NDN.onclose event fired.");
+						//console.log("NDN.onclose event fired.");
 					} else {
-						console.log('Connected to ccnd.');
+						//console.log('Connected to ccnd.');
 						self.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
 						if (LOG>3) console.log(self.ccndid);
 						
 						// Call NDN.onopen after success
 						ndn.readyStatus = NDN.OPENED;
 						ndn.onopen();
-						console.log("NDN.onopen event fired.");
+						//console.log("NDN.onopen event fired.");
 					}
 				} else {
 					var pitEntry = getEntryForExpressedInterest(nameStr);
 					if (pitEntry != null) {
 						//console.log(pitEntry);
+						
+						// Cancel interest timer
+						clearTimeout(pitEntry.closure.timerID);
+						//console.log("Clear interest timer");
+						//console.log(pitEntry.closure.timerID);
+						// Raise callback
 						pitEntry.closure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(ndn, null, 0, co));
 					}
 				}
@@ -296,12 +285,12 @@
 	}
 	
 	this.ws.onopen = function(ev) {
-		console.log(ev);
-		console.log('ws.onopen: WebSocket connection opened.');
-		console.log('ws.onopen: ReadyState: ' + this.readyState);
+		if (LOG > 3) console.log(ev);
+		if (LOG > 3) console.log('ws.onopen: WebSocket connection opened.');
+		if (LOG > 3) console.log('ws.onopen: ReadyState: ' + this.readyState);
 
 		// Fetch ccndid now
-		interest = new Interest(new Name(ccndIdFetcher));
+		var interest = new Interest(new Name(ccndIdFetcher));
 		interest.InterestLifetime = 4200;
 		//var hex = encodeToHexInterest(interest);
 		var hex = encodeToBinaryInterest(interest);
@@ -329,7 +318,7 @@
 		// Close NDN when WebSocket is closed
 		ndn.readyStatus = NDN.CLOSED;
 		ndn.onclose();
-		console.log("NDN.onclose event fired.");
+		//console.log("NDN.onclose event fired.");
 	}
 }
 
@@ -351,6 +340,38 @@
 	return null;
 }
 
+WebSocketTransport.prototype.expressInterest = function(ndn, interest, closure) {
+	if (this.ws != null) {
+		//TODO: check local content store first
+
+        var binaryInterest = encodeToBinaryInterest(interest);
+		var bytearray = new Uint8Array(binaryInterest.length);
+		bytearray.set(binaryInterest);
+		
+		var pitEntry = new PITEntry(interest.name.getName(), closure);
+		PITTable.push(pitEntry);
+		
+		this.ws.send(bytearray.buffer);
+		if (LOG > 3) console.log('ws.send() returned.');
+		
+		// Set interest timer
+		closure.timerID = setTimeout(function() {
+			console.log("Interest time out.");
+			
+			// Remove PIT entry from PITTable
+			index = PITTable.indexOf(pitEntry);
+			//console.log(PITTable);
+			PITTable.splice(index, 1);
+			//console.log(PITTable);
+			// Raise closure callback
+			closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT, new UpcallInfo(ndn, interest, 0, null));
+		}, NDN.InterestTimeOut);
+		//console.log(closure.timerID);
+	}
+	else
+		console.log('WebSocket connection is not established.');
+};
+
 
 // For publishing data
 var CSTable = new Array();
@@ -372,7 +393,7 @@
 	if (this.ws != null) {
 		if (this.ccndid == null) {
 			console.log('ccnd node ID unkonwn. Cannot register prefix.');
-			return;
+			return -1;
 		}
 		
 		var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
@@ -402,7 +423,7 @@
 		//    ---Wentao
     	var bytearray = new Uint8Array(binaryInterest.length);
 		bytearray.set(binaryInterest);
-		console.log('Send Interest registration packet.');
+		if (LOG > 3) console.log('Send Interest registration packet.');
     	
     	var csEntry = new CSEntry(name.getName(), closure);
 		CSTable.push(csEntry);
@@ -876,6 +897,32 @@
     return -1;
 }
 
+/*
+ * Find the last component in name that has a ContentDigest and return the digest value as Uint8Array, 
+ *   or null if not found.
+ * A ContentDigest component is Name.ContentDigestPrefix + 32 bytes + Name.ContentDigestSuffix.
+ */
+Name.prototype.getContentDigestValue = function() {
+    var digestComponentLength = Name.ContentDigestPrefix.length + 32 + Name.ContentDigestSuffix.length; 
+    for (var i = this.components.length - 1; i >= 0; --i) {
+        // Check for the correct length and equal ContentDigestPrefix and ContentDigestSuffix.
+        if (this.components[i].length == digestComponentLength &&
+            DataUtils.arraysEqual(this.components[i].subarray(0, Name.ContentDigestPrefix.length), 
+                                  Name.ContentDigestPrefix) &&
+            DataUtils.arraysEqual(this.components[i].subarray
+               (this.components[i].length - Name.ContentDigestSuffix.length, this.components[i].length),
+                                  Name.ContentDigestSuffix))
+           return this.components[i].subarray
+               (Name.ContentDigestPrefix.length, Name.ContentDigestPrefix.length + 32);
+    }
+    
+    return null;
+}
+
+// Meta GUID "%C1.M.G%C1" + ContentDigest with a 32 byte BLOB. 
+Name.ContentDigestPrefix = new Uint8Array([0xc1, 0x2e, 0x4d, 0x2e, 0x47, 0xc1, 0x01, 0xaa, 0x02, 0x85]);
+Name.ContentDigestSuffix = new Uint8Array([0x00]);
+
 /**
  * Return component as an escaped string according to "CCNx URI Scheme".
  * We can't use encodeURIComponent because that doesn't encode all the characters we want to.
@@ -1108,26 +1155,6 @@
 	this.digestAlgorithm = _digestAlgorithm//String _digestAlgorithm;
 };
 
-var generateSignature = function(contentName,content,signedinfo){
-	
-	var enc = new BinaryXMLEncoder();
-	contentName.to_ccnb(enc);
-	var hex1 = toHex(enc.getReducedOstream());
-
-	var enc = new BinaryXMLEncoder();
-	content.to_ccnb(enc);
-	var hex2 = toHex(enc.getReducedOstream());
-
-	var enc = new BinaryXMLEncoder();
-	signedinfo.to_ccnb(enc);
-	var hex3 = toHex(enc.getReducedOstream());
-
-	var hex = hex1+hex2+hex3;
-
-	//globalKeyManager.sig
-
-};
-
 Signature.prototype.from_ccnb =function( decoder) {
 		decoder.readStartElement(this.getElementLabel());
 		
@@ -4761,8 +4788,10 @@
 {

   //console.log('Raw string comming is '+input);

   var output = Array(input.length >> 2);

+  /* JavaScript automatically zeroizes a new array.

   for(var i = 0; i < output.length; i++)

     output[i] = 0;

+   */

   for(var i = 0; i < input.length * 8; i += 8)

     output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);

   return output;

@@ -4777,8 +4806,10 @@
 function byteArray2binb(input){

 	//console.log("Byte array coming is " + input);

 	var output = Array(input.length >> 2);

+      /* JavaScript automatically zeroizes a new array.

 	  for(var i = 0; i < output.length; i++)

 	    output[i] = 0;

+       */

 	  for(var i = 0; i < input.length * 8; i += 8)

 	    output[i>>5] |= (input[i / 8] & 0xFF) << (24 - i % 32);

 	  return output;

@@ -4831,15 +4862,25 @@
   var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534,

                        1359893119, -1694144372, 528734635, 1541459225);

   var W = new Array(64);

-  var a, b, c, d, e, f, g, h;

-  var i, j, T1, T2;

 

   /* append padding */

   m[l >> 5] |= 0x80 << (24 - l % 32);

   m[((l + 64 >> 9) << 4) + 15] = l;

+ 

+  for(var offset = 0; offset < m.length; offset += 16)

+    processBlock_sha256(m, offset, HASH, W);

 

-  for(i = 0; i < m.length; i += 16)

-  {

+  return HASH;

+}

+

+/*

+ * Process a block of 16 4-byte words in m starting at offset and update HASH.  

+ * offset must be a multiple of 16 and less than m.length.  W is a scratchpad Array(64).

+ */

+function processBlock_sha256(m, offset, HASH, W) {

+    var a, b, c, d, e, f, g, h;

+    var j, T1, T2;

+    

     a = HASH[0];

     b = HASH[1];

     c = HASH[2];

@@ -4851,7 +4892,7 @@
 

     for(j = 0; j < 64; j++)

     {

-      if (j < 16) W[j] = m[j + i];

+      if (j < 16) W[j] = m[j + offset];

       else W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),

                                             sha256_Gamma0256(W[j - 15])), W[j - 16]);

 

@@ -4876,8 +4917,6 @@
     HASH[5] = safe_add(f, HASH[5]);

     HASH[6] = safe_add(g, HASH[6]);

     HASH[7] = safe_add(h, HASH[7]);

-  }

-  return HASH;

 }

 

 function safe_add (x, y)

@@ -4886,6 +4925,90 @@
   var msw = (x >> 16) + (y >> 16) + (lsw >> 16);

   return (msw << 16) | (lsw & 0xFFFF);

 }

+

+/*

+ * Create a Sha256, call update(data) multiple times, then call finalize().

+ */

+var Sha256 = function Sha256() {

+    this.W = new Array(64);

+    this.hash = new Array(1779033703, -1150833019, 1013904242, -1521486534,

+                          1359893119, -1694144372, 528734635, 1541459225);

+    this.nTotalBytes = 0;

+    this.buffer = new Uint8Array(16 * 4);

+    this.nBufferBytes = 0;

+}

+

+/*

+ * Update the hash with data, which is Uint8Array.

+ */

+Sha256.prototype.update = function(data) {

+    this.nTotalBytes += data.length;

+    

+    if (this.nBufferBytes > 0) {

+        // Fill up the buffer and process it first.

+        var bytesNeeded = this.buffer.length - this.nBufferBytes;

+        if (data.length < bytesNeeded) {

+            this.buffer.set(data, this.nBufferBytes);

+            this.nBufferBytes += data.length;

+            return;

+        }

+        else {

+            this.buffer.set(data.subarray(0, bytesNeeded), this.nBufferBytes);

+            processBlock_sha256(byteArray2binb(this.buffer), 0, this.hash, this.W);

+            this.nBufferBytes = 0;

+            // Consume the bytes from data.

+            data = data.subarray(bytesNeeded, data.length);

+            if (data.length == 0)

+                return;

+        }

+    }

+    

+    // 2^6 is 16 * 4.

+    var nBlocks = data.length >> 6;

+    if (nBlocks > 0) {

+        var nBytes = nBlocks * 16 * 4;

+        var m = byteArray2binb(data.subarray(0, nBytes));

+        for(var offset = 0; offset < m.length; offset += 16)

+            processBlock_sha256(m, offset, this.hash, this.W);

+

+        data = data.subarray(nBytes, data.length);

+    }

+    

+    if (data.length > 0) {

+        // Save the remainder in the buffer.

+        this.buffer.set(data);

+        this.nBufferBytes = data.length;

+    }

+}

+

+/*

+ * Finalize the hash and return the result as Uint8Array.

+ * Only call this once.  Return values on subsequent calls are undefined.

+ */

+Sha256.prototype.finalize = function() {

+    var m = byteArray2binb(this.buffer.subarray(0, this.nBufferBytes));

+    /* append padding */

+    var l = this.nBufferBytes * 8;

+    m[l >> 5] |= 0x80 << (24 - l % 32);

+    m[((l + 64 >> 9) << 4) + 15] = this.nTotalBytes * 8;

+

+    for(var offset = 0; offset < m.length; offset += 16)

+        processBlock_sha256(m, offset, this.hash, this.W);

+

+    return Sha256.binb2Uint8Array(this.hash);

+}

+

+/*

+ * Convert an array of big-endian words to Uint8Array.

+ */

+Sha256.binb2Uint8Array = function(input)

+{

+    var output = new Uint8Array(input.length * 4);

+    var iOutput = 0;

+    for (var i = 0; i < input.length * 32; i += 8)

+        output[iOutput++] = (input[i>>5] >>> (24 - i % 32)) & 0xFF;

+    return output;

+}

 var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

 var b64pad="=";