Author: nextgens
Date: 2006-02-03 22:29:13 +0000 (Fri, 03 Feb 2006)
New Revision: 7997

Added:
   branches/freenet-freejvms/src/freenet/crypt/DSA.java
   branches/freenet-freejvms/src/freenet/crypt/DSAGroupGenerator.java
   branches/freenet-freejvms/src/freenet/crypt/DSAPrivateKey.java
   branches/freenet-freejvms/src/freenet/crypt/DSAPublicKey.java
   branches/freenet-freejvms/src/freenet/keys/ClientKeyBlock.java
   branches/freenet-freejvms/src/freenet/keys/ClientSSK.java
   branches/freenet-freejvms/src/freenet/keys/ClientSSKBlock.java
   branches/freenet-freejvms/src/freenet/keys/NodeSSK.java
   branches/freenet-freejvms/src/freenet/keys/SSKBlock.java
   branches/freenet-freejvms/src/freenet/keys/SSKDecodeException.java
   branches/freenet-freejvms/src/freenet/keys/SSKVerifyException.java
   branches/freenet-freejvms/src/freenet/node/TestnetStatusUploader.java
Modified:
   branches/freenet-freejvms/.classpath
   branches/freenet-freejvms/build.xml
   branches/freenet-freejvms/run.sh
   branches/freenet-freejvms/src/freenet/client/ArchiveManager.java
   branches/freenet-freejvms/src/freenet/client/Fetcher.java
   branches/freenet-freejvms/src/freenet/client/HighLevelSimpleClientImpl.java
   branches/freenet-freejvms/src/freenet/client/Metadata.java
   branches/freenet-freejvms/src/freenet/keys/CHKBlock.java
   branches/freenet-freejvms/src/freenet/keys/ClientCHK.java
   branches/freenet-freejvms/src/freenet/keys/ClientCHKBlock.java
   branches/freenet-freejvms/src/freenet/keys/ClientKey.java
   branches/freenet-freejvms/src/freenet/keys/Key.java
   branches/freenet-freejvms/src/freenet/keys/KeyBlock.java
   branches/freenet-freejvms/src/freenet/keys/NodeCHK.java
   branches/freenet-freejvms/src/freenet/node/Node.java
   branches/freenet-freejvms/src/freenet/node/QueuedDataRequest.java
   branches/freenet-freejvms/src/freenet/node/QueueingSimpleLowLevelClient.java
   branches/freenet-freejvms/src/freenet/node/RealNodeRequestInsertTest.java
   branches/freenet-freejvms/src/freenet/node/RequestStarterClient.java
   branches/freenet-freejvms/src/freenet/node/SimpleLowLevelClient.java
   branches/freenet-freejvms/src/freenet/node/TestnetHandler.java
   branches/freenet-freejvms/src/freenet/node/TextModeClientInterface.java
   branches/freenet-freejvms/src/freenet/node/Version.java
   branches/freenet-freejvms/src/freenet/store/DataStore.java
Log:
Build 309 : merged changes from trunk since 7751

Modified: branches/freenet-freejvms/.classpath
===================================================================
--- branches/freenet-freejvms/.classpath        2006-02-03 21:46:40 UTC (rev 
7996)
+++ branches/freenet-freejvms/.classpath        2006-02-03 22:29:13 UTC (rev 
7997)
@@ -2,6 +2,6 @@
 <classpath>
        <classpathentry 
excluding="test/**|org/spaceroots/mantissa/random/MersenneTwisterTest.java" 
kind="src" path="src"/>
        <classpathentry kind="con" 
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-       <classpathentry kind="lib" 
path="/usr/src/cvs/freenet-stable/lib/freenet-ext.jar"/>
+       <classpathentry kind="lib" path="lib/freenet-ext.jar"/>
        <classpathentry kind="output" path="bin"/>
 </classpath>

Modified: branches/freenet-freejvms/build.xml
===================================================================
--- branches/freenet-freejvms/build.xml 2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/build.xml 2006-02-03 22:29:13 UTC (rev 7997)
@@ -6,31 +6,44 @@
      This file builds freenet...
      Possible targets: compile, dist (default), clean
   </description>
-  <!-- set global properties for this build -->
-  <property name="src" location="src"/>
-  <property name="build" location="build"/>
- 
+
+<!-- set global properties for this build -->
+       <property name="src" location="src"/>
+       <property name="build" location="build"/>
+       <property name="lib"    location="lib"/>
+       <property name="freenet-ext.location" 
location="${lib}/freenet-ext.jar"/>
+
+<target name="mkdir">
+    <mkdir dir="${build}"/>
+    <mkdir dir="${lib}"/>
+</target>
+
+<target name="env" depends="mkdir"   description="Learn about the environment">
+    <available file="${lib}/freenet-ext.jar" property="freenet-ext.present"/>
+</target>
+
+  <target name="get-extjar" depends="env" unless="freenet-ext.present"
+    description="Download some external libraries which Freenet relies on">
+    <mkdir dir="${lib}"/>
+    <get src="http://downloads.freenetproject.org/alpha/freenet-ext.jar"; 
+                       dest="${freenet-ext.location}" 
+        verbose="true"
+        usetimestamp="true"/>
+               <property name="freenet-ext.present" value="true"/>
+  </target>
     <!-- ================================================== -->
-  <target name="compile">
-
+  <target name="compile" depends="get-extjar">
     <!-- Create the time stamp -->
     <tstamp/>
     <!-- Create the build directory structure used by compile -->
-    <mkdir dir="${build}"/>

 <!-- FIXME: remove the debug and replace with optimize -->
-    <javac srcdir="${src}" destdir="${build}" source="1.4" debug="on" 
optimize="on">
-  <classpath>
-       <pathelement location="freenet-ext.jar"/>
-       <pathelement location="gnu-crypto.jar"/>
-       <pathelement location="javax-security.jar"/>
-       <pathelement location="javax-crypto.jar"/>
-  </classpath>
+    <javac srcdir="${src}" destdir="${build}" debug="on" optimize="on" 
source="1.4">
+    <classpath>
+       <pathelement location="${freenet-ext.location}"/>
+    </classpath>
 <!-- following a very temporary list of files to be build -->
-      <include name="*.class"/>
       <include name="org/**/*.java"/>
-      <include name="com/**/*.java"/>
-      <include name="com/onionnetworks/**/*.class"/>
       <include name="freenet/**/*.java"/>
       <include name="net/i2p/util/*.java"/>
       <exclude name="org/**/MersenneTwisterTest.java"/>
@@ -44,7 +57,7 @@
     <!-- Create the distribution directory -->
     <!--<mkdir dir="."/>-->
     <!-- Put everything in ${build} into the freenet-${DSTAMP}.jar file -->
-    <jar jarfile="freenet-cvs-snapshot.jar" basedir="${build}">
+    <jar jarfile="${lib}/freenet-cvs-snapshot.jar" basedir="${build}">
       <manifest>
       <attribute name="Main-Class" value="freenet/node/Node"/>
          <attribute name="Built-By" value="${user.name}"/>
@@ -65,5 +78,10 @@
         <delete dir="${build}"/>
         <!--<delete><fileset dir="src" includes="**/*.class"/></delete>-->
     </target>
+    <target name="distclean" description="Delete class files, lib dir and docs 
dir.">
+        <delete dir="${build}"/>
+        <delete dir="${lib}"/>
+        <!--<delete><fileset dir="src" includes="**/*.class"/></delete>-->
+    </target>

 </project>

Modified: branches/freenet-freejvms/run.sh
===================================================================
--- branches/freenet-freejvms/run.sh    2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/run.sh    2006-02-03 22:29:13 UTC (rev 7997)
@@ -1,3 +1,7 @@
+echo "Compiling freenet-ext.jar"
 gcj -c -g -fjni -Ifreenet-cvs-snapshot.jar -Ifreenet-ext.jar 
-Ignu-crypto-der.jar -Ignu-crypto.jar -o freenet-ext.o freenet-ext.jar 
+echo "Compiling freenet-cvs-snapshot.jar"
 gcj -c -g -fjni -Ifreenet-cvs-snapshot.jar -Ifreenet-ext.jar 
-Ignu-crypto-der.jar -Ignu-crypto.jar -o freenet.o freenet-cvs-snapshot.jar 
+echo "Linking"
 gcj  -fjni -combine -g -Ifreenet-cvs-snapshot.jar -Ifreenet-ext.jar 
-Ignu-crypto-der.jar -Ignu-crypto.jar --main=freenet.node.Node  -o freenet  
-Lbin/lib/linux/x86/ -Lbin/net/i2p/util/   -lfec16 -lfec8 -ljbigi-linux-athlon 
-lgmp freenet-ext.o freenet.o  
+echo "Done"

Modified: branches/freenet-freejvms/src/freenet/client/ArchiveManager.java
===================================================================
--- branches/freenet-freejvms/src/freenet/client/ArchiveManager.java    
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/client/ArchiveManager.java    
2006-02-03 22:29:13 UTC (rev 7997)
@@ -111,9 +111,10 @@
         * @param archiveType The archive type, defined in Metadata.
         * @return An archive handler. 
         */
-       public synchronized ArchiveHandler makeHandler(FreenetURI key, short 
archiveType) {
+       public synchronized ArchiveHandler makeHandler(FreenetURI key, short 
archiveType, boolean returnNullIfNotFound) {
                ArchiveHandler handler = getCached(key);
                if(handler != null) return handler;
+               if(returnNullIfNotFound) return null;
                handler = new ArchiveStoreContext(this, key, archiveType);
                putCached(key, handler);
                return handler;

Modified: branches/freenet-freejvms/src/freenet/client/Fetcher.java
===================================================================
--- branches/freenet-freejvms/src/freenet/client/Fetcher.java   2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/client/Fetcher.java   2006-02-03 
22:29:13 UTC (rev 7997)
@@ -7,7 +7,9 @@
 import freenet.client.events.DecodedBlockEvent;
 import freenet.client.events.FetchedMetadataEvent;
 import freenet.client.events.GotBlockEvent;
+import freenet.keys.ClientCHK;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.FreenetURI;
 import freenet.keys.KeyBlock;
 import freenet.keys.KeyDecodeException;
@@ -107,7 +109,7 @@
                        throw new 
FetchException(FetchException.TOO_MUCH_RECURSION, ""+recursionLevel+" should be 
< "+ctx.maxRecursionLevel);

                // Do the fetch
-               KeyBlock block;
+               ClientKeyBlock block;
                try {
                        block = ctx.client.getKey(key, localOnly, 
ctx.starterClient, ctx.cacheLocalRequests);
                } catch (LowLevelGetException e) {
@@ -138,7 +140,7 @@

                Bucket data;
                try {
-                       data = block.decode(key, ctx.bucketFactory, (int) 
(Math.min(ctx.maxTempLength, Integer.MAX_VALUE)));
+                       data = block.decode(ctx.bucketFactory, (int) 
(Math.min(ctx.maxTempLength, Integer.MAX_VALUE)));
                } catch (KeyDecodeException e1) {
                        throw new 
FetchException(FetchException.BLOCK_DECODE_ERROR, e1.getMessage());
                } catch (IOException e) {
@@ -148,7 +150,7 @@

                ctx.eventProducer.produceEvent(new DecodedBlockEvent(key));

-               if(!key.isMetadata()) {
+               if(!block.isMetadata()) {
                        // Just return the data
                        return new FetchResult(dm, data);
                }
@@ -202,7 +204,7 @@
                        }
                        return runMetadata(dm, recursionLevel, key, 
metaStrings, metadata, container, thisKey, dontEnterImplicitArchives, 
localOnly);
                } else if(metadata.isArchiveManifest()) {
-                       container = ctx.archiveManager.makeHandler(thisKey, 
metadata.getArchiveType());
+                       container = ctx.archiveManager.makeHandler(thisKey, 
metadata.getArchiveType(), false);
                        Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                        try {
                                metadata = Metadata.construct(metadataBucket);
@@ -223,7 +225,7 @@
                                 */
                                if((!dontEnterImplicitArchives) && 
ArchiveManager.isUsableArchiveType(dm.getMIMEType()) && 
(!metaStrings.isEmpty())) {
                                        // Possible implicit archive inside 
archive?
-                                       container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()));
+                                       container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()), false);
                                        Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                                        try {
                                                metadata = 
Metadata.construct(metadataBucket);
@@ -250,17 +252,16 @@
                        FreenetURI uri = metadata.getSingleTarget();
                        dm.mergeNoOverwrite(metadata.getClientMetadata());
                        if((!dontEnterImplicitArchives) && 
ArchiveManager.isUsableArchiveType(dm.getMIMEType()) && 
(!metaStrings.isEmpty())) {
+                               // Is probably an implicit archive.
                                ClientKey target;
                                try {
                                        target = ClientKey.getBaseKey(uri);
                                } catch (MalformedURLException e1) {
                                        throw new 
FetchException(FetchException.INVALID_URI, "Invalid URI: "+uri);
                                }
-                               if(!(target.isMetadata())) {
-                                       // Target *is not* metadata.
-                                       // Therefore target is a usable archive.
-                                       // We might not have to fetch it.
-                                       container = 
ctx.archiveManager.makeHandler(uri, 
ArchiveManager.getArchiveType(dm.getMIMEType()));
+                               // Probably a usable archive as-is. We may not 
have to fetch it.
+                               container = ctx.archiveManager.makeHandler(uri, 
ArchiveManager.getArchiveType(dm.getMIMEType()), true);
+                               if(container != null) {
                                        Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                                        try {
                                                metadata = 
Metadata.construct(metadataBucket);
@@ -268,7 +269,7 @@
                                                throw new 
FetchException(FetchException.BUCKET_ERROR);
                                        }
                                        return runMetadata(dm, 
recursionLevel+1, key, metaStrings, metadata, container, thisKey, 
dontEnterImplicitArchives, localOnly);
-                               }
+                               } // else just fetch it, create context later
                        }
                        FetchResult fr = realRun(dm, recursionLevel, uri, 
dontEnterImplicitArchives, localOnly);
                        if(metadata.compressed) {
@@ -293,7 +294,7 @@
                        dm.mergeNoOverwrite(metadata.getClientMetadata()); // 
even splitfiles can have mime types!
                        if((!dontEnterImplicitArchives) && 
ArchiveManager.isUsableArchiveType(dm.getMIMEType()) && 
(!metaStrings.isEmpty())) {
                                // We know target is not metadata.
-                               container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()));
+                               container = 
ctx.archiveManager.makeHandler(thisKey, 
ArchiveManager.getArchiveType(dm.getMIMEType()), false);
                                Bucket metadataBucket = 
container.getMetadata(archiveContext, ctx, dm, recursionLevel, true);
                                try {
                                        metadata = 
Metadata.construct(metadataBucket);

Modified: 
branches/freenet-freejvms/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- branches/freenet-freejvms/src/freenet/client/HighLevelSimpleClientImpl.java 
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/client/HighLevelSimpleClientImpl.java 
2006-02-03 22:29:13 UTC (rev 7997)
@@ -103,6 +103,13 @@
                return i.run(insert, false, getCHKOnly, false);
        }

+       public FreenetURI insert(InsertBlock insert, boolean getCHKOnly, 
boolean metadata) throws InserterException {
+               InserterContext context = new InserterContext(client, 
bucketFactory, random, INSERT_RETRIES, CONSECUTIVE_RNFS_ASSUME_SUCCESS,
+                               SPLITFILE_INSERT_THREADS, 
SPLITFILE_BLOCKS_PER_SEGMENT, SPLITFILE_CHECK_BLOCKS_PER_SEGMENT, 
globalEventProducer, insertStarter, cacheLocalRequests);
+               FileInserter i = new FileInserter(context);
+               return i.run(insert, metadata, getCHKOnly, false);
+       }
+
        public void addGlobalHook(ClientEventListener listener) {
                globalEventProducer.addEventListener(listener);
        }

Modified: branches/freenet-freejvms/src/freenet/client/Metadata.java
===================================================================
--- branches/freenet-freejvms/src/freenet/client/Metadata.java  2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/client/Metadata.java  2006-02-03 
22:29:13 UTC (rev 7997)
@@ -236,11 +236,99 @@
        }

        /**
+        * Create an empty Metadata object 
+        */
+       private Metadata() {
+               // Should be followed by addRedirectionManifest
+       }
+       
+       /**
+        * Create a Metadata object and add data for redirection to it.
+        * 
+        * @param dir A map of names (string) to either files (same string) or
+        * directories (more HashMap's)
+        * @throws MalformedURLException One of the URI:s were malformed
+        */
+       private void addRedirectionManifest(HashMap dir) throws 
MalformedURLException {
+               // Simple manifest - contains actual redirects.
+               // Not zip manifest, which is basically a redirect.
+               documentType = SIMPLE_MANIFEST;
+               noMIME = true;
+               //mimeType = null;
+               //clientMetadata = new ClientMetadata(null);
+               manifestEntries = new HashMap();
+               int count = 0;
+               for(Iterator i = dir.keySet().iterator();i.hasNext();) {
+                       String key = (String) i.next();
+                       count++;
+                       Object o = dir.get(key);
+                       Metadata target;
+                       if(o instanceof String) {
+                               // External redirect
+                               FreenetURI uri = new FreenetURI((String)o);
+                               target = new Metadata(SIMPLE_REDIRECT, uri, 
null);
+                       } else if(o instanceof HashMap) {
+                               target = new Metadata();
+                               target.addRedirectionManifest((HashMap)o);
+                       } else throw new IllegalArgumentException("Not String 
nor HashMap: "+o);
+                       byte[] data = target.writeToByteArray();
+                       manifestEntries.put(key, data);
+               }
+               manifestEntryCount = count;
+               
+       }
+       
+       /**
+        * Create a Metadata object and add data for redirection to it.
+        * 
+        * @param dir A map of names (string) to either files (same string) or
+        * directories (more HashMap's)
+        * @throws MalformedURLException One of the URI:s were malformed
+        */
+       public static Metadata mkRedirectionManifest(HashMap dir) throws 
MalformedURLException {
+               Metadata ret = new Metadata();
+               ret.addRedirectionManifest(dir);
+               return ret;
+       }
+       
+       /**
         * Create a Metadata object for an archive which does not have its own
         * metadata.
         * @param dir A map of names (string) to either files (same string) or
         * directories (more HashMap's)
         */
+       public void addManifest(HashMap dir) {
+               // Simple manifest - contains actual redirects.
+               // Not zip manifest, which is basically a redirect.
+               documentType = SIMPLE_MANIFEST;
+               noMIME = true;
+               //mimeType = null;
+               //clientMetadata = new ClientMetadata(null);
+               manifestEntries = new HashMap();
+               int count = 0;
+               for(Iterator i = dir.keySet().iterator();i.hasNext();) {
+                       String key = (String) i.next();
+                       count++;
+                       Object o = dir.get(key);
+                       Metadata target;
+                       if(o instanceof String) {
+                               // Zip internal redirect
+                               target = new Metadata(ZIP_INTERNAL_REDIRECT, 
key);
+                       } else if(o instanceof HashMap) {
+                               target = new Metadata((HashMap)o);
+                       } else throw new IllegalArgumentException("Not String 
nor HashMap: "+o);
+                       byte[] data = target.writeToByteArray();
+                       manifestEntries.put(key, data);
+               }
+               manifestEntryCount = count;
+       }
+       
+       /**
+        * Create a Metadata object for an archive which does not have its own
+        * metadata.
+        * @param dir A map of names (string) to either files (same string) or
+        * directories (more HashMap's)
+        */
        Metadata(HashMap dir) {
                // Simple manifest - contains actual redirects.
                // Not zip manifest, which is basically a redirect.

Copied: branches/freenet-freejvms/src/freenet/crypt/DSA.java (from rev 7751, 
trunk/freenet/src/freenet/crypt/DSA.java)

Copied: branches/freenet-freejvms/src/freenet/crypt/DSAGroupGenerator.java 
(from rev 7751, trunk/freenet/src/freenet/crypt/DSAGroupGenerator.java)

Copied: branches/freenet-freejvms/src/freenet/crypt/DSAPrivateKey.java (from 
rev 7751, trunk/freenet/src/freenet/crypt/DSAPrivateKey.java)

Copied: branches/freenet-freejvms/src/freenet/crypt/DSAPublicKey.java (from rev 
7751, trunk/freenet/src/freenet/crypt/DSAPublicKey.java)

Modified: branches/freenet-freejvms/src/freenet/keys/CHKBlock.java
===================================================================
--- branches/freenet-freejvms/src/freenet/keys/CHKBlock.java    2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/keys/CHKBlock.java    2006-02-03 
22:29:13 UTC (rev 7997)
@@ -44,7 +44,6 @@
     final short hashIdentifier;
     final NodeCHK chk;
     public static final int MAX_LENGTH_BEFORE_COMPRESSION = Integer.MAX_VALUE;
-    final static int HASH_SHA256 = 1;

     public String toString() {
         return super.toString()+": chk="+chk;

Modified: branches/freenet-freejvms/src/freenet/keys/ClientCHK.java
===================================================================
--- branches/freenet-freejvms/src/freenet/keys/ClientCHK.java   2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/keys/ClientCHK.java   2006-02-03 
22:29:13 UTC (rev 7997)
@@ -119,18 +119,17 @@
                ","+cryptoAlgorithm;
     }

+       public Key getNodeKey() {
+               return getNodeCHK();
+       }

+       public NodeCHK getNodeCHK() {
+               if(nodeKey == null)
+               nodeKey = new NodeCHK(routingKey);
+           return nodeKey;
+       }
+       
     /**
-     * @return a NodeCHK corresponding to this key. Basically keep the 
-     * routingKey and lose everything else.
-     */
-    public NodeCHK getNodeCHK() {
-        if(nodeKey == null)
-            nodeKey = new NodeCHK(routingKey);
-        return nodeKey;
-    }
-
-    /**
      * @return URI form of this key.
      */
     public FreenetURI getURI() {

Modified: branches/freenet-freejvms/src/freenet/keys/ClientCHKBlock.java
===================================================================
--- branches/freenet-freejvms/src/freenet/keys/ClientCHKBlock.java      
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/keys/ClientCHKBlock.java      
2006-02-03 22:29:13 UTC (rev 7997)
@@ -3,6 +3,7 @@
 import java.io.IOException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;

 import org.spaceroots.mantissa.random.MersenneTwister;

@@ -10,10 +11,14 @@
 import freenet.crypt.PCFBMode;
 import freenet.crypt.UnsupportedCipherException;
 import freenet.crypt.ciphers.Rijndael;
+import freenet.node.Node;
 import freenet.support.ArrayBucket;
 import freenet.support.ArrayBucketFactory;
 import freenet.support.Bucket;
+import freenet.support.BucketFactory;
 import freenet.support.BucketTools;
+import freenet.support.Logger;
+import freenet.support.SimpleReadOnlyArrayBucket;
 import freenet.support.compress.CompressionOutputSizeException;
 import freenet.support.compress.Compressor;
 import freenet.support.compress.DecompressException;
@@ -31,9 +36,9 @@
 /**
  * @author amphibian
  * 
- * Client CHKBlock - provides functions for decoding, holds a key.
+ * Client CHKBlock - provides functions for decoding, holds a client-key.
  */
-public class ClientCHKBlock extends CHKBlock {
+public class ClientCHKBlock extends CHKBlock implements ClientKeyBlock {

     public static final long MAX_COMPRESSED_DATA_LENGTH = NodeCHK.BLOCK_SIZE - 
4;
        final ClientCHK key;
@@ -63,6 +68,82 @@
     }

     /**
+     * Decode into RAM, if short.
+     * @throws CHKDecodeException 
+     */
+       public byte[] memoryDecode() throws CHKDecodeException {
+               try {
+                       ArrayBucket a = (ArrayBucket) decode(new 
ArrayBucketFactory(), 32*1024);
+                       return BucketTools.toByteArray(a); // FIXME
+               } catch (IOException e) {
+                       throw new Error(e);
+               }
+       }
+       
+    public Bucket decode(ClientKey key, BucketFactory bf, int maxLength) 
throws KeyDecodeException, IOException {
+       if(!(key instanceof ClientCHK))
+               throw new KeyDecodeException("Not a CHK!: "+key);
+       return decode((ClientCHK)key, bf, maxLength);
+    }
+    
+    /**
+     * Decode the CHK and recover the original data
+     * @return the original data
+     * @throws IOException If there is a bucket error.
+     */
+    public Bucket decode(BucketFactory bf, int maxLength) throws 
CHKDecodeException, IOException {
+        // Overall hash already verified, so first job is to decrypt.
+        if(key.cryptoAlgorithm != ClientCHK.ALGO_AES_PCFB_256)
+            throw new UnsupportedOperationException();
+        BlockCipher cipher;
+        try {
+            cipher = new Rijndael(256, 256);
+        } catch (UnsupportedCipherException e) {
+            // FIXME - log this properly
+            throw new Error(e);
+        }
+        byte[] cryptoKey = key.cryptoKey;
+        if(cryptoKey.length < Node.SYMMETRIC_KEY_LENGTH)
+            throw new CHKDecodeException("Crypto key too short");
+        cipher.initialize(key.cryptoKey);
+        PCFBMode pcfb = new PCFBMode(cipher);
+        byte[] hbuf = new byte[header.length-2];
+        System.arraycopy(header, 2, hbuf, 0, header.length-2);
+        byte[] dbuf = new byte[data.length];
+        System.arraycopy(data, 0, dbuf, 0, data.length);
+        // Decipher header first - functions as IV
+        pcfb.blockDecipher(hbuf, 0, hbuf.length);
+        pcfb.blockDecipher(dbuf, 0, dbuf.length);
+        // Check: Decryption key == hash of data (not including header)
+        MessageDigest md256;
+        try {
+            md256 = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e1) {
+            // FIXME: log this properly?
+            throw new Error(e1);
+        }
+        byte[] dkey = md256.digest(dbuf);
+        if(!java.util.Arrays.equals(dkey, key.cryptoKey)) {
+            throw new CHKDecodeException("Check failed: decrypt key == 
H(data)");
+        }
+        // Check: IV == hash of decryption key
+        byte[] predIV = md256.digest(dkey);
+        // Extract the IV
+        byte[] iv = new byte[32];
+        System.arraycopy(hbuf, 0, iv, 0, 32);
+        if(!Arrays.equals(iv, predIV))
+            throw new CHKDecodeException("Check failed: Decrypted IV == 
H(decryption key)");
+        // Checks complete
+        int size = ((hbuf[32] & 0xff) << 8) + (hbuf[33] & 0xff);
+        if(size > 32768 || size < 0)
+            throw new CHKDecodeException("Invalid size: "+size);
+        byte[] output = new byte[size];
+        // No particular reason to check the padding, is there?
+        System.arraycopy(dbuf, 0, output, 0, size);
+        return Key.decompress(key, output, bf, maxLength, 
key.compressionAlgorithm, Math.min(maxLength, MAX_LENGTH_BEFORE_COMPRESSION));
+    }
+
+    /**
      * Encode a Bucket of data to a CHKBlock.
      * @param sourceData The bucket of data to encode. Can be arbitrarily 
large.
      * @param asMetadata Is this a metadata key?
@@ -239,4 +320,8 @@
         return key;
     }

+       public boolean isMetadata() {
+               return key.isMetadata();
+       }
+
 }

Modified: branches/freenet-freejvms/src/freenet/keys/ClientKey.java
===================================================================
--- branches/freenet-freejvms/src/freenet/keys/ClientKey.java   2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/keys/ClientKey.java   2006-02-03 
22:29:13 UTC (rev 7997)
@@ -13,12 +13,13 @@
                        return new ClientCHK(origURI);
                throw new UnsupportedOperationException("Unknown keytype from 
"+origURI);
        }
+       
+       public abstract FreenetURI getURI();

        /**
-        * Does the key contain metadata? If not, it contains real data.
+        * @return a NodeCHK corresponding to this key. Basically keep the 
+        * routingKey and lose everything else.
         */
-       public abstract boolean isMetadata();
+       public abstract Key getNodeKey();

-       public abstract FreenetURI getURI();
-
 }

Copied: branches/freenet-freejvms/src/freenet/keys/ClientKeyBlock.java (from 
rev 7751, trunk/freenet/src/freenet/keys/ClientKeyBlock.java)

Copied: branches/freenet-freejvms/src/freenet/keys/ClientSSK.java (from rev 
7751, trunk/freenet/src/freenet/keys/ClientSSK.java)

Copied: branches/freenet-freejvms/src/freenet/keys/ClientSSKBlock.java (from 
rev 7751, trunk/freenet/src/freenet/keys/ClientSSKBlock.java)

Modified: branches/freenet-freejvms/src/freenet/keys/Key.java
===================================================================
--- branches/freenet-freejvms/src/freenet/keys/Key.java 2006-02-03 21:46:40 UTC 
(rev 7996)
+++ branches/freenet-freejvms/src/freenet/keys/Key.java 2006-02-03 22:29:13 UTC 
(rev 7997)
@@ -3,8 +3,18 @@
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;

 import freenet.io.WritableToDataOutputStream;
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+import freenet.support.BucketTools;
+import freenet.support.Fields;
+import freenet.support.Logger;
+import freenet.support.SimpleReadOnlyArrayBucket;
+import freenet.support.compress.CompressionOutputSizeException;
+import freenet.support.compress.Compressor;

 /**
  * @author amphibian
@@ -13,9 +23,20 @@
  */
 public abstract class Key implements WritableToDataOutputStream {

+    final int hash;
+    double cachedNormalizedDouble;
+    /** Whatever its type, it will need a routingKey ! */
+    final byte[] routingKey;
+    
     /** 32 bytes for hash, 2 bytes for type */
     public static final short KEY_SIZE_ON_DISK = 34;

+    protected Key(byte[] routingKey) {
+       this.routingKey = routingKey;
+       hash = Fields.hashCode(routingKey);
+        cachedNormalizedDouble = -1;
+    }
+    
     /**
      * Write to disk.
      * Take up exactly 22 bytes.
@@ -36,10 +57,59 @@
         throw new IOException("Unrecognized format: "+type);
     }

+
     /**
      * Convert the key to a double between 0.0 and 1.0.
      * Normally we will hash the key first, in order to
      * make chosen-key attacks harder.
      */
-    public abstract double toNormalizedDouble();
+    public synchronized double toNormalizedDouble() {
+        if(cachedNormalizedDouble > 0) return cachedNormalizedDouble;
+        MessageDigest md;
+        try {
+            md = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e) {
+            throw new Error(e);
+        }
+        md.update(routingKey);
+        int TYPE = getType();
+        md.update((byte)(TYPE >> 8));
+        md.update((byte)TYPE);
+        byte[] digest = md.digest();
+        long asLong = Math.abs(Fields.bytesToLong(digest));
+        // Math.abs can actually return negative...
+        if(asLong == Long.MIN_VALUE)
+            asLong = Long.MAX_VALUE;
+        cachedNormalizedDouble = ((double)asLong)/((double)Long.MAX_VALUE);
+        return cachedNormalizedDouble;
+    }
+    
+    public abstract short getType();
+    
+    public int hashCode() {
+        return hash;
+    }
+    
+    static Bucket decompress(ClientCHK key, byte[] output, BucketFactory bf, 
int maxLength, short compressionAlgorithm, int maxDecompressedLength) throws 
CHKDecodeException, IOException {
+        if(key.isCompressed()) {
+               Logger.minor(key, "Decompressing in decode: "+key.getURI()+" 
with codec "+compressionAlgorithm);
+            if(output.length < 5) throw new CHKDecodeException("No bytes to 
decompress");
+            // Decompress
+            // First get the length
+            int len = ((((((output[0] & 0xff) << 8) + (output[1] & 0xff)) << 
8) + (output[2] & 0xff)) << 8) +
+               (output[3] & 0xff);
+            if(len > maxDecompressedLength)
+                throw new CHKDecodeException("Invalid precompressed size: 
"+len);
+            Compressor decompressor = 
Compressor.getCompressionAlgorithmByMetadataID(compressionAlgorithm);
+            Bucket inputBucket = new SimpleReadOnlyArrayBucket(output, 4, 
output.length-4);
+            try {
+                               return decompressor.decompress(inputBucket, bf, 
maxLength);
+                       } catch (CompressionOutputSizeException e) {
+                               throw new CHKDecodeException("Too big");
+                       }
+        } else {
+               return BucketTools.makeImmutableBucket(bf, output);
+        }
+       }
+
 }

Modified: branches/freenet-freejvms/src/freenet/keys/KeyBlock.java
===================================================================
--- branches/freenet-freejvms/src/freenet/keys/KeyBlock.java    2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/keys/KeyBlock.java    2006-02-03 
22:29:13 UTC (rev 7997)
@@ -10,11 +10,6 @@
  */
 public interface KeyBlock {

-       /** Decode with the key
-        * @param key The ClientKey to use to decode the block. 
-        * @param factory The BucketFactory to use to create the Bucket to 
return the data in.
-        * @param maxLength The maximum size of the returned data in bytes.
-        */
-       Bucket decode(ClientKey key, BucketFactory factory, int maxLength) 
throws KeyDecodeException, IOException;
-
+    final static int HASH_SHA256 = 1;
+       
 }

Modified: branches/freenet-freejvms/src/freenet/keys/NodeCHK.java
===================================================================
--- branches/freenet-freejvms/src/freenet/keys/NodeCHK.java     2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/keys/NodeCHK.java     2006-02-03 
22:29:13 UTC (rev 7997)
@@ -27,21 +27,16 @@
  */
 public class NodeCHK extends Key {

-    final int hash;
-    double cachedNormalizedDouble;
-    
     public NodeCHK(byte[] routingKey2) {
-        routingKey = routingKey2;
+       super(routingKey2);
         if(routingKey2.length != KEY_LENGTH)
             throw new IllegalArgumentException("Wrong length: 
"+routingKey2.length+" should be "+KEY_LENGTH);
-        hash = Fields.hashCode(routingKey);
-        cachedNormalizedDouble = -1;
     }

     static final int KEY_LENGTH = 32;

-    byte[] routingKey;
-    public static final short TYPE = 0x0302;
+    // 01 = CHK, 01 = first version of CHK
+    public static final short TYPE = 0x0101;
     /** The size of the data */
        public static final int BLOCK_SIZE = 32768;

@@ -64,10 +59,6 @@
         return new NodeCHK(buf);
     }

-    public int hashCode() {
-        return hash;
-    }
-
     public boolean equals(Object key) {
         if(key instanceof NodeCHK) {
             NodeCHK chk = (NodeCHK) key;
@@ -99,4 +90,7 @@
         cachedNormalizedDouble = ((double)asLong)/((double)Long.MAX_VALUE);
         return cachedNormalizedDouble;
     }
+       public short getType() {
+               return TYPE;
+       }
 }

Copied: branches/freenet-freejvms/src/freenet/keys/NodeSSK.java (from rev 7751, 
trunk/freenet/src/freenet/keys/NodeSSK.java)

Copied: branches/freenet-freejvms/src/freenet/keys/SSKBlock.java (from rev 
7751, trunk/freenet/src/freenet/keys/SSKBlock.java)

Copied: branches/freenet-freejvms/src/freenet/keys/SSKDecodeException.java 
(from rev 7751, trunk/freenet/src/freenet/keys/SSKDecodeException.java)

Copied: branches/freenet-freejvms/src/freenet/keys/SSKVerifyException.java 
(from rev 7751, trunk/freenet/src/freenet/keys/SSKVerifyException.java)

Modified: branches/freenet-freejvms/src/freenet/node/Node.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/Node.java        2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/Node.java        2006-02-03 
22:29:13 UTC (rev 7997)
@@ -45,6 +45,7 @@
 import freenet.keys.ClientCHK;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;
 import freenet.keys.NodeCHK;
 import freenet.store.BaseFreenetStore;
@@ -178,6 +179,7 @@
     final RequestStarter insertStarter;
     final File downloadDir;
     final TestnetHandler testnetHandler;
+    final TestnetStatusUploader statusUploader;

     // Client stuff that needs to be configged - FIXME
     static final int MAX_ARCHIVE_HANDLERS = 200; // don't take up much RAM... 
FIXME
@@ -186,7 +188,6 @@
     static final long MAX_ARCHIVED_FILE_SIZE = 1024*1024; // arbitrary... FIXME
     static final int MAX_CACHED_ELEMENTS = 1024; // equally arbitrary! FIXME 
hopefully we can cache many of these though

-    
     /**
      * Read all storable settings (identity etc) from the node file.
      * @param filename The name of the file to read from.
@@ -343,10 +344,12 @@
                testnetEnabled = true;
                testnetPort = 1024 + (port-1024+1000) % (65536 - 1024);
                testnetHandler = new TestnetHandler(this, testnetPort);
+               statusUploader = new TestnetStatusUploader(this, 180000);
        } else {
                testnetEnabled = false;
                testnetPort = -1;
                testnetHandler = null;
+               statusUploader = null;
        }
         portNumber = port;
         startupTime = System.currentTimeMillis();
@@ -447,14 +450,14 @@
         usm.start();
     }

-    public KeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException {
+    public ClientKeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException {
        if(localOnly)
                return realGetKey(key, localOnly, cache);
        else
                return client.getKey(key, localOnly, cache);
     }

-    public KeyBlock realGetKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException {
+    public ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException {
        if(key instanceof ClientCHK)
                return realGetCHK((ClientCHK)key, localOnly, cache);
        else
@@ -950,7 +953,8 @@
      */
     public String getStatus() {
        StringBuffer sb = new StringBuffer();
-       sb.append(peers.getStatus());
+       if (peers != null)
+               sb.append(peers.getStatus());
        sb.append("\nInserts: ");
        int x = insertSenders.size();
        sb.append(x);

Modified: branches/freenet-freejvms/src/freenet/node/QueuedDataRequest.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/QueuedDataRequest.java   
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/QueuedDataRequest.java   
2006-02-03 22:29:13 UTC (rev 7997)
@@ -1,6 +1,7 @@
 package freenet.node;

 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;

 public class QueuedDataRequest extends QueuedRequest {
@@ -17,7 +18,7 @@
                this.cache = cache;
        }

-       public KeyBlock waitAndFetch() throws LowLevelGetException {
+       public ClientKeyBlock waitAndFetch() throws LowLevelGetException {
                waitForSendClearance();
                return client.realGetKey(key, localOnly, cache);
        }

Modified: 
branches/freenet-freejvms/src/freenet/node/QueueingSimpleLowLevelClient.java
===================================================================
--- 
branches/freenet-freejvms/src/freenet/node/QueueingSimpleLowLevelClient.java    
    2006-02-03 21:46:40 UTC (rev 7996)
+++ 
branches/freenet-freejvms/src/freenet/node/QueueingSimpleLowLevelClient.java    
    2006-02-03 22:29:13 UTC (rev 7997)
@@ -3,12 +3,13 @@
 import freenet.client.InsertBlock;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;

 interface QueueingSimpleLowLevelClient extends SimpleLowLevelClient {

        /** Unqueued version. Only call from QueuedDataRequest ! */
-       KeyBlock realGetKey(ClientKey key, boolean localOnly, boolean cache) 
throws LowLevelGetException;
+       ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException;

        /** Ditto */
        void realPutCHK(ClientCHKBlock block, boolean cache) throws 
LowLevelPutException;

Modified: 
branches/freenet-freejvms/src/freenet/node/RealNodeRequestInsertTest.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/RealNodeRequestInsertTest.java   
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/RealNodeRequestInsertTest.java   
2006-02-03 22:29:13 UTC (rev 7997)
@@ -179,7 +179,7 @@
                 byte[] encData = block.getData();
                 byte[] encHeaders = block.getHeader();
                 ClientCHKBlock newBlock = new ClientCHKBlock(encData, 
encHeaders, chk, true);
-                Logger.error(RealNodeRequestInsertTest.class, "Decoded: "+new 
String(newBlock.memoryDecode(chk)));
+                Logger.error(RealNodeRequestInsertTest.class, "Decoded: "+new 
String(newBlock.memoryDecode()));
                 Logger.error(RealNodeRequestInsertTest.class,"CHK: 
"+chk.getURI());
                 Logger.error(RealNodeRequestInsertTest.class,"Headers: 
"+HexUtil.bytesToHex(block.getHeader()));
                 randomNode.putCHK(block, starters[node1], true);
@@ -196,7 +196,7 @@
                     Logger.error(RealNodeRequestInsertTest.class, "Fetch 
FAILED from "+node2);
                     requestsAvg.report(0.0);
                 } else {
-                    byte[] results = block.memoryDecode(chk);
+                    byte[] results = block.memoryDecode();
                     requestsAvg.report(1.0);
                     if(Arrays.equals(results, data)) {
                         Logger.error(RealNodeRequestInsertTest.class, "Fetch 
succeeded: "+new String(results));

Modified: branches/freenet-freejvms/src/freenet/node/RequestStarterClient.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/RequestStarterClient.java        
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/RequestStarterClient.java        
2006-02-03 22:29:13 UTC (rev 7997)
@@ -5,6 +5,7 @@
 import freenet.crypt.RandomSource;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;
 import freenet.support.DoublyLinkedList;
 import freenet.support.UpdatableSortedLinkedListItemImpl;
@@ -42,7 +43,7 @@
         * Blocking fetch of a key.
         * @throws LowLevelGetException If the fetch failed for some reason.
         */
-       public KeyBlock getKey(ClientKey key, boolean localOnly, boolean cache) 
throws LowLevelGetException {
+       public ClientKeyBlock getKey(ClientKey key, boolean localOnly, boolean 
cache) throws LowLevelGetException {
                QueuedDataRequest qdr = new QueuedDataRequest(key, localOnly, 
cache, client);
                addRequest(qdr);
                return qdr.waitAndFetch();

Modified: branches/freenet-freejvms/src/freenet/node/SimpleLowLevelClient.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/SimpleLowLevelClient.java        
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/SimpleLowLevelClient.java        
2006-02-03 22:29:13 UTC (rev 7997)
@@ -2,6 +2,7 @@

 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
 import freenet.keys.KeyBlock;

 /**
@@ -19,7 +20,7 @@
      * @param cache If false, don't cache the data. See the comments at the top
      * of Node.java.
      */
-    public KeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException;
+    public ClientKeyBlock getKey(ClientKey key, boolean localOnly, 
RequestStarterClient client, boolean cache) throws LowLevelGetException;

     /**
      * Insert a key.

Modified: branches/freenet-freejvms/src/freenet/node/TestnetHandler.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/TestnetHandler.java      
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/TestnetHandler.java      
2006-02-03 22:29:13 UTC (rev 7997)
@@ -115,7 +115,12 @@
                                                return;
                                        }
                                        
node.fileLoggerHook.sendLogByContainedDate(d.getTime(), os);
-                               } else {
+                               } else if(command.equalsIgnoreCase("STATUS")) {
+                                       Logger.minor(this, "Sending status");
+                                       OutputStreamWriter osw = new 
OutputStreamWriter(os, "ISO-8859-1");
+                                       osw.write(node.getStatus());
+                                       osw.close();
+                               }else {
                                        Logger.error(this, "Unknown testnet 
command: "+command);
                                }
                        } catch (IOException e) {

Copied: branches/freenet-freejvms/src/freenet/node/TestnetStatusUploader.java 
(from rev 7751, trunk/freenet/src/freenet/node/TestnetStatusUploader.java)

Modified: 
branches/freenet-freejvms/src/freenet/node/TextModeClientInterface.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/TextModeClientInterface.java     
2006-02-03 21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/TextModeClientInterface.java     
2006-02-03 22:29:13 UTC (rev 7997)
@@ -2,6 +2,7 @@

 import java.io.BufferedReader;
 import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
@@ -10,15 +11,19 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.net.MalformedURLException;
+import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;

 import freenet.client.ClientMetadata;
 import freenet.client.DefaultMIMETypes;
 import freenet.client.FetchException;
 import freenet.client.FetchResult;
 import freenet.client.HighLevelSimpleClient;
+import freenet.client.HighLevelSimpleClientImpl;
 import freenet.client.InsertBlock;
 import freenet.client.InserterException;
+import freenet.client.Metadata;
 import freenet.client.events.EventDumper;
 import freenet.crypt.RandomSource;
 import freenet.io.comm.PeerParseException;
@@ -92,6 +97,8 @@
         System.out.println("PUTFILE:<filename> - Put a file from disk.");
         System.out.println("GETFILE:<filename> - Fetch a key and put it in a 
file. If the key includes a filename we will use it but we will not overwrite 
local files.");
         System.out.println("GETCHKFILE:<filename> - Get the key that would be 
returned if we inserted the file.");
+        System.out.println("PUTDIR:<path>[#<defaultfile>] - Put the entire 
directory from disk.");
+        System.out.println("GETCHKDIR:<path>[#<defaultfile>] - Get the key 
that would be returned if we'd put the entire directory from disk.");
 //        System.out.println("PUBLISH:<name> - create a publish/subscribe 
stream called <name>");
 //        System.out.println("PUSH:<name>:<text> - publish a single line of 
text to the stream named");
 //        System.out.println("SUBSCRIBE:<key> - subscribe to a 
publish/subscribe stream by key");
@@ -250,6 +257,85 @@
             }

             System.out.println("URI: "+uri);
+            
////////////////////////////////////////////////////////////////////////////////
+        } else if(uline.startsWith("PUTDIR:") || (getCHKOnly = 
uline.startsWith("GETCHKDIR:"))) {
+               // TODO: Check for errors?
+               if(getCHKOnly) {
+                       line = line.substring(("GETCHKDIR:").length());
+               } else {
+                       line = line.substring("PUTDIR:".length());
+               }
+               
+               line = line.trim();
+               
+               if(line.length() < 1) {
+                       printHeader();
+                       return;
+               }
+               
+               String defaultFile = null;
+               
+               // set default file?
+               if (line.matches("^.*#.*$")) {
+                       defaultFile = line.split("#")[1];
+                       line = line.split("#")[0];
+               }
+               
+               // Get files as name and keys
+               HashMap manifestBase = dirPut(line, getCHKOnly);
+               
+               // Set defaultfile
+               if (defaultFile != null) {
+                       HashMap currPos = manifestBase;
+                       String splitpath[] = defaultFile.split("/");
+                       int i = 0;
+                       for( ; i < (splitpath.length - 1) ; i++)
+                               currPos = (HashMap)currPos.get(splitpath[i]);
+                       
+                       if (currPos.get(splitpath[i]) != null) {
+                               // Add key as default
+                               manifestBase.put("", currPos.get(splitpath[i]));
+                               System.out.println("Using default key: " + 
currPos.get(splitpath[i]));
+                       }else{
+                               System.err.println("Default key not found. No 
default document.");
+                       }
+                       //getchkdir:/home/cyberdo/fort/new#filelist
+               }
+               
+               // Create metadata
+            Metadata med = Metadata.mkRedirectionManifest(manifestBase);
+            ClientMetadata md = med.getClientMetadata();
+            
+            // Extract binary data from metadata
+            ArrayBucket metabucket = new ArrayBucket();
+            DataOutputStream mdos = new DataOutputStream( 
metabucket.getOutputStream() );
+            med.writeTo(mdos);
+            mdos.close();
+            
+            // Insert metadata
+            InsertBlock block = new InsertBlock(metabucket, md, 
FreenetURI.EMPTY_CHK_URI);
+            
+            FreenetURI uri;
+            try {
+               uri = ((HighLevelSimpleClientImpl)client).insert(block, 
getCHKOnly, true);
+            } catch (InserterException e) {
+               System.out.println("Error: "+e.getMessage());
+               if(e.uri != null)
+                       System.out.println("URI would have been: "+e.uri);
+               int mode = e.getMode();
+               if(mode == InserterException.FATAL_ERRORS_IN_BLOCKS || mode == 
InserterException.TOO_MANY_RETRIES_IN_BLOCKS) {
+                       System.out.println("Splitfile-specific 
error:\n"+e.errorCodes.toVerboseString());
+               }
+               return;
+            }
+            
+               String filelist = dirPutToList(manifestBase, "");
+               
System.out.println("=======================================================");
+               System.out.println(filelist);
+               
System.out.println("=======================================================");
+            System.out.println("URI: "+uri);
+               
System.out.println("=======================================================");
+            
         } else if(uline.startsWith("PUTFILE:") || (getCHKOnly = 
uline.startsWith("GETCHKFILE:"))) {
             // Just insert to local store
                if(getCHKOnly) {
@@ -358,6 +444,101 @@
         }
     }

+    
+    private String dirPutToList(HashMap dir, String basedir) {
+       String ret = "";
+               for(Iterator i = dir.keySet().iterator();i.hasNext();) {
+                       String key = (String) i.next();
+                       Object o = dir.get(key);
+                       Metadata target;
+                       if(o instanceof String) {
+                               // File
+                               ret += basedir + key + "\n";
+                       } else if(o instanceof HashMap) {
+                               ret += dirPutToList((HashMap)o, basedir + key + 
"//");
+                       } else throw new IllegalArgumentException("Not String 
nor HashMap: "+o);
+               }
+               return ret;
+    }
+    
+    private HashMap dirPut(String directory, boolean getCHKOnly) {
+       if (!directory.endsWith("/"))
+               directory = directory + "/";
+       File thisdir = new File(directory);
+       
+       HashMap ret = new HashMap();
+       
+       File filelist[] = thisdir.listFiles();
+       for(int i = 0 ; i < filelist.length ; i++)
+               if (filelist[i].isFile()) {
+                       FreenetURI uri = null;
+                       File f = filelist[i];
+                       String line = f.getAbsolutePath(); 
+                       // To ease cleanup, the following code is taken from 
above
+                       // Except for the uri-declaration above.
+                       // Somelines is also commented out
+                       
//////////////////////////////////////////////////////////////////////////////////////
+                       System.out.println("Attempting to read file "+line);
+                long startTime = System.currentTimeMillis();
+                try {
+                       if(!(f.exists() && f.canRead())) {
+                               throw new FileNotFoundException();
+                       }
+                       
+                       // Guess MIME type
+                       String mimeType = DefaultMIMETypes.guessMIMEType(line);
+                       System.out.println("Using MIME type: "+mimeType);
+                       
+                       FileBucket fb = new FileBucket(f, true, false, false);
+                       InsertBlock block = new InsertBlock(fb, new 
ClientMetadata(mimeType), FreenetURI.EMPTY_CHK_URI);
+
+                       startTime = System.currentTimeMillis();
+                       // Declaration is moved out!!!!!!!!!!!!
+                       uri = client.insert(block, getCHKOnly);
+                       
+                       // FIXME depends on CHK's still being renamable
+                    //uri = uri.setDocName(f.getName());
+                       
+                    System.out.println("URI: "+uri);
+                       long endTime = System.currentTimeMillis();
+                    long sz = f.length();
+                    double rate = 1000.0 * sz / (endTime-startTime);
+                    System.out.println("Upload rate: "+rate+" bytes / second");
+                } catch (FileNotFoundException e1) {
+                    System.out.println("File not found");
+                } catch (InserterException e) {
+                       System.out.println("Finished insert but: 
"+e.getMessage());
+                       if(e.uri != null) {
+                               System.out.println("URI would have been: 
"+e.uri);
+                       long endTime = System.currentTimeMillis();
+                        long sz = f.length();
+                        double rate = 1000.0 * sz / (endTime-startTime);
+                        System.out.println("Upload rate: "+rate+" bytes / 
second");
+                       }
+                       if(e.errorCodes != null) {
+                               System.out.println("Splitfile errors 
breakdown:");
+                               
System.out.println(e.errorCodes.toVerboseString());
+                       }
+                } catch (Throwable t) {
+                    System.out.println("Insert threw: "+t);
+                    t.printStackTrace();
+                }
+                
//////////////////////////////////////////////////////////////////////////////////////
+                       
+                if (uri != null)
+                       ret.put(filelist[i].getName(), uri.toString(false));
+                else
+                       System.err.println("Could not insert file.");
+                //ret.put(filelist[i].getName(), null);
+               } else {
+                       HashMap subdir = dirPut(filelist[i].getAbsolutePath(), 
getCHKOnly);
+                       ret.put(filelist[i].getName(), subdir);
+               }
+       
+       return ret;
+       }
+    
+    
     /**
      * @return A block of text, input from stdin, ending with a
      * . on a line by itself. Does some mangling for a fieldset if 

Modified: branches/freenet-freejvms/src/freenet/node/Version.java
===================================================================
--- branches/freenet-freejvms/src/freenet/node/Version.java     2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/node/Version.java     2006-02-03 
22:29:13 UTC (rev 7997)
@@ -20,7 +20,7 @@
        public static final String protocolVersion = "1.0";

        /** The build number of the current revision */
-       public static final int buildNumber = 305;
+       public static final int buildNumber = 309;

        /** Oldest build of Fred we will talk to */
        public static final int lastGoodBuild = 305;

Modified: branches/freenet-freejvms/src/freenet/store/DataStore.java
===================================================================
--- branches/freenet-freejvms/src/freenet/store/DataStore.java  2006-02-03 
21:46:40 UTC (rev 7996)
+++ branches/freenet-freejvms/src/freenet/store/DataStore.java  2006-02-03 
22:29:13 UTC (rev 7997)
@@ -27,8 +27,22 @@

 public class DataStore extends Store {

-    public static final String VERSION = "$Id: DataStore.java,v 1.5 2005/08/20 
21:21:21 amphibian Exp $";    
+    public class ATimeComparator implements Comparator {

+               public int compare(Object arg0, Object arg1) {
+                       DataBlock db0 = (DataBlock) arg0;
+                       DataBlock db1 = (DataBlock) arg1;
+                       long a0 = db0.getLastAccessTime();
+                       long a1 = db1.getLastAccessTime();
+                       if(a0 < a1) return -1;
+                       if(a0 > a1) return 1;
+                       return 0;
+               }
+
+       }
+
+       public static final String VERSION = "$Id: DataStore.java,v 1.5 
2005/08/20 21:21:21 amphibian Exp $";    
+
        private RandomAccessFile _index;
        private final int blockSize;

@@ -55,6 +69,8 @@
                _index.seek(0);
                int recordNum = 0;

+               Vector v = new Vector();
+               
                try {
                while (_index.getFilePointer() < _index.length()) {

@@ -65,14 +81,19 @@

                        getKeyMap().put(dataBlock.getKey(), dataBlock);
                        getRecordNumberList().add(recordNum, dataBlock);
-
-                       updateLastAccess(dataBlock);
+                       v.add(dataBlock);
                        recordNum++;
                }
                } catch (EOFException e) {
                        // Chopped off in the middle of a key
                        Logger.normal(this, "Store index truncated");
                        return;
+               } finally {
+                       DataBlock[] blocks = (DataBlock[]) v.toArray(new 
DataBlock[v.size()]);
+                       Arrays.sort(blocks, new ATimeComparator());
+                       for(int i=0;i<blocks.length;i++) {
+                               updateLastAccess(blocks[i]);
+                       }
                }
        }



Reply via email to